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 }