google.golang.org/grpc@v1.74.2/credentials/xds/xds.go (about) 1 /* 2 * 3 * Copyright 2020 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package xds provides a transport credentials implementation where the 20 // security configuration is pushed by a management server using xDS APIs. 21 package xds 22 23 import ( 24 "context" 25 "crypto/tls" 26 "errors" 27 "net" 28 "sync/atomic" 29 "time" 30 31 "google.golang.org/grpc/credentials" 32 credinternal "google.golang.org/grpc/internal/credentials" 33 xdsinternal "google.golang.org/grpc/internal/credentials/xds" 34 ) 35 36 // ClientOptions contains parameters to configure a new client-side xDS 37 // credentials implementation. 38 type ClientOptions struct { 39 // FallbackCreds specifies the fallback credentials to be used when either 40 // the `xds` scheme is not used in the user's dial target or when the 41 // management server does not return any security configuration. Attempts to 42 // create client credentials without fallback credentials will fail. 43 FallbackCreds credentials.TransportCredentials 44 } 45 46 // NewClientCredentials returns a new client-side transport credentials 47 // implementation which uses xDS APIs to fetch its security configuration. 48 func NewClientCredentials(opts ClientOptions) (credentials.TransportCredentials, error) { 49 if opts.FallbackCreds == nil { 50 return nil, errors.New("missing fallback credentials") 51 } 52 return &credsImpl{ 53 isClient: true, 54 fallback: opts.FallbackCreds, 55 }, nil 56 } 57 58 // ServerOptions contains parameters to configure a new server-side xDS 59 // credentials implementation. 60 type ServerOptions struct { 61 // FallbackCreds specifies the fallback credentials to be used when the 62 // management server does not return any security configuration. Attempts to 63 // create server credentials without fallback credentials will fail. 64 FallbackCreds credentials.TransportCredentials 65 } 66 67 // NewServerCredentials returns a new server-side transport credentials 68 // implementation which uses xDS APIs to fetch its security configuration. 69 func NewServerCredentials(opts ServerOptions) (credentials.TransportCredentials, error) { 70 if opts.FallbackCreds == nil { 71 return nil, errors.New("missing fallback credentials") 72 } 73 return &credsImpl{ 74 isClient: false, 75 fallback: opts.FallbackCreds, 76 }, nil 77 } 78 79 // credsImpl is an implementation of the credentials.TransportCredentials 80 // interface which uses xDS APIs to fetch its security configuration. 81 type credsImpl struct { 82 isClient bool 83 fallback credentials.TransportCredentials 84 } 85 86 // ClientHandshake performs the TLS handshake on the client-side. 87 // 88 // It looks for the presence of a HandshakeInfo value in the passed in context 89 // (added using a call to NewContextWithHandshakeInfo()), and retrieves identity 90 // and root certificates from there. It also retrieves a list of acceptable SANs 91 // and uses a custom verification function to validate the certificate presented 92 // by the peer. It uses fallback credentials if no HandshakeInfo is present in 93 // the passed in context. 94 func (c *credsImpl) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { 95 if !c.isClient { 96 return nil, nil, errors.New("ClientHandshake() is not supported for server credentials") 97 } 98 99 // The CDS balancer constructs a new HandshakeInfo using a call to 100 // NewHandshakeInfo(), and then adds it to the attributes field of the 101 // resolver.Address when handling calls to NewSubConn(). The transport layer 102 // takes care of shipping these attributes in the context to this handshake 103 // function. We first read the credentials.ClientHandshakeInfo type from the 104 // context, which contains the attributes added by the CDS balancer. We then 105 // read the HandshakeInfo from the attributes to get to the actual data that 106 // we need here for the handshake. 107 chi := credentials.ClientHandshakeInfoFromContext(ctx) 108 // If there are no attributes in the received context or the attributes does 109 // not contain a HandshakeInfo, it could either mean that the user did not 110 // specify an `xds` scheme in their dial target or that the xDS server did 111 // not provide any security configuration. In both of these cases, we use 112 // the fallback credentials specified by the user. 113 if chi.Attributes == nil { 114 return c.fallback.ClientHandshake(ctx, authority, rawConn) 115 } 116 117 uPtr := xdsinternal.GetHandshakeInfo(chi.Attributes) 118 hi := (*xdsinternal.HandshakeInfo)(atomic.LoadPointer(uPtr)) 119 if hi.UseFallbackCreds() { 120 return c.fallback.ClientHandshake(ctx, authority, rawConn) 121 } 122 123 // We build the tls.Config with the following values 124 // 1. Root certificate as returned by the root provider. 125 // 2. Identity certificate as returned by the identity provider. This may be 126 // empty on the client side, if the client is not doing mTLS. 127 // 3. InsecureSkipVerify to true. Certificates used in Mesh environments 128 // usually contains the identity of the workload presenting the 129 // certificate as a SAN (instead of a hostname in the CommonName field). 130 // This means that normal certificate verification as done by the 131 // standard library will fail. 132 // 4. Key usage to match whether client/server usage. 133 // 5. A `VerifyPeerCertificate` function which performs normal peer 134 // cert verification using configured roots, and the custom SAN checks. 135 cfg, err := hi.ClientSideTLSConfig(ctx) 136 if err != nil { 137 return nil, nil, err 138 } 139 140 // Perform the TLS handshake with the tls.Config that we have. We run the 141 // actual Handshake() function in a goroutine because we need to respect the 142 // deadline specified on the passed in context, and we need a way to cancel 143 // the handshake if the context is cancelled. 144 conn := tls.Client(rawConn, cfg) 145 errCh := make(chan error, 1) 146 go func() { 147 errCh <- conn.Handshake() 148 close(errCh) 149 }() 150 select { 151 case err := <-errCh: 152 if err != nil { 153 conn.Close() 154 return nil, nil, err 155 } 156 case <-ctx.Done(): 157 conn.Close() 158 return nil, nil, ctx.Err() 159 } 160 info := credentials.TLSInfo{ 161 State: conn.ConnectionState(), 162 CommonAuthInfo: credentials.CommonAuthInfo{ 163 SecurityLevel: credentials.PrivacyAndIntegrity, 164 }, 165 SPIFFEID: credinternal.SPIFFEIDFromState(conn.ConnectionState()), 166 } 167 return credinternal.WrapSyscallConn(rawConn, conn), info, nil 168 } 169 170 // ServerHandshake performs the TLS handshake on the server-side. 171 func (c *credsImpl) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { 172 if c.isClient { 173 return nil, nil, errors.New("ServerHandshake is not supported for client credentials") 174 } 175 176 // An xds-enabled gRPC server wraps the underlying raw net.Conn in a type 177 // that provides a way to retrieve `HandshakeInfo`, which contains the 178 // certificate providers to be used during the handshake. If the net.Conn 179 // passed to this function does not implement this interface, or if the 180 // `HandshakeInfo` does not contain the information we are looking for, we 181 // delegate the handshake to the fallback credentials. 182 hiConn, ok := rawConn.(interface { 183 XDSHandshakeInfo() (*xdsinternal.HandshakeInfo, error) 184 }) 185 if !ok { 186 return c.fallback.ServerHandshake(rawConn) 187 } 188 hi, err := hiConn.XDSHandshakeInfo() 189 if err != nil { 190 return nil, nil, err 191 } 192 if hi.UseFallbackCreds() { 193 return c.fallback.ServerHandshake(rawConn) 194 } 195 196 // An xds-enabled gRPC server is expected to wrap the underlying raw 197 // net.Conn in a type which provides a way to retrieve the deadline set on 198 // it. If we cannot retrieve the deadline here, we fail (by setting deadline 199 // to time.Now()), instead of using a default deadline and possibly taking 200 // longer to eventually fail. 201 deadline := time.Now() 202 if dConn, ok := rawConn.(interface{ GetDeadline() time.Time }); ok { 203 deadline = dConn.GetDeadline() 204 } 205 ctx, cancel := context.WithDeadline(context.Background(), deadline) 206 defer cancel() 207 cfg, err := hi.ServerSideTLSConfig(ctx) 208 if err != nil { 209 return nil, nil, err 210 } 211 212 conn := tls.Server(rawConn, cfg) 213 if err := conn.Handshake(); err != nil { 214 conn.Close() 215 return nil, nil, err 216 } 217 info := credentials.TLSInfo{ 218 State: conn.ConnectionState(), 219 CommonAuthInfo: credentials.CommonAuthInfo{ 220 SecurityLevel: credentials.PrivacyAndIntegrity, 221 }, 222 } 223 info.SPIFFEID = credinternal.SPIFFEIDFromState(conn.ConnectionState()) 224 return credinternal.WrapSyscallConn(rawConn, conn), info, nil 225 } 226 227 // Info provides the ProtocolInfo of this TransportCredentials. 228 func (c *credsImpl) Info() credentials.ProtocolInfo { 229 return credentials.ProtocolInfo{SecurityProtocol: "tls"} 230 } 231 232 // Clone makes a copy of this TransportCredentials. 233 func (c *credsImpl) Clone() credentials.TransportCredentials { 234 clone := *c 235 return &clone 236 } 237 238 func (c *credsImpl) OverrideServerName(_ string) error { 239 return errors.New("serverName for peer validation must be configured as a list of acceptable SANs") 240 } 241 242 // UsesXDS returns true if c uses xDS to fetch security configuration 243 // used at handshake time, and false otherwise. 244 func (c *credsImpl) UsesXDS() bool { 245 return true 246 }