Skip to content
Open
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
35 changes: 32 additions & 3 deletions components/cluster/command/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"strings"

perrs "github.com/pingcap/errors"
"github.com/pingcap/tiup/pkg/cluster/manager"
"github.com/spf13/cobra"
)

Expand All @@ -25,10 +26,14 @@ func newTLSCmd() *cobra.Command {
reloadCertificate bool // reload certificate when the cluster enable encrypted communication
cleanCertificate bool // cleanup certificate when the cluster disable encrypted communication
enableTLS bool
customMode bool
clientCA string
clientCert string
clientKey string
)

cmd := &cobra.Command{
Use: "tls <cluster-name> <enable/disable>",
Use: "tls <cluster-name> <enable/disable/swap-client-cert>",
Short: "Enable/Disable TLS between TiDB components",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
Expand All @@ -45,8 +50,13 @@ func newTLSCmd() *cobra.Command {
enableTLS = true
case "disable":
enableTLS = false
case "swap-client-cert":
if clientCA == "" || clientCert == "" || clientKey == "" {
return perrs.New("swap-client-cert requires --client-ca, --client-cert, and --client-key")
}
return cm.SwapClientCert(clusterName, clientCA, clientCert, clientKey)
default:
return perrs.New("enable or disable must be specified at least one")
return perrs.New("action must be one of: enable, disable, swap-client-cert")
}

if enableTLS && cleanCertificate {
Expand All @@ -57,13 +67,32 @@ func newTLSCmd() *cobra.Command {
return perrs.New("reload-certificate only works when tls enable")
}

return cm.TLS(clusterName, gOpt, enableTLS, cleanCertificate, reloadCertificate, skipConfirm)
if !enableTLS && customMode {
return perrs.New("custom mode only applies to enable")
}

if customMode && (clientCA == "" || clientCert == "" || clientKey == "") {
return perrs.New("--custom requires --client-ca, --client-cert, and --client-key")
}

customOpts := manager.CustomTLSOptions{
Enabled: customMode,
ClientCA: clientCA,
ClientCert: clientCert,
ClientKey: clientKey,
}

return cm.TLS(clusterName, gOpt, enableTLS, cleanCertificate, reloadCertificate, skipConfirm, customOpts)
},
}

cmd.Flags().BoolVar(&cleanCertificate, "clean-certificate", false, "Cleanup the certificate file if it already exists when tls disable")
cmd.Flags().BoolVar(&reloadCertificate, "reload-certificate", false, "Load the certificate file whether it exists or not when tls enable")
cmd.Flags().BoolVar(&gOpt.Force, "force", false, "Force enable/disable tls regardless of the current state")
cmd.Flags().BoolVar(&customMode, "custom", false, "Use custom (BYOC) certificates instead of TiUP-managed self-signed certificates")
cmd.Flags().StringVar(&clientCA, "client-ca", "", "Path to the client CA certificate file (used with --custom)")
cmd.Flags().StringVar(&clientCert, "client-cert", "", "Path to the client certificate file (used with --custom)")
cmd.Flags().StringVar(&clientKey, "client-key", "", "Path to the client private key file (used with --custom)")

return cmd
}
8 changes: 4 additions & 4 deletions components/dm/spec/logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (i *MasterInstance) InitConfig(
return err
}

