feat: auto tls sni
Signed-off-by: Steven Kreitzer <skre@skre.me>
This commit is contained in:
		
							parent
							
								
									ae0b261e56
								
							
						
					
					
						commit
						d91a1be65d
					
				|  | @ -205,6 +205,8 @@ type OriginRequestConfig struct { | ||||||
| 	HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader,omitempty"` | 	HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader,omitempty"` | ||||||
| 	// Hostname on the origin server certificate.
 | 	// Hostname on the origin server certificate.
 | ||||||
| 	OriginServerName *string `yaml:"originServerName" json:"originServerName,omitempty"` | 	OriginServerName *string `yaml:"originServerName" json:"originServerName,omitempty"` | ||||||
|  | 	// Auto configure the Hostname on the origin server certificate.
 | ||||||
|  | 	MatchSNIToHost *bool `yaml:"matchSNItoHost" json:"matchSNItoHost,omitempty"` | ||||||
| 	// Path to the CA for the certificate of your origin.
 | 	// Path to the CA for the certificate of your origin.
 | ||||||
| 	// This option should be used only if your certificate is not signed by Cloudflare.
 | 	// This option should be used only if your certificate is not signed by Cloudflare.
 | ||||||
| 	CAPool *string `yaml:"caPool" json:"caPool,omitempty"` | 	CAPool *string `yaml:"caPool" json:"caPool,omitempty"` | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ const ( | ||||||
| 	ProxyKeepAliveTimeoutFlag     = "proxy-keepalive-timeout" | 	ProxyKeepAliveTimeoutFlag     = "proxy-keepalive-timeout" | ||||||
| 	HTTPHostHeaderFlag            = "http-host-header" | 	HTTPHostHeaderFlag            = "http-host-header" | ||||||
| 	OriginServerNameFlag          = "origin-server-name" | 	OriginServerNameFlag          = "origin-server-name" | ||||||
|  | 	MatchSNIToHostFlag            = "match-sni-to-host" | ||||||
| 	NoTLSVerifyFlag               = "no-tls-verify" | 	NoTLSVerifyFlag               = "no-tls-verify" | ||||||
| 	NoChunkedEncodingFlag         = "no-chunked-encoding" | 	NoChunkedEncodingFlag         = "no-chunked-encoding" | ||||||
| 	ProxyAddressFlag              = "proxy-address" | 	ProxyAddressFlag              = "proxy-address" | ||||||
|  | @ -118,6 +119,7 @@ func originRequestFromSingleRule(c *cli.Context) OriginRequestConfig { | ||||||
| 	var keepAliveTimeout = defaultKeepAliveTimeout | 	var keepAliveTimeout = defaultKeepAliveTimeout | ||||||
| 	var httpHostHeader string | 	var httpHostHeader string | ||||||
| 	var originServerName string | 	var originServerName string | ||||||
|  | 	var matchSNItoHost bool | ||||||
| 	var caPool string | 	var caPool string | ||||||
| 	var noTLSVerify bool | 	var noTLSVerify bool | ||||||
| 	var disableChunkedEncoding bool | 	var disableChunkedEncoding bool | ||||||
|  | @ -150,6 +152,9 @@ func originRequestFromSingleRule(c *cli.Context) OriginRequestConfig { | ||||||
| 	if flag := OriginServerNameFlag; c.IsSet(flag) { | 	if flag := OriginServerNameFlag; c.IsSet(flag) { | ||||||
| 		originServerName = c.String(flag) | 		originServerName = c.String(flag) | ||||||
| 	} | 	} | ||||||
|  | 	if flag := MatchSNIToHostFlag; c.IsSet(flag) { | ||||||
|  | 		matchSNItoHost = c.Bool(flag) | ||||||
|  | 	} | ||||||
| 	if flag := tlsconfig.OriginCAPoolFlag; c.IsSet(flag) { | 	if flag := tlsconfig.OriginCAPoolFlag; c.IsSet(flag) { | ||||||
| 		caPool = c.String(flag) | 		caPool = c.String(flag) | ||||||
| 	} | 	} | ||||||
|  | @ -185,6 +190,7 @@ func originRequestFromSingleRule(c *cli.Context) OriginRequestConfig { | ||||||
| 		KeepAliveTimeout:       keepAliveTimeout, | 		KeepAliveTimeout:       keepAliveTimeout, | ||||||
| 		HTTPHostHeader:         httpHostHeader, | 		HTTPHostHeader:         httpHostHeader, | ||||||
| 		OriginServerName:       originServerName, | 		OriginServerName:       originServerName, | ||||||
|  | 		MatchSNIToHost:         matchSNItoHost, | ||||||
| 		CAPool:                 caPool, | 		CAPool:                 caPool, | ||||||
| 		NoTLSVerify:            noTLSVerify, | 		NoTLSVerify:            noTLSVerify, | ||||||
| 		DisableChunkedEncoding: disableChunkedEncoding, | 		DisableChunkedEncoding: disableChunkedEncoding, | ||||||
|  | @ -229,6 +235,9 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { | ||||||
| 	if c.OriginServerName != nil { | 	if c.OriginServerName != nil { | ||||||
| 		out.OriginServerName = *c.OriginServerName | 		out.OriginServerName = *c.OriginServerName | ||||||
| 	} | 	} | ||||||
|  | 	if c.MatchSNIToHost != nil { | ||||||
|  | 		out.MatchSNIToHost = *c.MatchSNIToHost | ||||||
|  | 	} | ||||||
| 	if c.CAPool != nil { | 	if c.CAPool != nil { | ||||||
| 		out.CAPool = *c.CAPool | 		out.CAPool = *c.CAPool | ||||||
| 	} | 	} | ||||||
|  | @ -287,6 +296,8 @@ type OriginRequestConfig struct { | ||||||
| 	HTTPHostHeader string `yaml:"httpHostHeader" json:"httpHostHeader"` | 	HTTPHostHeader string `yaml:"httpHostHeader" json:"httpHostHeader"` | ||||||
| 	// Hostname on the origin server certificate.
 | 	// Hostname on the origin server certificate.
 | ||||||
| 	OriginServerName string `yaml:"originServerName" json:"originServerName"` | 	OriginServerName string `yaml:"originServerName" json:"originServerName"` | ||||||
|  | 	// Auto configure the Hostname on the origin server certificate.
 | ||||||
|  | 	MatchSNIToHost bool `yaml:"matchSNItoHost" json:"matchSNItoHost"` | ||||||
| 	// Path to the CA for the certificate of your origin.
 | 	// Path to the CA for the certificate of your origin.
 | ||||||
| 	// This option should be used only if your certificate is not signed by Cloudflare.
 | 	// This option should be used only if your certificate is not signed by Cloudflare.
 | ||||||
| 	CAPool string `yaml:"caPool" json:"caPool"` | 	CAPool string `yaml:"caPool" json:"caPool"` | ||||||
|  | @ -362,6 +373,12 @@ func (defaults *OriginRequestConfig) setOriginServerName(overrides config.Origin | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (defaults *OriginRequestConfig) setMatchSNIToHost(overrides config.OriginRequestConfig) { | ||||||
|  | 	if val := overrides.MatchSNIToHost; val != nil { | ||||||
|  | 		defaults.MatchSNIToHost = *val | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (defaults *OriginRequestConfig) setCAPool(overrides config.OriginRequestConfig) { | func (defaults *OriginRequestConfig) setCAPool(overrides config.OriginRequestConfig) { | ||||||
| 	if val := overrides.CAPool; val != nil { | 	if val := overrides.CAPool; val != nil { | ||||||
| 		defaults.CAPool = *val | 		defaults.CAPool = *val | ||||||
|  | @ -447,6 +464,7 @@ func setConfig(defaults OriginRequestConfig, overrides config.OriginRequestConfi | ||||||
| 	cfg.setTCPKeepAlive(overrides) | 	cfg.setTCPKeepAlive(overrides) | ||||||
| 	cfg.setHTTPHostHeader(overrides) | 	cfg.setHTTPHostHeader(overrides) | ||||||
| 	cfg.setOriginServerName(overrides) | 	cfg.setOriginServerName(overrides) | ||||||
|  | 	cfg.setMatchSNIToHost(overrides) | ||||||
| 	cfg.setCAPool(overrides) | 	cfg.setCAPool(overrides) | ||||||
| 	cfg.setNoTLSVerify(overrides) | 	cfg.setNoTLSVerify(overrides) | ||||||
| 	cfg.setDisableChunkedEncoding(overrides) | 	cfg.setDisableChunkedEncoding(overrides) | ||||||
|  | @ -501,6 +519,7 @@ func ConvertToRawOriginConfig(c OriginRequestConfig) config.OriginRequestConfig | ||||||
| 		KeepAliveTimeout:       keepAliveTimeout, | 		KeepAliveTimeout:       keepAliveTimeout, | ||||||
| 		HTTPHostHeader:         emptyStringToNil(c.HTTPHostHeader), | 		HTTPHostHeader:         emptyStringToNil(c.HTTPHostHeader), | ||||||
| 		OriginServerName:       emptyStringToNil(c.OriginServerName), | 		OriginServerName:       emptyStringToNil(c.OriginServerName), | ||||||
|  | 		MatchSNIToHost:         defaultBoolToNil(c.MatchSNIToHost), | ||||||
| 		CAPool:                 emptyStringToNil(c.CAPool), | 		CAPool:                 emptyStringToNil(c.CAPool), | ||||||
| 		NoTLSVerify:            defaultBoolToNil(c.NoTLSVerify), | 		NoTLSVerify:            defaultBoolToNil(c.NoTLSVerify), | ||||||
| 		DisableChunkedEncoding: defaultBoolToNil(c.DisableChunkedEncoding), | 		DisableChunkedEncoding: defaultBoolToNil(c.DisableChunkedEncoding), | ||||||
|  |  | ||||||
|  | @ -2,7 +2,9 @@ package ingress | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"crypto/tls" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -46,9 +48,28 @@ func (o *httpService) RoundTrip(req *http.Request) (*http.Response, error) { | ||||||
| 		req.Header.Set("X-Forwarded-Host", req.Host) | 		req.Header.Set("X-Forwarded-Host", req.Host) | ||||||
| 		req.Host = o.hostHeader | 		req.Host = o.hostHeader | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if o.matchSNIToHost { | ||||||
|  | 		o.SetOriginServerName(req) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return o.transport.RoundTrip(req) | 	return o.transport.RoundTrip(req) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (o *httpService) SetOriginServerName(req *http.Request) { | ||||||
|  | 	o.transport.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { | ||||||
|  | 		conn, err := o.transport.DialContext(ctx, network, addr) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		return tls.Client(conn, &tls.Config{ | ||||||
|  | 			RootCAs:            o.transport.TLSClientConfig.RootCAs, | ||||||
|  | 			InsecureSkipVerify: o.transport.TLSClientConfig.InsecureSkipVerify, | ||||||
|  | 			ServerName:         req.Host, | ||||||
|  | 		}), nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (o *statusCode) RoundTrip(_ *http.Request) (*http.Response, error) { | func (o *statusCode) RoundTrip(_ *http.Request) (*http.Response, error) { | ||||||
| 	if o.defaultResp { | 	if o.defaultResp { | ||||||
| 		o.log.Warn().Msgf(ErrNoIngressRulesCLI.Error()) | 		o.log.Warn().Msgf(ErrNoIngressRulesCLI.Error()) | ||||||
|  |  | ||||||
|  | @ -68,9 +68,10 @@ func (o unixSocketPath) MarshalJSON() ([]byte, error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type httpService struct { | type httpService struct { | ||||||
| 	url        *url.URL | 	url            *url.URL | ||||||
| 	hostHeader string | 	hostHeader     string | ||||||
| 	transport  *http.Transport | 	transport      *http.Transport | ||||||
|  | 	matchSNIToHost bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (o *httpService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error { | func (o *httpService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error { | ||||||
|  | @ -80,6 +81,7 @@ func (o *httpService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRe | ||||||
| 	} | 	} | ||||||
| 	o.hostHeader = cfg.HTTPHostHeader | 	o.hostHeader = cfg.HTTPHostHeader | ||||||
| 	o.transport = transport | 	o.transport = transport | ||||||
|  | 	o.matchSNIToHost = cfg.MatchSNIToHost | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -204,25 +204,25 @@ func TestMarshalJSON(t *testing.T) { | ||||||
| 		{ | 		{ | ||||||
| 			name:     "Nil", | 			name:     "Nil", | ||||||
| 			path:     nil, | 			path:     nil, | ||||||
| 			expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | 			expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | ||||||
| 			want:     true, | 			want:     true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:     "Nil regex", | 			name:     "Nil regex", | ||||||
| 			path:     &Regexp{Regexp: nil}, | 			path:     &Regexp{Regexp: nil}, | ||||||
| 			expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | 			expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | ||||||
| 			want:     true, | 			want:     true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:     "Empty", | 			name:     "Empty", | ||||||
| 			path:     &Regexp{Regexp: regexp.MustCompile("")}, | 			path:     &Regexp{Regexp: regexp.MustCompile("")}, | ||||||
| 			expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | 			expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | ||||||
| 			want:     true, | 			want:     true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:     "Basic", | 			name:     "Basic", | ||||||
| 			path:     &Regexp{Regexp: regexp.MustCompile("/echo")}, | 			path:     &Regexp{Regexp: regexp.MustCompile("/echo")}, | ||||||
| 			expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | 			expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, | ||||||
| 			want:     true, | 			want:     true, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue