diff --git a/bootstrapper/internal/initserver/initserver.go b/bootstrapper/internal/initserver/initserver.go index 7fb454b75..ff5406f1a 100644 --- a/bootstrapper/internal/initserver/initserver.go +++ b/bootstrapper/internal/initserver/initserver.go @@ -19,9 +19,7 @@ package initserver import ( "bufio" - "bytes" "context" - "crypto/ed25519" "errors" "fmt" "io" @@ -234,16 +232,9 @@ func (s *Server) Init(req *initproto.InitRequest, stream initproto.API_InitServe } return err } - _, priv, err := ed25519.GenerateKey(bytes.NewReader(key)) + ca, err := crypto.GenerateEmergencySSHCAKey(key) if err != nil { - if e := s.sendLogsWithMessage(stream, status.Errorf(codes.Internal, "generating signing key for emergency ssh CA: %s", err)); e != nil { - err = errors.Join(err, e) - } - return err - } - ca, err := ssh.NewSignerFromSigner(priv) - if err != nil { - if e := s.sendLogsWithMessage(stream, status.Errorf(codes.Internal, "signing emergency ssh CA key: %s", err)); e != nil { + if e := s.sendLogsWithMessage(stream, status.Errorf(codes.Internal, "generating emergency SSH CA key: %s", err)); e != nil { err = errors.Join(err, e) } return err diff --git a/bootstrapper/internal/joinclient/BUILD.bazel b/bootstrapper/internal/joinclient/BUILD.bazel index 02a8258aa..68fbc4bcb 100644 --- a/bootstrapper/internal/joinclient/BUILD.bazel +++ b/bootstrapper/internal/joinclient/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "//internal/attestation", "//internal/cloud/metadata", "//internal/constants", + "//internal/crypto", "//internal/file", "//internal/nodestate", "//internal/role", diff --git a/bootstrapper/internal/joinclient/joinclient.go b/bootstrapper/internal/joinclient/joinclient.go index 03e6067d1..fbeb90f6e 100644 --- a/bootstrapper/internal/joinclient/joinclient.go +++ b/bootstrapper/internal/joinclient/joinclient.go @@ -18,9 +18,7 @@ If the JoinClient finds an existing cluster, it will attempt to join it as eithe package joinclient import ( - "bytes" "context" - "crypto/ed25519" "errors" "fmt" "log/slog" @@ -33,6 +31,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/cloud/metadata" "github.com/edgelesssys/constellation/v2/internal/constants" + "github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/nodestate" "github.com/edgelesssys/constellation/v2/internal/role" @@ -274,17 +273,11 @@ func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse, return fmt.Errorf("writing kubelet key: %w", err) } - // derive CA key from emergency key - _, priv, err := ed25519.GenerateKey(bytes.NewReader(ticket.EmergencyCaKey)) + ca, err := crypto.GenerateEmergencySSHCAKey(ticket.EmergencyCaKey) if err != nil { - return fmt.Errorf("deriving ed25519 ssh emergency key: %w", err) - } - ca, err := ssh.NewSignerFromSigner(priv) - if err != nil { - return fmt.Errorf("creating emergency SSH CA key: %w", err) + return fmt.Errorf("generating emergency SSH CA key: %s", err) } - // TODO(miampf): Make path a constant if err := c.fileHandler.Write(constants.SSHCAKeyPath, ssh.MarshalAuthorizedKey(ca.PublicKey()), file.OptMkdirAll); err != nil { return fmt.Errorf("writing ca key: %w", err) } diff --git a/cli/internal/cmd/ssh.go b/cli/internal/cmd/ssh.go index 54727b25b..9f6971c8f 100644 --- a/cli/internal/cmd/ssh.go +++ b/cli/internal/cmd/ssh.go @@ -7,8 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only package cmd import ( - "bytes" - "crypto/ed25519" "crypto/rand" "fmt" "os" @@ -77,17 +75,12 @@ func runSSH(cmd *cobra.Command, _ []string) error { return fmt.Errorf("Failed to retrieve key from key management service: %s", err) } - _, priv, err := ed25519.GenerateKey(bytes.NewReader(key)) + ca, err := crypto.GenerateEmergencySSHCAKey(key) if err != nil { - return fmt.Errorf("Failed to create signing key from master secret: %s", err) + return fmt.Errorf("Failed to generate emergency SSH CA key: %s", err) } - ca, err := ssh.NewSignerFromSigner(priv) - if err != nil { - return fmt.Errorf("Failed to create ssh CA key from master secret: %s", err) - } - - debugLogger.Debug("SSH CA KEY generated", "key", string(ssh.MarshalAuthorizedKey(ca.PublicKey()))) + debugLogger.Debug("SSH CA KEY generated", "public-key", string(ssh.MarshalAuthorizedKey(ca.PublicKey()))) key_path, err := cmd.Flags().GetString("key") if err != nil { diff --git a/internal/crypto/BUILD.bazel b/internal/crypto/BUILD.bazel index 28131c022..0b3e402d9 100644 --- a/internal/crypto/BUILD.bazel +++ b/internal/crypto/BUILD.bazel @@ -6,7 +6,10 @@ go_library( srcs = ["crypto.go"], importpath = "github.com/edgelesssys/constellation/v2/internal/crypto", visibility = ["//:__subpackages__"], - deps = ["@org_golang_x_crypto//hkdf"], + deps = [ + "@org_golang_x_crypto//hkdf", + "@org_golang_x_crypto//ssh", + ], ) go_test( diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go index 081e25d71..d208b993a 100644 --- a/internal/crypto/crypto.go +++ b/internal/crypto/crypto.go @@ -9,6 +9,7 @@ package crypto import ( "bytes" + "crypto/ed25519" "crypto/rand" "crypto/sha256" "crypto/x509" @@ -18,6 +19,7 @@ import ( "math/big" "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/ssh" ) const ( @@ -62,6 +64,19 @@ func GenerateRandomBytes(length int) ([]byte, error) { return nonce, nil } +// GenerateEmergencySSHCAKey creates a CA that is used to sign keys for emergency ssh access. +func GenerateEmergencySSHCAKey(key []byte) (ssh.Signer, error) { + _, priv, err := ed25519.GenerateKey(bytes.NewReader(key)) + if err != nil { + return nil, err + } + ca, err := ssh.NewSignerFromSigner(priv) + if err != nil { + return nil, err + } + return ca, nil +} + // PemToX509Cert takes a list of PEM-encoded certificates, parses the first one and returns it // as an x.509 certificate. func PemToX509Cert(raw []byte) (*x509.Certificate, error) {