github.com/letsencrypt/boulder@v0.20251208.0/grpc/creds/creds.go (about) 1 package creds 2 3 import ( 4 "context" 5 "crypto/tls" 6 "crypto/x509" 7 "errors" 8 "fmt" 9 "net" 10 11 "google.golang.org/grpc/credentials" 12 ) 13 14 var ( 15 ErrClientHandshakeNop = errors.New( 16 "boulder/grpc/creds: Client-side handshakes are not implemented with " + 17 "serverTransportCredentials") 18 ErrServerHandshakeNop = errors.New( 19 "boulder/grpc/creds: Server-side handshakes are not implemented with " + 20 "clientTransportCredentials") 21 ErrOverrideServerNameNop = errors.New( 22 "boulder/grpc/creds: OverrideServerName() is not implemented") 23 ErrNilServerConfig = errors.New( 24 "boulder/grpc/creds: `serverConfig` must not be nil") 25 ErrEmptyPeerCerts = errors.New( 26 "boulder/grpc/creds: validateClient given state with empty PeerCertificates") 27 ) 28 29 type ErrSANNotAccepted struct { 30 got, expected []string 31 } 32 33 func (e ErrSANNotAccepted) Error() string { 34 return fmt.Sprintf("boulder/grpc/creds: client certificate SAN was invalid. "+ 35 "Got %q, expected one of %q.", e.got, e.expected) 36 } 37 38 // clientTransportCredentials is a grpc/credentials.TransportCredentials which supports 39 // connecting to, and verifying multiple DNS names 40 type clientTransportCredentials struct { 41 roots *x509.CertPool 42 clients []tls.Certificate 43 // If set, this is used as the hostname to validate on certificates, instead 44 // of the value passed to ClientHandshake by grpc. 45 hostOverride string 46 } 47 48 // NewClientCredentials returns a new initialized grpc/credentials.TransportCredentials for client usage 49 func NewClientCredentials(rootCAs *x509.CertPool, clientCerts []tls.Certificate, hostOverride string) credentials.TransportCredentials { 50 return &clientTransportCredentials{rootCAs, clientCerts, hostOverride} 51 } 52 53 // ClientHandshake does the authentication handshake specified by the corresponding 54 // authentication protocol on rawConn for clients. It returns the authenticated 55 // connection and the corresponding auth information about the connection. 56 // Implementations must use the provided context to implement timely cancellation. 57 func (tc *clientTransportCredentials) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { 58 var err error 59 host := tc.hostOverride 60 if host == "" { 61 // IMPORTANT: Don't wrap the errors returned from this method. gRPC expects to be 62 // able to check err.Temporary to spot temporary errors and reconnect when they happen. 63 host, _, err = net.SplitHostPort(addr) 64 if err != nil { 65 return nil, nil, err 66 } 67 } 68 conn := tls.Client(rawConn, &tls.Config{ 69 ServerName: host, 70 RootCAs: tc.roots, 71 Certificates: tc.clients, 72 }) 73 err = conn.HandshakeContext(ctx) 74 if err != nil { 75 _ = rawConn.Close() 76 return nil, nil, err 77 } 78 return conn, nil, nil 79 } 80 81 // ServerHandshake is not implemented for a `clientTransportCredentials`, use 82 // a `serverTransportCredentials` if you require `ServerHandshake`. 83 func (tc *clientTransportCredentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { 84 return nil, nil, ErrServerHandshakeNop 85 } 86 87 // Info returns information about the transport protocol used 88 func (tc *clientTransportCredentials) Info() credentials.ProtocolInfo { 89 return credentials.ProtocolInfo{SecurityProtocol: "tls"} 90 } 91 92 // GetRequestMetadata returns nil, nil since TLS credentials do not have metadata. 93 func (tc *clientTransportCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 94 return nil, nil 95 } 96 97 // RequireTransportSecurity always returns true because TLS is transport security 98 func (tc *clientTransportCredentials) RequireTransportSecurity() bool { 99 return true 100 } 101 102 // Clone returns a copy of the clientTransportCredentials 103 func (tc *clientTransportCredentials) Clone() credentials.TransportCredentials { 104 return NewClientCredentials(tc.roots, tc.clients, tc.hostOverride) 105 } 106 107 // OverrideServerName is not implemented and here only to satisfy the interface 108 func (tc *clientTransportCredentials) OverrideServerName(serverNameOverride string) error { 109 return ErrOverrideServerNameNop 110 } 111 112 // serverTransportCredentials is a grpc/credentials.TransportCredentials which supports 113 // filtering acceptable peer connections by a list of accepted client certificate SANs 114 type serverTransportCredentials struct { 115 serverConfig *tls.Config 116 acceptedSANs map[string]struct{} 117 } 118 119 // NewServerCredentials returns a new initialized grpc/credentials.TransportCredentials for server usage 120 func NewServerCredentials(serverConfig *tls.Config, acceptedSANs map[string]struct{}) (credentials.TransportCredentials, error) { 121 if serverConfig == nil { 122 return nil, ErrNilServerConfig 123 } 124 125 return &serverTransportCredentials{serverConfig, acceptedSANs}, nil 126 } 127 128 // validateClient checks a peer's client certificate's SAN entries against 129 // a list of accepted SANs. If the client certificate does not have a SAN on the 130 // list it is rejected. 131 // 132 // Note 1: This function *only* verifies the SAN entries! Callers are expected to 133 // have provided the `tls.ConnectionState` returned from a validate (e.g. 134 // non-error producing) `conn.Handshake()`. 135 // 136 // Note 2: We do *not* consider the client certificate subject common name. The 137 // CN field is deprecated and should be present as a DNS SAN! 138 func (tc *serverTransportCredentials) validateClient(peerState tls.ConnectionState) error { 139 /* 140 * If there's no list of accepted SANs, all clients are OK 141 * 142 * TODO(@cpu): This should be converted to a hard error at initialization time 143 * once we have deployed & updated all gRPC configurations to have an accepted 144 * SAN list configured 145 */ 146 if len(tc.acceptedSANs) == 0 { 147 return nil 148 } 149 150 // If `conn.Handshake()` is called before `validateClient` this should not 151 // occur. We return an error in this event primarily for unit tests that may 152 // call `validateClient` with manufactured & artificial connection states. 153 if len(peerState.PeerCertificates) < 1 { 154 return ErrEmptyPeerCerts 155 } 156 157 // Since we call `conn.Handshake()` before `validateClient` and ensure 158 // a non-error response we don't need to validate anything except the presence 159 // of an acceptable SAN in the leaf entry of `PeerCertificates`. The tls 160 // package's `serverHandshake` and in particular, `processCertsFromClient` 161 // will address everything else as an error returned from `Handshake()`. 162 leaf := peerState.PeerCertificates[0] 163 164 // Combine both the DNS and IP address subjectAlternativeNames into a single 165 // list for checking. 166 var receivedSANs []string 167 receivedSANs = append(receivedSANs, leaf.DNSNames...) 168 for _, ip := range leaf.IPAddresses { 169 receivedSANs = append(receivedSANs, ip.String()) 170 } 171 172 for _, name := range receivedSANs { 173 if _, ok := tc.acceptedSANs[name]; ok { 174 return nil 175 } 176 } 177 178 // If none of the DNS or IP SANs on the leaf certificate matched the 179 // acceptable list, the client isn't valid and we error 180 var acceptableSANs []string 181 for k := range tc.acceptedSANs { 182 acceptableSANs = append(acceptableSANs, k) 183 } 184 return ErrSANNotAccepted{receivedSANs, acceptableSANs} 185 } 186 187 // ServerHandshake does the authentication handshake for servers. It returns 188 // the authenticated connection and the corresponding auth information about 189 // the connection. 190 func (tc *serverTransportCredentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { 191 // Perform the server <- client TLS handshake. This will validate the peer's 192 // client certificate. 193 conn := tls.Server(rawConn, tc.serverConfig) 194 err := conn.Handshake() 195 if err != nil { 196 return nil, nil, err 197 } 198 199 // In addition to the validation from `conn.Handshake()` we apply further 200 // constraints on what constitutes a valid peer 201 err = tc.validateClient(conn.ConnectionState()) 202 if err != nil { 203 return nil, nil, err 204 } 205 206 return conn, credentials.TLSInfo{State: conn.ConnectionState()}, nil 207 } 208 209 // ClientHandshake is not implemented for a `serverTransportCredentials`, use 210 // a `clientTransportCredentials` if you require `ClientHandshake`. 211 func (tc *serverTransportCredentials) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { 212 return nil, nil, ErrClientHandshakeNop 213 } 214 215 // Info provides the ProtocolInfo of this TransportCredentials. 216 func (tc *serverTransportCredentials) Info() credentials.ProtocolInfo { 217 return credentials.ProtocolInfo{SecurityProtocol: "tls"} 218 } 219 220 // GetRequestMetadata returns nil, nil since TLS credentials do not have metadata. 221 func (tc *serverTransportCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 222 return nil, nil 223 } 224 225 // RequireTransportSecurity always returns true because TLS is transport security 226 func (tc *serverTransportCredentials) RequireTransportSecurity() bool { 227 return true 228 } 229 230 // Clone returns a copy of the serverTransportCredentials 231 func (tc *serverTransportCredentials) Clone() credentials.TransportCredentials { 232 clone, _ := NewServerCredentials(tc.serverConfig, tc.acceptedSANs) 233 return clone 234 } 235 236 // OverrideServerName is not implemented and here only to satisfy the interface 237 func (tc *serverTransportCredentials) OverrideServerName(serverNameOverride string) error { 238 return ErrOverrideServerNameNop 239 }