From d671a71f306a3dcf4719a2948007d4f99d3d4579 Mon Sep 17 00:00:00 2001 From: miampf Date: Wed, 16 Apr 2025 12:29:11 +0200 Subject: [PATCH] refactor: create function in crypto package --- internal/crypto/crypto.go | 37 ++++++++++++++++++++++++++ joinservice/internal/server/server.go | 38 +++++---------------------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go index 0a88ec2f5..c798e08ca 100644 --- a/internal/crypto/crypto.go +++ b/internal/crypto/crypto.go @@ -17,6 +17,7 @@ import ( "fmt" "io" "math/big" + "time" "golang.org/x/crypto/hkdf" "golang.org/x/crypto/ssh" @@ -77,6 +78,42 @@ func GenerateEmergencySSHCAKey(seed []byte) (ssh.Signer, error) { return ca, nil } +// GenerateSignedSSHHostKey creates a host key that is signed by the given CA. +func GenerateSignedSSHHostKey(principal string, ca ssh.Signer) (*pem.Block, *ssh.Certificate, error) { + hostKeyPub, hostKey, err := ed25519.GenerateKey(nil) + if err != nil { + return nil, nil, err + } + hostKeySSHPub, err := ssh.NewPublicKey(hostKeyPub) + if err != nil { + return nil, nil, err + } + hostKeySSH, err := x509.MarshalPKCS8PrivateKey(hostKey) + if err != nil { + return nil, nil, err + } + pemHostKey := &pem.Block{Type: "OPENSSH PRIVATE KEY", Bytes: hostKeySSH} + + certificate := ssh.Certificate{ + CertType: ssh.HostCert, + ValidPrincipals: []string{principal}, + ValidAfter: uint64(time.Now().Unix()), + ValidBefore: ssh.CertTimeInfinity, + Reserved: []byte{}, + Key: hostKeySSHPub, + KeyId: "host_key", + Permissions: ssh.Permissions{ + CriticalOptions: map[string]string{}, + Extensions: map[string]string{}, + }, + } + if err := certificate.SignCert(rand.Reader, ca); err != nil { + return nil, nil, err + } + + return pemHostKey, &certificate, 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) { diff --git a/joinservice/internal/server/server.go b/joinservice/internal/server/server.go index 1ab2856d9..37acb46ed 100644 --- a/joinservice/internal/server/server.go +++ b/joinservice/internal/server/server.go @@ -10,7 +10,7 @@ package server import ( "context" "crypto/ed25519" - "crypto/rand" + "encoding/pem" "fmt" "log/slog" "net" @@ -118,38 +118,12 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi p, _ := peer.FromContext(ctx) nodeIP := p.Addr.String() - hostKey, _, err := ed25519.GenerateKey(nil) + hostKey, hostCertificate, err := crypto.GenerateSignedSSHHostKey(nodeIP, ca) if err != nil { - log.With(slog.Any("error", err), slog.String("ip", nodeIP)).Error("Failed to generate host key for node") - return nil, status.Errorf(codes.Internal, "generating host key for node: %s", err) - } - hostKeyPub, err := ssh.NewPublicKey(hostKey) - if err != nil { - log.With(slog.Any("error", err), slog.String("ip", nodeIP)).Error("Failed to create public host key") - return nil, status.Errorf(codes.Internal, "generating public host key for node: %s", err) - } - - certificate := ssh.Certificate{ - CertType: ssh.HostCert, - Nonce: []byte{}, - ValidPrincipals: []string{p.Addr.String()}, - ValidAfter: uint64(time.Now().Unix()), - ValidBefore: ssh.CertTimeInfinity, - Reserved: []byte{}, - Key: hostKeyPub, - KeyId: "host_key", - SignatureKey: ca.PublicKey(), - Permissions: ssh.Permissions{ - CriticalOptions: map[string]string{}, - Extensions: map[string]string{}, - }, - } - if certificate.SignCert(rand.Reader, ca) != nil { - log.With(slog.Any("error", err), slog.String("ip", nodeIP)).Error("Failed to sign host key with ssh CA for node") - return nil, status.Errorf(codes.Internal, "signing host key for node with CA: %s", err) + log.With(slog.Any("error", err)).Error("Failed to generate and sign SSH host key") + return nil, status.Errorf(codes.Internal, "generating and signing SSH host key: %s", err) } log.Info("Generated host key and certificate for node", "ip", nodeIP) - // TODO: send cert and host key to node for installation log.Info("Creating Kubernetes join token") kubeArgs, err := s.joinTokenGetter.GetJoinToken(constants.KubernetesJoinTokenTTL) @@ -219,8 +193,8 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi ControlPlaneFiles: controlPlaneFiles, KubernetesComponents: components, AuthorizedCaPublicKey: ssh.MarshalAuthorizedKey(ca.PublicKey()), - HostKey: ssh.MarshalAuthorizedKey(hostKeyPub), - HostCertificate: ssh.MarshalAuthorizedKey(&certificate), + HostKey: pem.EncodeToMemory(hostKey), + HostCertificate: ssh.MarshalAuthorizedKey(hostCertificate), }, nil }