istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/echo/server/forwarder/config.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package forwarder
    16  
    17  import (
    18  	"crypto/tls"
    19  	"crypto/x509"
    20  	"crypto/x509/pkix"
    21  	"encoding/asn1"
    22  	"fmt"
    23  	"net/http"
    24  	"net/url"
    25  	"os"
    26  	"strings"
    27  	"time"
    28  
    29  	"istio.io/istio/pkg/test/echo/common"
    30  	"istio.io/istio/pkg/test/echo/common/scheme"
    31  	"istio.io/istio/pkg/test/echo/proto"
    32  )
    33  
    34  type Config struct {
    35  	Request *proto.ForwardEchoRequest
    36  	UDS     string
    37  	// XDSTestBootstrap, for gRPC forwarders, is used to set the bootstrap without using a global one defined in the env
    38  	XDSTestBootstrap []byte
    39  	// Http proxy used for connection
    40  	Proxy string
    41  
    42  	// Filled in values.
    43  	scheme                  scheme.Instance
    44  	tlsConfig               *tls.Config
    45  	getClientCertificate    func(info *tls.CertificateRequestInfo) (*tls.Certificate, error)
    46  	checkRedirect           func(req *http.Request, via []*http.Request) error
    47  	proxyURL                func(*http.Request) (*url.URL, error)
    48  	timeout                 time.Duration
    49  	count                   int
    50  	headers                 http.Header
    51  	newConnectionPerRequest bool
    52  	PropagateResponse       func(req *http.Request, resp *http.Response)
    53  	forceDNSLookup          bool
    54  	hostHeader              string
    55  	urlHost                 string
    56  	urlPath                 string
    57  	method                  string
    58  	secure                  bool
    59  
    60  	hboneTLSConfig       *tls.Config
    61  	hboneClientConfig    func(info *tls.CertificateRequestInfo) (*tls.Certificate, error)
    62  	hboneHeaders         http.Header
    63  	proxyProtocolVersion int
    64  	previousResponse     *http.Response
    65  }
    66  
    67  func (c *Config) fillDefaults() error {
    68  	c.checkRedirect = checkRedirectFunc(c.Request)
    69  	c.timeout = common.GetTimeout(c.Request)
    70  	c.count = common.GetCount(c.Request)
    71  	c.headers = common.GetHeaders(c.Request)
    72  	c.hboneHeaders = common.ProtoToHTTPHeaders(c.Request.Hbone.GetHeaders())
    73  
    74  	// Extract the host from the headers and then remove it.
    75  	c.hostHeader = c.headers.Get(hostHeader)
    76  	c.headers.Del(hostHeader)
    77  
    78  	c.urlHost, c.urlPath = splitPath(c.Request.Url)
    79  
    80  	c.method = c.Request.Method
    81  	if c.method == "" {
    82  		c.method = "GET"
    83  	}
    84  
    85  	if i := strings.IndexByte(c.Request.Url, ':'); i > 0 {
    86  		c.scheme = scheme.Instance(strings.ToLower(c.Request.Url[0:i]))
    87  	} else {
    88  		return fmt.Errorf("missing protocol scheme in the request URL: %s", c.Request.Url)
    89  	}
    90  
    91  	var err error
    92  	c.getClientCertificate, err = getClientCertificateFunc(c.Request)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	c.secure = c.getClientCertificate != nil
    97  
    98  	c.tlsConfig, err = newTLSConfig(c)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	c.hboneClientConfig, err = getHBONEClientConfig(c.Request.Hbone)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	c.hboneTLSConfig, err = newHBONETLSConfig(c)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	// Parse the proxy if specified.
   113  	if len(c.Proxy) > 0 {
   114  		proxyURL, err := url.Parse(c.Proxy)
   115  		if err != nil {
   116  			return err
   117  		}
   118  
   119  		c.proxyURL = http.ProxyURL(proxyURL)
   120  	}
   121  
   122  	// Configure reuseConnection and forceDNSLookup as appropriate.
   123  	switch c.scheme {
   124  	case scheme.DNS:
   125  		c.newConnectionPerRequest = true
   126  		c.forceDNSLookup = true
   127  	case scheme.TCP, scheme.TLS, scheme.WebSocket, scheme.HTTPS:
   128  		c.newConnectionPerRequest = true
   129  		c.forceDNSLookup = c.Request.ForceDNSLookup
   130  	default:
   131  		c.newConnectionPerRequest = c.Request.NewConnectionPerRequest
   132  		c.forceDNSLookup = c.newConnectionPerRequest && c.Request.ForceDNSLookup
   133  	}
   134  
   135  	switch c.Request.ProxyProtocolVersion {
   136  	case proto.ProxyProtoVersion_V1:
   137  		c.proxyProtocolVersion = 1
   138  	case proto.ProxyProtoVersion_V2:
   139  		c.proxyProtocolVersion = 2
   140  	default:
   141  		c.proxyProtocolVersion = 0
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  func splitPath(raw string) (url, path string) {
   148  	schemeSep := "://"
   149  	schemeBegin := strings.Index(raw, schemeSep)
   150  	if schemeBegin == -1 {
   151  		return raw, ""
   152  	}
   153  	schemeEnd := schemeBegin + len(schemeSep)
   154  	pathBegin := strings.IndexByte(raw[schemeEnd:], '/')
   155  	if pathBegin == -1 {
   156  		return raw, ""
   157  	}
   158  	return raw[:schemeEnd+pathBegin], raw[schemeEnd+pathBegin:]
   159  }
   160  
   161  func getClientCertificateFunc(r *proto.ForwardEchoRequest) (func(info *tls.CertificateRequestInfo) (*tls.Certificate, error), error) {
   162  	if r.KeyFile != "" && r.CertFile != "" {
   163  		certData, err := os.ReadFile(r.CertFile)
   164  		if err != nil {
   165  			return nil, fmt.Errorf("failed to load client certificate: %v", err)
   166  		}
   167  		r.Cert = string(certData)
   168  		keyData, err := os.ReadFile(r.KeyFile)
   169  		if err != nil {
   170  			return nil, fmt.Errorf("failed to load client certificate key: %v", err)
   171  		}
   172  		r.Key = string(keyData)
   173  	}
   174  
   175  	if r.Cert != "" && r.Key != "" {
   176  		cert, err := tls.X509KeyPair([]byte(r.Cert), []byte(r.Key))
   177  		if err != nil {
   178  			return nil, fmt.Errorf("failed to parse x509 key pair: %v", err)
   179  		}
   180  
   181  		for _, c := range cert.Certificate {
   182  			cert, err := x509.ParseCertificate(c)
   183  			if err != nil {
   184  				fwLog.Errorf("Failed to parse client certificate: %v", err)
   185  			}
   186  			fwLog.Debugf("Using client certificate [%s] issued by %s", cert.SerialNumber, cert.Issuer)
   187  			for _, uri := range cert.URIs {
   188  				fwLog.Debugf("  URI SAN: %s", uri)
   189  			}
   190  		}
   191  		// nolint: unparam
   192  		return func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
   193  			fwLog.Debugf("Peer asking for client certificate")
   194  			for i, ca := range info.AcceptableCAs {
   195  				x := &pkix.RDNSequence{}
   196  				if _, err := asn1.Unmarshal(ca, x); err != nil {
   197  					fwLog.Errorf("Failed to decode AcceptableCA[%d]: %v", i, err)
   198  				} else {
   199  					name := &pkix.Name{}
   200  					name.FillFromRDNSequence(x)
   201  					fwLog.Debugf("  AcceptableCA[%d]: %s", i, name)
   202  				}
   203  			}
   204  
   205  			return &cert, nil
   206  		}, nil
   207  	}
   208  
   209  	return nil, nil
   210  }
   211  
   212  func getHBONEClientConfig(r *proto.HBONE) (func(info *tls.CertificateRequestInfo) (*tls.Certificate, error), error) {
   213  	if r == nil {
   214  		return nil, nil
   215  	}
   216  	if r.KeyFile != "" && r.CertFile != "" {
   217  		certData, err := os.ReadFile(r.CertFile)
   218  		if err != nil {
   219  			return nil, fmt.Errorf("failed to load client certificate: %v", err)
   220  		}
   221  		r.Cert = string(certData)
   222  		keyData, err := os.ReadFile(r.KeyFile)
   223  		if err != nil {
   224  			return nil, fmt.Errorf("failed to load client certificate key: %v", err)
   225  		}
   226  		r.Key = string(keyData)
   227  	}
   228  
   229  	if r.Cert != "" && r.Key != "" {
   230  		cert, err := tls.X509KeyPair([]byte(r.Cert), []byte(r.Key))
   231  		if err != nil {
   232  			return nil, fmt.Errorf("failed to parse x509 key pair: %v", err)
   233  		}
   234  
   235  		for _, c := range cert.Certificate {
   236  			cert, err := x509.ParseCertificate(c)
   237  			if err != nil {
   238  				fwLog.Errorf("Failed to parse client certificate: %v", err)
   239  			}
   240  			fwLog.Debugf("Using client certificate [%s] issued by %s", cert.SerialNumber, cert.Issuer)
   241  			for _, uri := range cert.URIs {
   242  				fwLog.Debugf("  URI SAN: %s", uri)
   243  			}
   244  		}
   245  		// nolint: unparam
   246  		return func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
   247  			fwLog.Debugf("Peer asking for client certificate")
   248  			for i, ca := range info.AcceptableCAs {
   249  				x := &pkix.RDNSequence{}
   250  				if _, err := asn1.Unmarshal(ca, x); err != nil {
   251  					fwLog.Errorf("Failed to decode AcceptableCA[%d]: %v", i, err)
   252  				} else {
   253  					name := &pkix.Name{}
   254  					name.FillFromRDNSequence(x)
   255  					fwLog.Debugf("  AcceptableCA[%d]: %s", i, name)
   256  				}
   257  			}
   258  
   259  			return &cert, nil
   260  		}, nil
   261  	}
   262  
   263  	return nil, nil
   264  }
   265  
   266  func newTLSConfig(c *Config) (*tls.Config, error) {
   267  	r := c.Request
   268  	tlsConfig := &tls.Config{
   269  		GetClientCertificate: c.getClientCertificate,
   270  		NextProtos:           r.GetAlpn().GetValue(),
   271  		ServerName:           r.ServerName,
   272  		MinVersion:           tls.VersionTLS12,
   273  	}
   274  	if r.CaCertFile != "" {
   275  		certData, err := os.ReadFile(r.CaCertFile)
   276  		if err != nil {
   277  			return nil, fmt.Errorf("failed to load client certificate: %v", err)
   278  		}
   279  		r.CaCert = string(certData)
   280  	}
   281  	if r.InsecureSkipVerify || r.CaCert == "" {
   282  		tlsConfig.InsecureSkipVerify = true
   283  	} else if r.CaCert != "" {
   284  		certPool := x509.NewCertPool()
   285  		if !certPool.AppendCertsFromPEM([]byte(r.CaCert)) {
   286  			return nil, fmt.Errorf("failed to create cert pool")
   287  		}
   288  		tlsConfig.RootCAs = certPool
   289  	}
   290  
   291  	setALPNForHTTP := func() {
   292  		if r.Alpn == nil {
   293  			switch {
   294  			case r.Http3:
   295  				// Do nothing.
   296  			case r.Http2:
   297  				tlsConfig.NextProtos = []string{"h2"}
   298  			default:
   299  				tlsConfig.NextProtos = []string{"http/1.1"}
   300  			}
   301  		}
   302  	}
   303  
   304  	// Per-protocol setup.
   305  	switch c.scheme {
   306  	case scheme.HTTPS:
   307  		// Set SNI value to be same as the request Host
   308  		// For use with SNI routing tests
   309  		if tlsConfig.ServerName == "" {
   310  			tlsConfig.ServerName = c.hostHeader
   311  		}
   312  		setALPNForHTTP()
   313  	case scheme.HTTP:
   314  		if r.Http3 {
   315  			return nil, fmt.Errorf("http3 requires HTTPS")
   316  		}
   317  		setALPNForHTTP()
   318  	}
   319  	return tlsConfig, nil
   320  }
   321  
   322  func newHBONETLSConfig(c *Config) (*tls.Config, error) {
   323  	r := c.Request.Hbone
   324  	if r == nil {
   325  		return nil, nil
   326  	}
   327  	tlsConfig := &tls.Config{
   328  		GetClientCertificate: c.hboneClientConfig,
   329  		MinVersion:           tls.VersionTLS12,
   330  	}
   331  	if r.CaCertFile != "" {
   332  		certData, err := os.ReadFile(r.CaCertFile)
   333  		if err != nil {
   334  			return nil, fmt.Errorf("failed to load client certificate: %v", err)
   335  		}
   336  		r.CaCert = string(certData)
   337  	}
   338  	if r.InsecureSkipVerify || r.CaCert == "" {
   339  		tlsConfig.InsecureSkipVerify = true
   340  	} else if r.CaCert != "" {
   341  		certPool := x509.NewCertPool()
   342  		if !certPool.AppendCertsFromPEM([]byte(r.CaCert)) {
   343  			return nil, fmt.Errorf("failed to create cert pool")
   344  		}
   345  		tlsConfig.RootCAs = certPool
   346  	}
   347  
   348  	return tlsConfig, nil
   349  }
   350  
   351  func checkRedirectFunc(req *proto.ForwardEchoRequest) func(req *http.Request, via []*http.Request) error {
   352  	if req.FollowRedirects {
   353  		return nil
   354  	}
   355  
   356  	return func(req *http.Request, via []*http.Request) error {
   357  		// Disable redirects
   358  		return http.ErrUseLastResponse
   359  	}
   360  }