Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cluster: support init root password #1700

Merged
merged 7 commits into from
Dec 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions components/bench/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ tidy:
clean:
@# Target: run the build cleanup steps
@rm -rf bin
@rm -rf cover
@rm -rf tests/*/{bin/*.test,logs,cover/*.out}
@rm -rf tests/*/{bin/*.test,logs}

test: failpoint-enable run-tests failpoint-disable
@# Target: run tests with failpoint enabled
Expand All @@ -91,8 +90,8 @@ run-tests: unit-test
# Run tests
unit-test:
@# Target: run the code coverage test phase
mkdir -p cover
TIUP_HOME=$(shell pwd)/../../tests/tiup $(GOTEST) ./... -covermode=count -coverprofile ../../cover/cov.unit-test.out
mkdir -p ../../cover
TIUP_HOME=$(shell pwd)/../../tests/tiup $(GOTEST) ./... -covermode=count -coverprofile ../../cover/cov.unit-test.bench.out

race: failpoint-enable
@# Target: run race check with failpoint enabled
Expand Down
7 changes: 3 additions & 4 deletions components/client/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ tidy:
clean:
@# Target: run the build cleanup steps
@rm -rf bin
@rm -rf cover
@rm -rf tests/*/{bin/*.test,logs,cover/*.out}
@rm -rf tests/*/{bin/*.test,logs}

test: failpoint-enable run-tests failpoint-disable
@# Target: run tests with failpoint enabled
Expand All @@ -91,8 +90,8 @@ run-tests: unit-test
# Run tests
unit-test:
@# Target: run the code coverage test phase
mkdir -p cover
TIUP_HOME=$(shell pwd)/../../tests/tiup $(GOTEST) ./... -covermode=count -coverprofile ../../cover/cov.unit-test.out
mkdir -p ../../cover
TIUP_HOME=$(shell pwd)/../../tests/tiup $(GOTEST) ./... -covermode=count -coverprofile ../../cover/cov.unit-test.client.out

race: failpoint-enable
@# Target: run race check with failpoint enabled
Expand Down
80 changes: 78 additions & 2 deletions components/cluster/command/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,23 @@
package command

import (
"database/sql"
"fmt"

"github.com/fatih/color"
"github.com/pingcap/tiup/pkg/cluster/spec"
"github.com/pingcap/tiup/pkg/cluster/task"
"github.com/pingcap/tiup/pkg/crypto/rand"
"github.com/pingcap/tiup/pkg/proxy"
"github.com/spf13/cobra"

// for sql/driver
_ "github.com/go-sql-driver/mysql"
)

func newStartCmd() *cobra.Command {
var initPasswd bool

cmd := &cobra.Command{
Use: "start <cluster-name>",
Short: "Start a TiDB cluster",
Expand All @@ -36,19 +47,84 @@ func newStartCmd() *cobra.Command {
clusterReport.ID = scrubClusterName(clusterName)
teleCommand = append(teleCommand, scrubClusterName(clusterName))

return cm.StartCluster(clusterName, gOpt, func(b *task.Builder, metadata spec.Metadata) {
if err := cm.StartCluster(clusterName, gOpt, func(b *task.Builder, metadata spec.Metadata) {
b.UpdateTopology(
clusterName,
tidbSpec.Path(clusterName),
metadata.(*spec.ClusterMeta),
nil, /* deleteNodeIds */
)
})
}); err != nil {
return err
}

// init password
if initPasswd {
pwd, err := initPassword(clusterName)
if err != nil {
log.Errorf("failed to set root password of TiDB database to '%s'", pwd)
return err
}
log.Warnf("The root password of TiDB database has been changed.")
fmt.Printf("The new password is: '%s'.", color.HiYellowString(pwd)) // use fmt to avoid printing to audit log
log.Warnf("Copy and record it to somewhere safe, %s, and will not be stored.", color.HiRedString("it is only displayed once"))
log.Warnf("The generated password %s.", color.HiRedString("could NOT be get and shown again"))
}
return nil
},
}

cmd.Flags().BoolVar(&initPasswd, "init", false, "Initialize a secure root password for the database")
cmd.Flags().StringSliceVarP(&gOpt.Roles, "role", "R", nil, "Only start specified roles")
cmd.Flags().StringSliceVarP(&gOpt.Nodes, "node", "N", nil, "Only start specified nodes")

return cmd
}

func initPassword(clusterName string) (string, error) {
metadata, err := spec.ClusterMetadata(clusterName)
if err != nil {
return "", err
}
tcpProxy := proxy.GetTCPProxy()

// generate password
pwd, err := rand.Password(18)
if err != nil {
return pwd, err
}

var lastErr error
for _, spec := range metadata.Topology.TiDBServers {
spec := spec
endpoint := fmt.Sprintf("%s:%d", spec.Host, spec.Port)
if tcpProxy != nil {
closeC := tcpProxy.Run([]string{endpoint})
defer tcpProxy.Close(closeC)
endpoint = tcpProxy.GetEndpoints()[0]
}
db, err := createDB(endpoint)
if err != nil {
lastErr = err
continue
}
defer db.Close()

sqlStr := fmt.Sprintf("SET PASSWORD FOR 'root'@'%%' = '%s'; FLUSH PRIVILEGES;", pwd)
_, err = db.Exec(sqlStr)
if err != nil {
lastErr = err
continue
}
return pwd, nil
}

return pwd, lastErr
}

func createDB(endpoint string) (db *sql.DB, err error) {
dsn := fmt.Sprintf("root:@tcp(%s)/?charset=utf8mb4,utf8&multiStatements=true", endpoint)
db, err = sql.Open("mysql", dsn)

return
}
8 changes: 0 additions & 8 deletions components/cluster/command/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package command

