| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * |
| * Copyright 2021 gRPC authors. |
| * |
| */ |
| |
| package server |
| |
| import ( |
| "errors" |
| "fmt" |
| "net" |
| "sync" |
| "time" |
| ) |
| |
| import ( |
| "google.golang.org/grpc/credentials/tls/certprovider" |
| ) |
| |
| import ( |
| "dubbo.apache.org/dubbo-go/v3/xds/client/resource" |
| xdsinternal "dubbo.apache.org/dubbo-go/v3/xds/utils/credentials/xds" |
| ) |
| |
| // connWrapper is a thin wrapper around a net.Conn returned by Accept(). It |
| // provides the following additional functionality: |
| // 1. A way to retrieve the configured deadline. This is required by the |
| // ServerHandshake() method of the xdsCredentials when it attempts to read |
| // key material from the certificate providers. |
| // 2. Implements the XDSHandshakeInfo() method used by the xdsCredentials to |
| // retrieve the configured certificate providers. |
| // 3. xDS filter_chain matching logic to select appropriate security |
| // configuration for the incoming connection. |
| type connWrapper struct { |
| net.Conn |
| |
| // The specific filter chain picked for handling this connection. |
| filterChain *resource.FilterChain |
| |
| // A reference fo the listenerWrapper on which this connection was accepted. |
| parent *listenerWrapper |
| |
| // The certificate providers created for this connection. |
| rootProvider, identityProvider certprovider.Provider |
| |
| // The connection deadline as configured by the grpc.Server on the rawConn |
| // that is returned by a call to Accept(). This is set to the connection |
| // timeout value configured by the user (or to a default value) before |
| // initiating the transport credential handshake, and set to zero after |
| // completing the HTTP2 handshake. |
| deadlineMu sync.Mutex |
| deadline time.Time |
| |
| // The virtual hosts with matchable routes and instantiated HTTP Filters per |
| // route. |
| virtualHosts []resource.VirtualHostWithInterceptors |
| } |
| |
| // VirtualHosts returns the virtual hosts to be used for server side routing. |
| func (c *connWrapper) VirtualHosts() []resource.VirtualHostWithInterceptors { |
| return c.virtualHosts |
| } |
| |
| // SetDeadline makes a copy of the passed in deadline and forwards the call to |
| // the underlying rawConn. |
| func (c *connWrapper) SetDeadline(t time.Time) error { |
| c.deadlineMu.Lock() |
| c.deadline = t |
| c.deadlineMu.Unlock() |
| return c.Conn.SetDeadline(t) |
| } |
| |
| // GetDeadline returns the configured deadline. This will be invoked by the |
| // ServerHandshake() method of the XdsCredentials, which needs a deadline to |
| // pass to the certificate provider. |
| func (c *connWrapper) GetDeadline() time.Time { |
| c.deadlineMu.Lock() |
| t := c.deadline |
| c.deadlineMu.Unlock() |
| return t |
| } |
| |
| // XDSHandshakeInfo returns a HandshakeInfo with appropriate security |
| // configuration for this connection. This method is invoked by the |
| // ServerHandshake() method of the XdsCredentials. |
| func (c *connWrapper) XDSHandshakeInfo() (*xdsinternal.HandshakeInfo, error) { |
| // Ideally this should never happen, since xdsCredentials are the only ones |
| // which will invoke this method at handshake time. But to be on the safe |
| // side, we avoid acting on the security configuration received from the |
| // control plane when the user has not configured the use of xDS |
| // credentials, by checking the value of this flag. |
| if !c.parent.xdsCredsInUse { |
| return nil, errors.New("user has not configured xDS credentials") |
| } |
| |
| if c.filterChain.SecurityCfg == nil { |
| // If the security config is empty, this means that the control plane |
| // did not provide any security configuration and therefore we should |
| // return an empty HandshakeInfo here so that the xdsCreds can use the |
| // configured fallback credentials. |
| return xdsinternal.NewHandshakeInfo(nil, nil), nil |
| } |
| |
| cpc := c.parent.xdsC.BootstrapConfig().CertProviderConfigs |
| // Identity provider name is mandatory on the server-side, and this is |
| // enforced when the resource is received at the XDSClient layer. |
| secCfg := c.filterChain.SecurityCfg |
| ip, err := buildProviderFunc(cpc, secCfg.IdentityInstanceName, secCfg.IdentityCertName, true, false) |
| if err != nil { |
| return nil, err |
| } |
| // Root provider name is optional and required only when doing mTLS. |
| var rp certprovider.Provider |
| if instance, cert := secCfg.RootInstanceName, secCfg.RootCertName; instance != "" { |
| rp, err = buildProviderFunc(cpc, instance, cert, false, true) |
| if err != nil { |
| return nil, err |
| } |
| } |
| c.identityProvider = ip |
| c.rootProvider = rp |
| |
| xdsHI := xdsinternal.NewHandshakeInfo(c.rootProvider, c.identityProvider) |
| xdsHI.SetRequireClientCert(secCfg.RequireClientCert) |
| return xdsHI, nil |
| } |
| |
| // Close closes the providers and the underlying connection. |
| func (c *connWrapper) Close() error { |
| if c.identityProvider != nil { |
| c.identityProvider.Close() |
| } |
| if c.rootProvider != nil { |
| c.rootProvider.Close() |
| } |
| return c.Conn.Close() |
| } |
| |
| func buildProviderFunc(configs map[string]*certprovider.BuildableConfig, instanceName, certName string, wantIdentity, wantRoot bool) (certprovider.Provider, error) { |
| cfg, ok := configs[instanceName] |
| if !ok { |
| return nil, fmt.Errorf("certificate provider instance %q not found in bootstrap file", instanceName) |
| } |
| provider, err := cfg.Build(certprovider.BuildOptions{ |
| CertName: certName, |
| WantIdentity: wantIdentity, |
| WantRoot: wantRoot, |
| }) |
| if err != nil { |
| // This error is not expected since the bootstrap process parses the |
| // config and makes sure that it is acceptable to the plugin. Still, it |
| // is possible that the plugin parses the config successfully, but its |
| // Build() method errors out. |
| return nil, fmt.Errorf("failed to get security plugin instance (%+v): %v", cfg, err) |
| } |
| return provider, nil |
| } |