blob: cc48f95fa0c8277887ea3785b3e04ba454e05fa3 [file] [log] [blame]
Serge Bazanskid7d6e022021-09-01 15:03:06 +02001package rpc
2
3import (
4 "context"
5 "crypto/ed25519"
6 "crypto/rand"
7 "crypto/tls"
8 "crypto/x509"
9 "fmt"
10 "math/big"
11 "time"
12
13 "google.golang.org/grpc"
14 "google.golang.org/grpc/credentials"
15
16 "source.monogon.dev/metropolis/pkg/pki"
17 apb "source.monogon.dev/metropolis/proto/api"
18)
19
20// NewEphemeralClient dials a cluster's services using just a self-signed
21// certificate and can be used to then escrow real cluster credentials for the
22// owner.
23//
24// These self-signed certificates are used by clients connecting to the cluster
25// which want to prove ownership of an ED25519 keypair but don't have any
26// 'real' client certificate (yet). Current users include users of AAA.Escrow
27// and new nodes Registering into the Cluster.
28//
29// If ca is given, the other side of the connection is verified to be served by
30// a node presenting a certificate signed by that CA. Otherwise, no
31// verification of the other side is performed (however, any attacker
32// impersonating the cluster cannot use the escrowed credentials as the private
33// key is never passed to the server).
34func NewEphemeralClient(remote string, private ed25519.PrivateKey, ca *x509.Certificate) (*grpc.ClientConn, error) {
35 template := x509.Certificate{
36 SerialNumber: big.NewInt(1),
37 NotBefore: time.Now(),
38 NotAfter: pki.UnknownNotAfter,
39
40 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
41 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
42 BasicConstraintsValid: true,
43 }
44 certificateBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, private.Public(), private)
45 if err != nil {
46 return nil, fmt.Errorf("when generating self-signed certificate: %w", err)
47 }
48 certificate := tls.Certificate{
49 Certificate: [][]byte{certificateBytes},
50 PrivateKey: private,
51 }
52 creds := credentials.NewTLS(&tls.Config{
53 Certificates: []tls.Certificate{
54 certificate,
55 },
56 InsecureSkipVerify: true,
57 VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
58 if len(rawCerts) < 1 {
59 return fmt.Errorf("server presented no certificate")
60 }
61 certs := make([]*x509.Certificate, len(rawCerts))
62 for i, rawCert := range rawCerts {
63 cert, err := x509.ParseCertificate(rawCert)
64 if err != nil {
65 return fmt.Errorf("could not parse server certificate %d: %v", i, err)
66 }
67 certs[i] = cert
68 }
69
70 if ca != nil {
71 // CA given, perform full chain verification.
72 roots := x509.NewCertPool()
73 roots.AddCert(ca)
74 opts := x509.VerifyOptions{
75 Roots: roots,
76 Intermediates: x509.NewCertPool(),
77 }
78 for _, cert := range certs[1:] {
79 opts.Intermediates.AddCert(cert)
80 }
81 _, err := certs[0].Verify(opts)
82 if err != nil {
83 return err
84 }
85 }
86
87 // Regardless of CA given, ensure that the leaf certificate has the
88 // right ExtKeyUsage.
89 for _, ku := range certs[0].ExtKeyUsage {
90 if ku == x509.ExtKeyUsageServerAuth {
91 return nil
92 }
93 }
94 return fmt.Errorf("server presented a certificate without server auth ext key usage")
95 },
96 })
97
98 return grpc.Dial(remote, grpc.WithTransportCredentials(creds))
99}
100
101// RetrieveOwnerCertificates uses AAA.Escrow to retrieve a cluster manager
102// certificate for the initial owner of the cluster, authenticated by the
103// public/private key set in the clusters NodeParameters.ClusterBoostrap.
104//
105// The retrieved certificate can be used to dial further cluster RPCs.
106func RetrieveOwnerCertificate(ctx context.Context, aaa apb.AAAClient, private ed25519.PrivateKey) (*tls.Certificate, error) {
107 srv, err := aaa.Escrow(ctx)
108 if err != nil {
109 return nil, fmt.Errorf("when opening Escrow RPC: %w", err)
110 }
111 if err := srv.Send(&apb.EscrowFromClient{
112 Parameters: &apb.EscrowFromClient_Parameters{
113 RequestedIdentityName: "owner",
114 PublicKey: private.Public().(ed25519.PublicKey),
115 },
116 }); err != nil {
117 return nil, fmt.Errorf("when sending client parameters: %w", err)
118 }
119 resp, err := srv.Recv()
120 if err != nil {
121 return nil, fmt.Errorf("when receiving server message: %w", err)
122 }
123 if len(resp.EmittedCertificate) == 0 {
124 return nil, fmt.Errorf("expected certificate, instead got needed proofs: %+v", resp.Needed)
125 }
126
127 return &tls.Certificate{
128 Certificate: [][]byte{resp.EmittedCertificate},
129 PrivateKey: private,
130 }, nil
131}