diff --git a/bootstrapper/internal/joinclient/BUILD.bazel b/bootstrapper/internal/joinclient/BUILD.bazel index 048df72ac..02a8258aa 100644 --- a/bootstrapper/internal/joinclient/BUILD.bazel +++ b/bootstrapper/internal/joinclient/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "@io_k8s_kubernetes//cmd/kubeadm/app/constants", "@io_k8s_utils//clock", "@org_golang_google_grpc//:grpc", + "@org_golang_x_crypto//ssh", ], ) diff --git a/bootstrapper/internal/joinclient/joinclient.go b/bootstrapper/internal/joinclient/joinclient.go index 285b02173..a12356708 100644 --- a/bootstrapper/internal/joinclient/joinclient.go +++ b/bootstrapper/internal/joinclient/joinclient.go @@ -18,7 +18,9 @@ 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" @@ -37,6 +39,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/versions/components" "github.com/edgelesssys/constellation/v2/joinservice/joinproto" "github.com/spf13/afero" + "golang.org/x/crypto/ssh" "google.golang.org/grpc" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" kubeconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -270,8 +273,19 @@ func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse, if err := c.fileHandler.Write(certificate.KeyFilename, kubeletKey, file.OptMkdirAll); err != nil { return fmt.Errorf("writing kubelet key: %w", err) } + + // derive CA key from emergency key + _, priv, err := ed25519.GenerateKey(bytes.NewReader(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) + } + // TODO(miampf): Make path a constant - if err := c.fileHandler.Write("/run/ssh/ssh_ca.pub", ticket.EmergencyCaKey, file.OptMkdirAll); err != nil { + if err := c.fileHandler.Write("/run/ssh/ssh_ca.pub", ssh.MarshalAuthorizedKey(ca.PublicKey()), file.OptMkdirAll); err != nil { return fmt.Errorf("writing ca key: %w", err) } diff --git a/joinservice/internal/server/server.go b/joinservice/internal/server/server.go index 21bb24d67..7f8afc85d 100644 --- a/joinservice/internal/server/server.go +++ b/joinservice/internal/server/server.go @@ -100,6 +100,13 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi return nil, status.Errorf(codes.Internal, "getting key for stateful disk: %s", err) } + log.Info("Requesting emergency SSH CA derivation key") + sshCAKey, err := s.dataKeyGetter.GetDataKey(ctx, constants.SSHCAKeySuffix, 256) + if err != nil { + log.With(slog.Any("error", err)).Error("Failed to get emergency SSH CA derivation key") + return nil, status.Errorf(codes.Internal, "getting emergency SSH CA derivation key: %s", err) + } + log.Info("Creating Kubernetes join token") kubeArgs, err := s.joinTokenGetter.GetJoinToken(constants.KubernetesJoinTokenTTL) if err != nil { @@ -167,6 +174,7 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi KubeletCert: kubeletCert, ControlPlaneFiles: controlPlaneFiles, KubernetesComponents: components, + EmergencyCaKey: sshCAKey, }, nil } diff --git a/keyservice/internal/server/BUILD.bazel b/keyservice/internal/server/BUILD.bazel index 38bc3afbb..22110e200 100644 --- a/keyservice/internal/server/BUILD.bazel +++ b/keyservice/internal/server/BUILD.bazel @@ -15,7 +15,6 @@ go_library( "@org_golang_google_grpc//:grpc", "@org_golang_google_grpc//codes", "@org_golang_google_grpc//status", - "@org_golang_x_crypto//ssh", ], ) diff --git a/keyservice/internal/server/server.go b/keyservice/internal/server/server.go index f2da87da1..8ff9bed8a 100644 --- a/keyservice/internal/server/server.go +++ b/keyservice/internal/server/server.go @@ -8,9 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only package server import ( - "bytes" "context" - "crypto/ed25519" "fmt" "log/slog" "net" @@ -20,7 +18,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/kms/kms" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/keyservice/keyserviceproto" - "golang.org/x/crypto/ssh" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -85,27 +82,3 @@ func (s *Server) GetDataKey(ctx context.Context, in *keyserviceproto.GetDataKeyR } return &keyserviceproto.GetDataKeyResponse{DataKey: key}, nil } - -// GetCAKey returns the CA key. -func (s *Server) GetCAKey(ctx context.Context, in *keyserviceproto.GetCAKeyRequest) (*keyserviceproto.GetCAKeyResponse, error) { - log := s.log.With("peerAddress", grpclog.PeerAddrFromContext(ctx)) - - key, err := s.conKMS.GetDEK(ctx, crypto.DEKPrefix, 256) - if err != nil { - log.With(slog.Any("error", err)).Error("Failed to get key for derivation") - return nil, status.Errorf(codes.Internal, "%v", err) - } - - _, priv, err := ed25519.GenerateKey(bytes.NewReader(key)) - if err != nil { - log.With(slog.Any("error", err)).Error("Failed to generate ed25519 key") - return nil, status.Errorf(codes.Internal, "%v", err) - } - - ca, err := ssh.NewSignerFromKey(priv) - if err != nil { - log.With(slog.Any("error", err)).Error("Failed to generate CA key") - return nil, status.Errorf(codes.Internal, "%v", err) - } - return &keyserviceproto.GetCAKeyResponse{CaKey: ssh.MarshalAuthorizedKey(ca.PublicKey())}, nil -} diff --git a/keyservice/keyserviceproto/keyservice.proto b/keyservice/keyserviceproto/keyservice.proto index 4baef70f1..8d74b4f11 100644 --- a/keyservice/keyserviceproto/keyservice.proto +++ b/keyservice/keyserviceproto/keyservice.proto @@ -4,22 +4,11 @@ package kms; option go_package = "github.com/edgelesssys/constellation/v2/keyservice/keyserviceproto"; -service API { - rpc GetDataKey(GetDataKeyRequest) returns (GetDataKeyResponse); - rpc GetCAKey(GetCAKeyRequest) returns (GetCAKeyResponse); -} +service API { rpc GetDataKey(GetDataKeyRequest) returns (GetDataKeyResponse); } message GetDataKeyRequest { string data_key_id = 1; uint32 length = 2; } -message GetDataKeyResponse { - bytes data_key = 1; -} - -message GetCAKeyRequest {} - -message GetCAKeyResponse { - bytes ca_key = 1; -} +message GetDataKeyResponse { bytes data_key = 1; }