-
Notifications
You must be signed in to change notification settings - Fork 6
Improve proxy logging and add TLS-specific testing #180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| inbound: | ||
| name: "inbound-server" | ||
| server: | ||
| # No TLS here because the mux connection is already mTLS | ||
| type: "mux" | ||
| mux: "muxed" | ||
| client: | ||
| tcp: | ||
| # Frontend of local cluster | ||
| serverAddress: "local-frontend:7233" | ||
| tls: | ||
| # Certificate that identifies this host to local Temporal frontend | ||
| certificatePath: "/tls/internode/tls.crt" | ||
| # Key this host will use to encrypt local communication to local Temporal frontend | ||
| keyPath: "/tls/internode/tls.key" | ||
| # CA for the local Temporal frontend | ||
| serverCAPath: "/tls/internode/ca.crt" | ||
| # Servername that should be stamped on the local Temporal frontend's cert | ||
| serverName: "frontend.temporal.svc.cluster.local" | ||
| aclPolicy: | ||
| allowedMethods: | ||
| adminService: | ||
| - AddOrUpdateRemoteCluster | ||
| - RemoveRemoteCluster | ||
| - DescribeCluster | ||
| - DescribeMutableState | ||
| - GetNamespaceReplicationMessages | ||
| - GetWorkflowExecutionRawHistoryV2 | ||
| - ListClusters | ||
| - StreamWorkflowReplicationMessages | ||
| - ReapplyEvents | ||
| - GetNamespace # for EagerGetNamespace | ||
| outbound: | ||
| name: "outbound-server" | ||
| server: | ||
| tcp: | ||
| listenAddress: "0.0.0.0:9233" | ||
| tls: | ||
| # Certificate that identifies this host to *local* Temporal cluster | ||
| certificatePath: "/tls/internode/tls.crt" | ||
| # Key this host will use to encrypt local communication to *local* Temporal cluster | ||
| keyPath: "/tls/internode/tls.key" | ||
| # CA for *local* Temporal cluster | ||
| clientCAPath: "/tls/internode/ca.crt" | ||
| # We trust our local cluster, we don't need to verify its cert | ||
| requireClientAuth: false | ||
| client: | ||
| # No TLS here because the mux connection is already mTLS | ||
| type: "mux" | ||
| mux: "muxed" | ||
| mux: | ||
| - name: "muxed" | ||
| mode: "client" | ||
| num_connections: 10 | ||
| client: | ||
| serverAddress: "remote-s2s-proxy-endpoint:8233" | ||
| tls: | ||
| # Certificate that identifies this host to the remote s2s proxy | ||
| certificatePath: "/s2c-client-tls/tls.crt" | ||
| # Key for encrypting the mux tunnel to the remote s2s proxy | ||
| keyPath: "/s2c-client-tls/tls.key" | ||
| # CA for the remote s2s proxy | ||
| serverCAPath: "/s2c-server-tls/tls.crt" | ||
| healthCheck: | ||
| protocol: "http" | ||
| listenAddress: "0.0.0.0:8234" | ||
| namespaceNameTranslation: | ||
| mappings: | ||
| - localName: "mg-hybrid" | ||
| remoteName: "mg-hybrid" | ||
| metrics: | ||
| prometheus: | ||
| listenAddress: "0.0.0.0:9090" | ||
| # This enables profiling with the default config and port | ||
| profiling: {} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,12 +17,18 @@ import ( | |
| ) | ||
|
|
||
| type ( | ||
| // TLSConfig sets TLS options for the proxy's clients and servers | ||
| TLSConfig struct { | ||
| CertificatePath string `yaml:"certificatePath"` | ||
| KeyPath string `yaml:"keyPath"` | ||
| RemoteCAPath string `yaml:"remoteCAPath"` | ||
| CAServerName string `yaml:"caServerName"` | ||
| ValidateClientCA bool `yaml:"validateClientCA"` | ||
| // CertificatePath is the path to the TLS cert that identifies this host | ||
| CertificatePath string `yaml:"certificatePath"` | ||
| // KeyPath is the path to the TLS key used to encrypt traffic | ||
| KeyPath string `yaml:"keyPath"` | ||
| // RemoteCAPath is the path to the TLS CA cert that is used to verify the remote host's certificate | ||
| RemoteCAPath string `yaml:"remoteCAPath"` | ||
| // CAServerName must match against the remote host's CA cert | ||
| CAServerName string `yaml:"caServerName"` | ||
| // If set to false, VerifyCA will skip the CA authentication step | ||
| VerifyCA bool `yaml:"verifyCA"` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. default would be false, should we make the default to be always Verify?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kept this matching the previous, but yeah setting default-verify does seem a lot safer |
||
| } | ||
|
|
||
| HttpGetter interface { | ||
|
|
@@ -38,102 +44,94 @@ var netClient HttpGetter = &http.Client{ | |
| Timeout: time.Second * 10, | ||
| } | ||
|
|
||
| func GetServerTLSConfig(serverConfig TLSConfig, logger log.Logger) (*tls.Config, error) { | ||
| certPath := serverConfig.CertificatePath | ||
| keyPath := serverConfig.KeyPath | ||
| clientCAPath := serverConfig.RemoteCAPath | ||
|
|
||
| func GetServerTLSConfig(serverConfig TLSConfig, logger log.Logger) (tlsConfig *tls.Config, err error) { | ||
| if !serverConfig.IsEnabled() { | ||
| return nil, nil | ||
| return | ||
| } | ||
|
|
||
| var serverCert *tls.Certificate | ||
| var clientCAPool *x509.CertPool | ||
|
|
||
| clientAuthType := tls.NoClientCert | ||
| if serverConfig.ValidateClientCA { | ||
| clientAuthType = tls.RequireAndVerifyClientCert | ||
| caCertPool, err := fetchCACert(clientCAPath) | ||
| tlsConfig = auth.NewEmptyTLSConfig() | ||
| if serverConfig.VerifyCA { | ||
| tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert | ||
| tlsConfig.ClientCAs, err = fetchCACert(serverConfig.RemoteCAPath) | ||
| if err != nil { | ||
| return nil, err | ||
| return nil, fmt.Errorf("failed to read CACert from %s: %w", serverConfig.RemoteCAPath, err) | ||
| } | ||
| clientCAPool = caCertPool | ||
| } else { | ||
| tlsConfig.ClientAuth = tls.NoClientCert | ||
| } | ||
|
|
||
| if certPath != "" { | ||
| cert, err := tls.LoadX509KeyPair(certPath, keyPath) | ||
| if serverConfig.CertificatePath != "" { | ||
| serverCert, err := tls.LoadX509KeyPair(serverConfig.CertificatePath, serverConfig.KeyPath) | ||
| if err != nil { | ||
| return nil, err | ||
| return nil, fmt.Errorf("failed to load cert-key pair (%s, %s): %w", | ||
| serverConfig.CertificatePath, serverConfig.KeyPath, err) | ||
| } | ||
| serverCert = &cert | ||
| tlsConfig.Certificates = []tls.Certificate{serverCert} | ||
| } | ||
|
|
||
| c := auth.NewEmptyTLSConfig() | ||
| c.ClientAuth = clientAuthType | ||
| c.Certificates = []tls.Certificate{*serverCert} | ||
| c.ClientCAs = clientCAPool | ||
| c.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) { | ||
| logger.Info("Received TLS handshake", tag.Address(hello.Conn.RemoteAddr().String())) | ||
| tlsConfig.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) { | ||
| logger.Info("Received TLS handshake", tag.Address(hello.Conn.RemoteAddr().String()), tag.ServerName(hello.ServerName)) | ||
| return nil, nil | ||
| } | ||
|
|
||
| c.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { | ||
| tlsConfig.GetClientCertificate = func(clientInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { | ||
| var allCertFailures error | ||
| for _, cert := range tlsConfig.Certificates { | ||
| certErr := clientInfo.SupportsCertificate(&cert) | ||
| if certErr == nil { | ||
| return &cert, nil | ||
| } | ||
| allCertFailures = errors.Join(allCertFailures, certErr) | ||
| } | ||
| logger.Warn("Could not match cert request. Check cert failures in error tag", tag.Error(allCertFailures)) | ||
| return nil, allCertFailures | ||
| } | ||
|
|
||
| tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { | ||
| if len(rawCerts) == 0 { | ||
| logger.Info("No client certificate provided") | ||
| logger.Info("No client certificate provided, so no verification performed") | ||
| } else { | ||
| cert, _ := x509.ParseCertificate(rawCerts[0]) | ||
| logger.Info(fmt.Sprintf("Client certificate subject: %s", cert.Subject)) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| return c, nil | ||
| return tlsConfig, nil | ||
| } | ||
|
|
||
| func GetClientTLSConfig(clientConfig TLSConfig) (*tls.Config, error) { | ||
| certPath := clientConfig.CertificatePath | ||
| keyPath := clientConfig.KeyPath | ||
| caPath := clientConfig.RemoteCAPath | ||
| serverName := clientConfig.CAServerName | ||
|
|
||
| func GetClientTLSConfig(clientConfig TLSConfig) (tlsConfig *tls.Config, err error) { | ||
| if !clientConfig.IsEnabled() { | ||
| return nil, nil | ||
| return | ||
| } | ||
|
|
||
| var cert *tls.Certificate | ||
| var caPool *x509.CertPool | ||
|
|
||
| if caPath != "" { | ||
| caCertPool, err := fetchCACert(caPath) | ||
| if err != nil { | ||
| return nil, err | ||
| tlsConfig = auth.NewEmptyTLSConfig() | ||
| if !clientConfig.VerifyCA { | ||
| tlsConfig.InsecureSkipVerify = true | ||
| } else { | ||
| if clientConfig.CAServerName == "" || clientConfig.RemoteCAPath == "" { | ||
| return nil, errors.New("CAServerName and RemoteCAPath must be set when VerifyCA is true") | ||
| } | ||
| caPool = caCertPool | ||
| tlsConfig.ServerName = clientConfig.CAServerName | ||
| } | ||
|
|
||
| if certPath != "" { | ||
| myCert, err := tls.LoadX509KeyPair(certPath, keyPath) | ||
| if clientConfig.RemoteCAPath != "" { | ||
| caCertPool, err := fetchCACert(clientConfig.RemoteCAPath) | ||
| if err != nil { | ||
| return nil, err | ||
| return nil, fmt.Errorf("failed to load CA cert: %w", err) | ||
| } | ||
| cert = &myCert | ||
| tlsConfig.RootCAs = caCertPool | ||
| } | ||
|
|
||
| // If we are given arguments to verify either server or client, configure TLS | ||
| if caPool != nil || cert != nil || serverName != "" { | ||
| enableHostVerification := serverName != "" && caPath != "" | ||
| tlsConfig := auth.NewTLSConfigForServer(serverName, enableHostVerification) | ||
| if caPool != nil { | ||
| tlsConfig.RootCAs = caPool | ||
| } | ||
| if cert != nil { | ||
| tlsConfig.Certificates = []tls.Certificate{*cert} | ||
| if clientConfig.CertificatePath != "" { | ||
| myCert, err := tls.LoadX509KeyPair(clientConfig.CertificatePath, clientConfig.KeyPath) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to load cert-key pair from path %s: %w", clientConfig.CertificatePath, err) | ||
| } | ||
|
|
||
| return tlsConfig, nil | ||
| tlsConfig.Certificates = []tls.Certificate{myCert} | ||
| } | ||
|
|
||
| return nil, nil | ||
| return | ||
| } | ||
|
|
||
| func fetchCACert(pathOrUrl string) (caPool *x509.CertPool, err error) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of calling it old, I prefer adding version. maybe call the new format version 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can rename the file, but I don't want to version the config at this point: All S2S proxies should be deployed for ~1-2months, tops, so we shouldn't be messing around with config versions