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