blob: 6f113e2474f6e26c7b9034e13a5c95d056684620 [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"
Serge Bazanski3379a5d2021-09-09 12:56:40 +020011 "net"
Serge Bazanskid7d6e022021-09-01 15:03:06 +020012 "time"
13
14 "google.golang.org/grpc"
15 "google.golang.org/grpc/credentials"
Serge Bazanski636032e2022-01-26 14:21:33 +010016 "google.golang.org/grpc/status"
Serge Bazanski3379a5d2021-09-09 12:56:40 +020017 "google.golang.org/grpc/test/bufconn"
Serge Bazanskid7d6e022021-09-01 15:03:06 +020018
Serge Bazanski3379a5d2021-09-09 12:56:40 +020019 "source.monogon.dev/metropolis/node/core/identity"
Serge Bazanskid7d6e022021-09-01 15:03:06 +020020 "source.monogon.dev/metropolis/pkg/pki"
21 apb "source.monogon.dev/metropolis/proto/api"
22)
23
Serge Bazanski3379a5d2021-09-09 12:56:40 +020024type verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
25
26func verifyClusterCertificate(ca *x509.Certificate) verifyPeerCertificate {
27 return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
28 if len(rawCerts) != 1 {
29 return fmt.Errorf("server presented %d certificates, wanted exactly one", len(rawCerts))
30 }
31 serverCert, err := x509.ParseCertificate(rawCerts[0])
32 if err != nil {
33 return fmt.Errorf("server presented unparseable certificate: %w", err)
34 }
35 if _, err := identity.VerifyNodeInCluster(serverCert, ca); err != nil {
36 return fmt.Errorf("node certificate verification failed: %w", err)
37 }
38
39 return nil
40 }
41}
42
Serge Bazanskid7d6e022021-09-01 15:03:06 +020043// NewEphemeralClient dials a cluster's services using just a self-signed
44// certificate and can be used to then escrow real cluster credentials for the
45// owner.
46//
47// These self-signed certificates are used by clients connecting to the cluster
48// which want to prove ownership of an ED25519 keypair but don't have any
49// 'real' client certificate (yet). Current users include users of AAA.Escrow
50// and new nodes Registering into the Cluster.
51//
Serge Bazanski3379a5d2021-09-09 12:56:40 +020052// If 'ca' is given, the remote side will be cryptographically verified to be a
53// node that's part of the cluster represented by the ca. Otherwise, no
54// verification is performed and this function is unsafe.
55func NewEphemeralClient(remote string, private ed25519.PrivateKey, ca *x509.Certificate, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
Serge Bazanskid7d6e022021-09-01 15:03:06 +020056 template := x509.Certificate{
57 SerialNumber: big.NewInt(1),
58 NotBefore: time.Now(),
59 NotAfter: pki.UnknownNotAfter,
60
Serge Bazanski3379a5d2021-09-09 12:56:40 +020061 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
Serge Bazanskid7d6e022021-09-01 15:03:06 +020062 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
63 BasicConstraintsValid: true,
64 }
65 certificateBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, private.Public(), private)
66 if err != nil {
67 return nil, fmt.Errorf("when generating self-signed certificate: %w", err)
68 }
69 certificate := tls.Certificate{
70 Certificate: [][]byte{certificateBytes},
71 PrivateKey: private,
72 }
Serge Bazanski3379a5d2021-09-09 12:56:40 +020073 return NewAuthenticatedClient(remote, certificate, ca, opts...)
74}
Serge Bazanskid7d6e022021-09-01 15:03:06 +020075
Serge Bazanski3379a5d2021-09-09 12:56:40 +020076func NewEphemeralClientTest(listener *bufconn.Listener, private ed25519.PrivateKey, ca *x509.Certificate) (*grpc.ClientConn, error) {
77 return NewEphemeralClient("local", private, ca, grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
78 return listener.Dial()
79 }))
Serge Bazanskid7d6e022021-09-01 15:03:06 +020080}
81
82// RetrieveOwnerCertificates uses AAA.Escrow to retrieve a cluster manager
83// certificate for the initial owner of the cluster, authenticated by the
84// public/private key set in the clusters NodeParameters.ClusterBoostrap.
85//
86// The retrieved certificate can be used to dial further cluster RPCs.
87func RetrieveOwnerCertificate(ctx context.Context, aaa apb.AAAClient, private ed25519.PrivateKey) (*tls.Certificate, error) {
88 srv, err := aaa.Escrow(ctx)
89 if err != nil {
Serge Bazanski636032e2022-01-26 14:21:33 +010090 if st, ok := status.FromError(err); ok {
91 return nil, status.Errorf(st.Code(), "Escrow call failed: %s", st.Message())
92 }
93 return nil, err
Serge Bazanskid7d6e022021-09-01 15:03:06 +020094 }
95 if err := srv.Send(&apb.EscrowFromClient{
96 Parameters: &apb.EscrowFromClient_Parameters{
97 RequestedIdentityName: "owner",
98 PublicKey: private.Public().(ed25519.PublicKey),
99 },
100 }); err != nil {
101 return nil, fmt.Errorf("when sending client parameters: %w", err)
102 }
103 resp, err := srv.Recv()
104 if err != nil {
105 return nil, fmt.Errorf("when receiving server message: %w", err)
106 }
107 if len(resp.EmittedCertificate) == 0 {
108 return nil, fmt.Errorf("expected certificate, instead got needed proofs: %+v", resp.Needed)
109 }
110
111 return &tls.Certificate{
112 Certificate: [][]byte{resp.EmittedCertificate},
113 PrivateKey: private,
114 }, nil
115}
Serge Bazanski3379a5d2021-09-09 12:56:40 +0200116
117// NewAuthenticatedClient dials a cluster's services using the given TLS
118// credentials (either user or node credentials).
119//
120// If 'ca' is given, the remote side will be cryptographically verified to be a
121// node that's part of the cluster represented by the ca. Otherwise, no
122// verification is performed and this function is unsafe.
123func NewAuthenticatedClient(remote string, cert tls.Certificate, ca *x509.Certificate, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
124 config := &tls.Config{
125 Certificates: []tls.Certificate{cert},
126 InsecureSkipVerify: true,
127 }
128 if ca != nil {
129 config.VerifyPeerCertificate = verifyClusterCertificate(ca)
130 }
131 opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(config)))
132 return grpc.Dial(remote, opts...)
133}
134
135func NewNodeClient(remote string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
136 opts = append(opts, grpc.WithInsecure())
137 return grpc.Dial(remote, opts...)
138}
139
140func NewAuthenticatedClientTest(listener *bufconn.Listener, cert tls.Certificate, ca *x509.Certificate) (*grpc.ClientConn, error) {
141 return NewAuthenticatedClient("local", cert, ca, grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
142 return listener.Dial()
143 }))
144}
145
146func NewNodeClientTest(listener *bufconn.Listener) (*grpc.ClientConn, error) {
147 return NewNodeClient("local", grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
148 return listener.Dial()
149 }))
150}