import (
"context"
"database/sql"
"errors"
"fmt"

Expand Down Expand Up @@ -72,13 +71,6 @@ func newTestCmd() *cobra.Command {
return cmd
}

func createDB(endpoint string) (db *sql.DB, err error) {
dsn := fmt.Sprintf("root:@tcp(%s)/?charset=utf8mb4,utf8&multiStatements=true", endpoint)
db, err = sql.Open("mysql", dsn)

return
}

// To check if test.ti_cluster has data
func data(topo *spec.Specification, tcpProxy *proxy.TCPProxy) error {
errg, _ := errgroup.WithContext(context.Background())
Expand Down
3 changes: 3 additions & 0 deletions embed/templates/scripts/run_tidb.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ exec env GODEBUG=madvdontneed=1 bin/tidb-server \
--host="{{.ListenHost}}" \
--advertise-address="{{.AdvertiseAddr}}" \
--store="tikv" \
{{- if .SupportSecboot}}
--initialize-insecure \
{{- end}}
--path="{{template "PDList" .Endpoints}}" \
--log-slow-query="{{.LogDir}}/tidb_slow_query.log" \
--config=conf/tidb.toml \
Expand Down
2 changes: 1 addition & 1 deletion examples
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ require (
github.com/r3labs/diff/v2 v2.14.0
github.com/relex/aini v1.5.0
github.com/sergi/go-diff v1.2.0
github.com/sethvargo/go-password v0.2.0
github.com/shirou/gopsutil v3.21.8+incompatible
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/spf13/cobra v1.2.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
github.com/shirou/gopsutil v3.21.8+incompatible h1:sh0foI8tMRlCidUJR+KzqWYWxrkuuPIGiO6Vp+KXdCU=
github.com/shirou/gopsutil v3.21.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
Expand Down
4 changes: 3 additions & 1 deletion pkg/cluster/spec/tidb.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/pingcap/tiup/pkg/cluster/ctxt"
"github.com/pingcap/tiup/pkg/cluster/template/scripts"
"github.com/pingcap/tiup/pkg/meta"
"golang.org/x/mod/semver"
)

// TiDBSpec represents the TiDB topology specification in topology.yaml
Expand Down Expand Up @@ -144,7 +145,8 @@ func (i *TiDBInstance) InitConfig(
WithStatusPort(spec.StatusPort).
AppendEndpoints(topo.Endpoints(deployUser)...).
WithListenHost(i.GetListenHost()).
WithAdvertiseAddr(spec.Host)
WithAdvertiseAddr(spec.Host).
SupportSecureBootstrap(semver.Compare(clusterVersion, "v5.3.0") >= 0)
fp := filepath.Join(paths.Cache, fmt.Sprintf("run_tidb_%s_%d.sh", i.GetHost(), i.GetPort()))
if err := cfg.ConfigToFile(fp); err != nil {
return err
Expand Down
25 changes: 16 additions & 9 deletions pkg/cluster/template/scripts/tidb.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ import (

// TiDBScript represent the data to generate TiDB config
type TiDBScript struct {
IP string
ListenHost string
AdvertiseAddr string
Port int
StatusPort int
DeployDir string
LogDir string
NumaNode string
Endpoints []*PDScript
IP string
ListenHost string
AdvertiseAddr string
Port int
StatusPort int
DeployDir string
LogDir string
NumaNode string
SupportSecboot bool
Endpoints []*PDScript
}

// NewTiDBScript returns a TiDBScript with given arguments
Expand Down Expand Up @@ -79,6 +80,12 @@ func (c *TiDBScript) WithNumaNode(numa string) *TiDBScript {
return c
}

// SupportSecureBootstrap set SupportSecboot field of TiDBScript
func (c *TiDBScript) SupportSecureBootstrap(s bool) *TiDBScript {
c.SupportSecboot = s
return c
}

// AppendEndpoints add new PDScript to Endpoints field
func (c *TiDBScript) AppendEndpoints(ends ...*PDScript) *TiDBScript {
c.Endpoints = append(c.Endpoints, ends...)
Expand Down
53 changes: 53 additions & 0 deletions pkg/crypto/rand/passwd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2021 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package rand

import (
"github.com/sethvargo/go-password/password"
)

// charsets with some in similar shapes removed (e.g., O, o, I, l, etc.)
const (
lowerLetters = "abcdefghijkmnpqrstuvwxyz"
upperLetters = "ABCDEFGHJKLMNPQRSTUVWXYZ"
digits = "0123456789"
symbols = "!@#$%^&*+-=_"
)

// Password generates a random password
func Password(length int) (string, error) {
if length < 8 {
panic("password length muster be at least 8.")
}

gi := &password.GeneratorInput{
LowerLetters: lowerLetters,
UpperLetters: upperLetters,
Digits: digits,
Symbols: symbols,
Reader: Reader,
}
g, err := password.NewGenerator(gi)
if err != nil {
return "", err
}

// 1/3 of the password are digits and 1/4 of it are symbols
numDigits := length / 3
numSymbols := length / 4
// allow repeat if the length is longer than the shortest charset
allowRepeat := (numDigits > len(digits) || numSymbols > len(symbols))

return g.Generate(length, numDigits, numSymbols, false, allowRepeat)
}
36 changes: 36 additions & 0 deletions pkg/crypto/rand/passwd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2021 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package rand

import (
"testing"
)

func TestPasswd(t *testing.T) {
for i := 0; i < 100; i++ {
l := Intn(64)
if l < 8 { // make sure it's greater than 8
l += 8
}
t.Logf("generating random password of length %d", l)
p, e := Password(l)
if e != nil {
t.Error(e)
}
t.Log(p)
if len(p) != l {
t.Fail()
}
}
}