if spec.Config, err = i.setTLSConfig(ctx, enableTLS, spec.Config, paths); err != nil {
if spec.Config, err = i.setTLSConfig(ctx, enableTLS, spec.Config, nil, paths); err != nil {
return err
}

Expand All @@ -187,7 +187,7 @@ func (i *MasterInstance) InitConfig(

// setTLSConfig set TLS Config to support enable/disable TLS
// MasterInstance no need to configure TLS
func (i *MasterInstance) setTLSConfig(ctx context.Context, enableTLS bool, configs map[string]any, paths meta.DirPaths) (map[string]any, error) {
func (i *MasterInstance) setTLSConfig(ctx context.Context, enableTLS bool, configs map[string]any, globalConfig map[string]any, paths meta.DirPaths) (map[string]any, error) {
// set TLS configs
if enableTLS {
if configs == nil {
Expand Down Expand Up @@ -398,7 +398,7 @@ func (i *WorkerInstance) InitConfig(
return err
}

if spec.Config, err = i.setTLSConfig(ctx, enableTLS, spec.Config, paths); err != nil {
if spec.Config, err = i.setTLSConfig(ctx, enableTLS, spec.Config, nil, paths); err != nil {
return err
}

Expand All @@ -408,7 +408,7 @@ func (i *WorkerInstance) InitConfig(

// setTLSConfig set TLS Config to support enable/disable TLS
// workrsInstance no need to configure TLS
func (i *WorkerInstance) setTLSConfig(ctx context.Context, enableTLS bool, configs map[string]any, paths meta.DirPaths) (map[string]any, error) {
func (i *WorkerInstance) setTLSConfig(ctx context.Context, enableTLS bool, configs map[string]any, globalConfig map[string]any, paths meta.DirPaths) (map[string]any, error) {
// set TLS configs
if enableTLS {
if configs == nil {
Expand Down
6 changes: 6 additions & 0 deletions embed/templates/config/blackbox.yml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ modules:
tls: true
tls_config:
insecure_skip_verify: false
{{- if .CustomCA}}
ca_file: {{.CustomCA}}
cert_file: {{.CustomCert}}
key_file: {{.CustomKey}}
{{- else}}
ca_file: {{.DeployDir}}/tls/ca.crt
cert_file: {{.DeployDir}}/tls/blackbox_exporter.crt
key_file: {{.DeployDir}}/tls/blackbox_exporter.pem
{{- end}}
{{- end}}
pop3s_banner:
prober: tcp
Expand Down
80 changes: 47 additions & 33 deletions pkg/cluster/manager/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,11 @@ func buildMonitoredCertificateTasks(
Mkdir(globalOptions.User, host, globalOptions.SystemdMode != spec.UserMode, tlsDir)

if comp == spec.ComponentBlackboxExporter {
// In custom mode the user provides blackbox certs via
// monitored.blackbox_ca/cert/key — no generation needed.
if globalOptions.IsCustomTLS() {
continue
}
ca, innerr := crypto.ReadCA(
name,
m.specManager.Path(name, spec.TLSCertKeyDir, spec.TLSCACert),
Expand Down Expand Up @@ -711,8 +716,9 @@ func buildTLSTask(
topo := metadata.GetTopology()
base := metadata.GetBaseMeta()

// load certificate file
if topo.BaseTopo().GlobalOptions.TLSEnabled {
// In custom mode: skip cert generation entirely — user provides their own certs.
// In managed mode: load/generate certificates as before.
if topo.BaseTopo().GlobalOptions.TLSEnabled && !topo.BaseTopo().GlobalOptions.IsCustomTLS() {
tlsDir := m.specManager.Path(name, spec.TLSCertKeyDir)
m.logger.Infof("Generate certificate: %s", color.YellowString(tlsDir))
if err := m.loadCertificate(name, topo.BaseTopo().GlobalOptions, reloadCertificate); err != nil {
Expand Down Expand Up @@ -893,43 +899,51 @@ func buildCertificateTasks(
base *spec.BaseMeta,
gOpt operator.Options,
p *tui.SSHConnectionProps) ([]*task.StepDisplay, error) {
if !topo.BaseTopo().GlobalOptions.TLSEnabled {
return nil, nil
}

// Custom mode: no cert generation or transfer needed.
// User's security.*-path config values point directly to their cert files.
if topo.BaseTopo().GlobalOptions.IsCustomTLS() {
return nil, nil
}

var (
iterErr error
certificateTasks []*task.StepDisplay // tasks which are used to copy certificate to remote host
)

// copy TLS certificate to remote host
if topo.BaseTopo().GlobalOptions.TLSEnabled {
topo.IterInstance(func(inst spec.Instance) {
deployDir := spec.Abs(base.User, inst.DeployDir())
tlsDir := filepath.Join(deployDir, spec.TLSCertKeyDir)

tb := task.NewSimpleUerSSH(m.logger, inst.GetManageHost(), inst.GetSSHPort(), base.User, gOpt, p, topo.BaseTopo().GlobalOptions.SSHType).
Mkdir(base.User, inst.GetManageHost(), topo.BaseTopo().GlobalOptions.SystemdMode != spec.UserMode, deployDir, tlsDir)
ca, err := crypto.ReadCA(
name,
m.specManager.Path(name, spec.TLSCertKeyDir, spec.TLSCACert),
m.specManager.Path(name, spec.TLSCertKeyDir, spec.TLSCAKey),
)
if err != nil {
iterErr = err
return
}
tb = tb.TLSCert(
inst.GetHost(),
inst.ComponentName(),
inst.Role(),
inst.GetMainPort(),
ca,
meta.DirPaths{
Deploy: deployDir,
Cache: m.specManager.Path(name, spec.TempConfigPath),
})
// Managed mode: generate and transfer TLS certificates to remote host
topo.IterInstance(func(inst spec.Instance) {
deployDir := spec.Abs(base.User, inst.DeployDir())
tlsDir := filepath.Join(deployDir, spec.TLSCertKeyDir)

t := tb.BuildAsStep(fmt.Sprintf(" - Generate certificate %s -> %s", inst.ComponentName(), inst.ID()))
certificateTasks = append(certificateTasks, t)
})
}
tb := task.NewSimpleUerSSH(m.logger, inst.GetManageHost(), inst.GetSSHPort(), base.User, gOpt, p, topo.BaseTopo().GlobalOptions.SSHType).
Mkdir(base.User, inst.GetManageHost(), topo.BaseTopo().GlobalOptions.SystemdMode != spec.UserMode, deployDir, tlsDir)
ca, err := crypto.ReadCA(
name,
m.specManager.Path(name, spec.TLSCertKeyDir, spec.TLSCACert),
m.specManager.Path(name, spec.TLSCertKeyDir, spec.TLSCAKey),
)
if err != nil {
iterErr = err
return
}
tb = tb.TLSCert(
inst.GetHost(),
inst.ComponentName(),
inst.Role(),
inst.GetMainPort(),
ca,
meta.DirPaths{
Deploy: deployDir,
Cache: m.specManager.Path(name, spec.TempConfigPath),
})

t := tb.BuildAsStep(fmt.Sprintf(" - Generate certificate %s -> %s", inst.ComponentName(), inst.ID()))
certificateTasks = append(certificateTasks, t)
})

return certificateTasks, iterErr
}
10 changes: 10 additions & 0 deletions pkg/cluster/manager/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ func (m *Manager) Display(dopt DisplayOption, opt operator.Options) error {
// display TLS info
if topo.BaseTopo().GlobalOptions.TLSEnabled {
fmt.Printf("TLS encryption: %s\n", cyan.Sprint("enabled"))
if topo.BaseTopo().GlobalOptions.IsCustomTLS() {
fmt.Printf("TLS mode: %s\n", cyan.Sprint("custom"))
} else {
fmt.Printf("TLS mode: %s\n", cyan.Sprint("managed"))
}
fmt.Printf("CA certificate: %s\n", cyan.Sprint(
m.specManager.Path(name, spec.TLSCertKeyDir, spec.TLSCACert),
))
Expand Down Expand Up @@ -393,6 +398,11 @@ func (m *Manager) DisplayTiKVLabels(dopt DisplayOption, opt operator.Options) er
// display TLS info
if topo.BaseTopo().GlobalOptions.TLSEnabled {
fmt.Printf("TLS encryption: %s\n", cyan.Sprint("enabled"))
if topo.BaseTopo().GlobalOptions.IsCustomTLS() {
fmt.Printf("TLS mode: %s\n", cyan.Sprint("custom"))
} else {
fmt.Printf("TLS mode: %s\n", cyan.Sprint("managed"))
}
fmt.Printf("CA certificate: %s\n", cyan.Sprint(
m.specManager.Path(name, spec.TLSCertKeyDir, spec.TLSCACert),
))
Expand Down
5 changes: 5 additions & 0 deletions pkg/cluster/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ func (m *Manager) confirmTopology(name, version string, topo spec.Topology, patc
fmt.Printf("Cluster version: %s\n", cyan.Sprint(version))
if topo.BaseTopo().GlobalOptions.TLSEnabled {
fmt.Printf("TLS encryption: %s\n", cyan.Sprint("enabled"))
if topo.BaseTopo().GlobalOptions.IsCustomTLS() {
fmt.Printf("TLS mode: %s\n", cyan.Sprint("custom"))
} else {
fmt.Printf("TLS mode: %s\n", cyan.Sprint("managed"))
}
}

// check if managehost is set
Expand Down
Loading