Skip to content
Merged
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
29 changes: 25 additions & 4 deletions internal/cluster/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,40 @@ func (c *Cluster) Join(ctx context.Context, opts JoinOptions) error {
return nil
}

func isHex(s string) bool {
for _, c := range s {
switch {
case c >= '0' && c <= '9':
case c >= 'a' && c <= 'f':
case c >= 'A' && c <= 'F':
default:
return false
}
}
return true
}

// generateJoinCommand generates a fresh join command from the control plane
func (c *Cluster) generateJoinCommand(ctx context.Context, cpSSHClient *ssh.Client, isControlPlane bool) (string, error) {
if isControlPlane {
// For control-plane nodes, we need to upload certificates and get the certificate key
// For control-plane nodes, we need to upload certificates and get the certificate key.
// The command prints log lines to stderr and the 64-char hex key on stdout.
// We avoid piping (which masks the exit code) and extract the key ourselves.
c.logger.Info("Uploading certificates for control-plane join...")
certKeyOutput, err := cpSSHClient.Exec(ctx, "sudo kubeadm init phase upload-certs --upload-certs 2>/dev/null | tail -1")
certKeyOutput, err := cpSSHClient.Exec(ctx, "sudo kubeadm init phase upload-certs --upload-certs --config /etc/kubernetes/kubeadm-config.yaml")
if err != nil {
return "", fmt.Errorf("failed to upload certificates: %w", err)
}

certificateKey := strings.TrimSpace(certKeyOutput)
var certificateKey string
for _, line := range strings.Split(certKeyOutput, "\n") {
line = strings.TrimSpace(line)
if len(line) == 64 && isHex(line) {
certificateKey = line
}
}
if certificateKey == "" {
return "", fmt.Errorf("certificate key is empty")
return "", fmt.Errorf("certificate key not found in upload-certs output: %s", certKeyOutput)
Comment on lines 167 to +168

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Avoid returning the full upload-certs output (including the key) in the error message

Including certKeyOutput here will log and propagate the certificate key, which is sensitive and should never be exposed. Instead, either omit the raw output from the error or redact the key (e.g., log only a short, non-sensitive snippet or use a structured message without the full value).

}

c.logger.Infof("Certificate key: %s", certificateKey)
Expand Down
Loading