xds.go 10 KB

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