From e854cfe83ba694826bb8885aa7b0b2bb5952faa1 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 18 May 2020 16:52:51 -0600 Subject: [PATCH] envoy: implement policy TLS options (#724) * envoy: implement policy TLS options * fix tests * log which CAs are being used --- config/policy.go | 34 ++++-- integration/backends/ws-echo/main.go | 9 +- integration/internal/cluster/certs.go | 2 +- integration/policy_test.go | 16 +-- internal/controlplane/xds.go | 120 ++++++++++++++++++++- internal/controlplane/xds_clusters.go | 136 ++++++++++++++++++------ internal/controlplane/xds_listeners.go | 31 +----- internal/controlplane/xds_routes.go | 2 +- internal/cryptutil/certificates.go | 28 ----- internal/cryptutil/certificates_test.go | 41 ------- 10 files changed, 258 insertions(+), 161 deletions(-) diff --git a/config/policy.go b/config/policy.go index 6f23cd3e8..9d4fb3cf4 100644 --- a/config/policy.go +++ b/config/policy.go @@ -2,12 +2,16 @@ package config import ( "crypto/tls" - "crypto/x509" + "encoding/base64" "encoding/json" "fmt" "net/url" + "os" "time" + "github.com/cespare/xxhash/v2" + "github.com/mitchellh/hashstructure" + "github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/urlutil" ) @@ -21,8 +25,8 @@ type Policy struct { AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty" json:"allowed_groups,omitempty"` AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"` - Source *StringURL `yaml:",omitempty" json:"source,omitempty"` - Destination *url.URL `yaml:",omitempty" json:"destination,omitempty"` + Source *StringURL `yaml:",omitempty" json:"source,omitempty" hash:"ignore"` + Destination *url.URL `yaml:",omitempty" json:"destination,omitempty" hash:"ignore"` // Additional route matching options Prefix string `mapstructure:"prefix" yaml:"prefix,omitempty" json:"prefix,omitempty"` @@ -60,9 +64,8 @@ type Policy struct { // TLSCustomCA defines the root certificate to use with a given // route when verifying server certificates. - TLSCustomCA string `mapstructure:"tls_custom_ca" yaml:"tls_custom_ca,omitempty"` - TLSCustomCAFile string `mapstructure:"tls_custom_ca_file" yaml:"tls_custom_ca_file,omitempty"` - RootCAs *x509.CertPool `yaml:",omitempty"` + TLSCustomCA string `mapstructure:"tls_custom_ca" yaml:"tls_custom_ca,omitempty"` + TLSCustomCAFile string `mapstructure:"tls_custom_ca_file" yaml:"tls_custom_ca_file,omitempty"` // Contains the x.509 client certificate to present to the downstream // host. @@ -70,7 +73,7 @@ type Policy struct { TLSClientKey string `mapstructure:"tls_client_key" yaml:"tls_client_key,omitempty"` TLSClientCertFile string `mapstructure:"tls_client_cert_file" yaml:"tls_client_cert_file,omitempty"` TLSClientKeyFile string `mapstructure:"tls_client_key_file" yaml:"tls_client_key_file,omitempty"` - ClientCertificate *tls.Certificate `yaml:",omitempty"` + ClientCertificate *tls.Certificate `yaml:",omitempty" hash:"ignore"` // SetRequestHeaders adds a collection of headers to the downstream request // in the form of key value pairs. Note bene, this will overwrite the @@ -127,19 +130,28 @@ func (p *Policy) Validate() error { } if p.TLSCustomCA != "" { - p.RootCAs, err = cryptutil.CertPoolFromBase64(p.TLSCustomCA) + _, err := base64.StdEncoding.DecodeString(p.TLSCustomCA) if err != nil { - return fmt.Errorf("config: couldn't decode custom ca %w", err) + return fmt.Errorf("config: couldn't decode custom ca: %w", err) } } else if p.TLSCustomCAFile != "" { - p.RootCAs, err = cryptutil.CertPoolFromFile(p.TLSCustomCAFile) + _, err := os.Stat(p.TLSCustomCAFile) if err != nil { - return fmt.Errorf("config: couldn't load custom ca file %w", err) + return fmt.Errorf("config: couldn't load client ca file: %w", err) } } + return nil } +// Checksum returns the xxhash hash for the policy. +func (p *Policy) Checksum() uint64 { + cs, _ := hashstructure.Hash(p, &hashstructure.HashOptions{ + Hasher: xxhash.New(), + }) + return cs +} + func (p *Policy) String() string { if p.Source == nil || p.Destination == nil { return fmt.Sprintf("%s → %s", p.From, p.To) diff --git a/integration/backends/ws-echo/main.go b/integration/backends/ws-echo/main.go index b42f6505d..5b377981f 100644 --- a/integration/backends/ws-echo/main.go +++ b/integration/backends/ws-echo/main.go @@ -34,15 +34,20 @@ func main() { err = http.ListenAndServe(bindAddr, http.HandlerFunc(handle)) } if err != nil { - fmt.Fprintf(os.Stderr, "failed to listen and serve: %v", err) + fmt.Fprintf(os.Stderr, "failed to listen and serve: %v\n", err) os.Exit(1) } } func handle(w http.ResponseWriter, r *http.Request) { - conn, err := websocket.Upgrade(w, r, nil, 1024, 1024) + conn, err := (&websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + }).Upgrade(w, r, nil) if err != nil { + fmt.Fprintf(os.Stderr, "error upgrading websocket connection: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) + return } defer conn.Close() diff --git a/integration/internal/cluster/certs.go b/integration/internal/cluster/certs.go index a42b286b0..5b192fe3c 100644 --- a/integration/internal/cluster/certs.go +++ b/integration/internal/cluster/certs.go @@ -43,7 +43,7 @@ func bootstrapCerts(ctx context.Context) (*TLSCertsBundle, error) { name string }{ {&bundle.Trusted, filepath.Join(wd, "trusted"), true, "*.localhost.pomerium.io"}, - {&bundle.WronglyNamed, filepath.Join(wd, "wrongly-named"), true, "*.localhost.notpomerium.io"}, + {&bundle.WronglyNamed, filepath.Join(wd, "trusted"), true, "*.localhost.notpomerium.io"}, {&bundle.Untrusted, filepath.Join(wd, "untrusted"), false, "*.localhost.pomerium.io"}, } diff --git a/integration/policy_test.go b/integration/policy_test.go index 3ae65b8a4..244de29f4 100644 --- a/integration/policy_test.go +++ b/integration/policy_test.go @@ -185,8 +185,6 @@ func TestWebsocket(t *testing.T) { } func TestTLSSkipVerify(t *testing.T) { - t.SkipNow() - ctx := mainCtx ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30) defer clearTimeout() @@ -221,13 +219,11 @@ func TestTLSSkipVerify(t *testing.T) { } defer res.Body.Close() - assert.Equal(t, http.StatusBadGateway, res.StatusCode) + assert.Contains(t, []int{http.StatusBadGateway, http.StatusServiceUnavailable}, res.StatusCode) }) } func TestTLSServerName(t *testing.T) { - t.SkipNow() - ctx := mainCtx ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30) defer clearTimeout() @@ -262,13 +258,11 @@ func TestTLSServerName(t *testing.T) { } defer res.Body.Close() - assert.Equal(t, http.StatusBadGateway, res.StatusCode) + assert.Contains(t, []int{http.StatusBadGateway, http.StatusServiceUnavailable}, res.StatusCode) }) } func TestTLSCustomCA(t *testing.T) { - t.SkipNow() - ctx := mainCtx ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30) defer clearTimeout() @@ -303,13 +297,11 @@ func TestTLSCustomCA(t *testing.T) { } defer res.Body.Close() - assert.Equal(t, http.StatusBadGateway, res.StatusCode) + assert.Contains(t, []int{http.StatusBadGateway, http.StatusServiceUnavailable}, res.StatusCode) }) } func TestTLSClientCert(t *testing.T) { - t.SkipNow() - ctx := mainCtx ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30) defer clearTimeout() @@ -343,7 +335,7 @@ func TestTLSClientCert(t *testing.T) { } defer res.Body.Close() - assert.Equal(t, http.StatusBadGateway, res.StatusCode) + assert.Contains(t, []int{http.StatusBadGateway, http.StatusServiceUnavailable}, res.StatusCode) }) } diff --git a/internal/controlplane/xds.go b/internal/controlplane/xds.go index 98956e1be..d88b4c934 100644 --- a/internal/controlplane/xds.go +++ b/internal/controlplane/xds.go @@ -1,20 +1,30 @@ package controlplane import ( + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/pem" "fmt" + "io/ioutil" "net" + "os" + "path/filepath" "strconv" - "github.com/pomerium/pomerium/config" - + xxhash "github.com/cespare/xxhash/v2" envoy_config_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_extensions_access_loggers_grpc_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" + envoy_extensions_transport_sockets_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" envoy_service_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/any" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/pomerium/pomerium/config" + "github.com/pomerium/pomerium/internal/log" ) func (srv *Server) buildDiscoveryResponse(version string, typeURL string, options *config.Options) (*envoy_service_discovery_v3.DiscoveryResponse, error) { @@ -117,3 +127,109 @@ func inlineBytes(bs []byte) *envoy_config_core_v3.DataSource { }, } } + +func inlineBytesAsFilename(name string, bs []byte) *envoy_config_core_v3.DataSource { + ext := filepath.Ext(name) + name = fmt.Sprintf("%s-%x%s", name[:len(ext)], xxhash.Sum64(bs), ext) + + cacheDir, err := os.UserCacheDir() + if err != nil { + cacheDir = filepath.Join(os.TempDir()) + } + cacheDir = filepath.Join(cacheDir, "pomerium", "envoy", "files") + if err = os.MkdirAll(cacheDir, 0755); err != nil { + log.Error().Err(err).Msg("error creating cache directory, falling back to inline bytes") + return inlineBytes(bs) + } + + fp := filepath.Join(cacheDir, name) + if _, err = os.Stat(fp); os.IsNotExist(err) { + err = ioutil.WriteFile(fp, bs, 0600) + if err != nil { + log.Error().Err(err).Msg("error writing cache file, falling back to inline bytes") + return inlineBytes(bs) + } + } else if err != nil { + log.Error().Err(err).Msg("error reading cache file, falling back to inline bytes") + return inlineBytes(bs) + } + + return inlineFilename(fp) +} + +func inlineFilename(name string) *envoy_config_core_v3.DataSource { + return &envoy_config_core_v3.DataSource{ + Specifier: &envoy_config_core_v3.DataSource_Filename{ + Filename: name, + }, + } +} + +func getPolicyName(policy *config.Policy) string { + return fmt.Sprintf("policy-%x", policy.Checksum()) +} + +func envoyTLSCertificateFromGoTLSCertificate(cert *tls.Certificate) *envoy_extensions_transport_sockets_tls_v3.TlsCertificate { + envoyCert := &envoy_extensions_transport_sockets_tls_v3.TlsCertificate{} + var chain bytes.Buffer + for _, cbs := range cert.Certificate { + _ = pem.Encode(&chain, &pem.Block{ + Type: "CERTIFICATE", + Bytes: cbs, + }) + break + } + envoyCert.CertificateChain = inlineBytesAsFilename("tls-crt.pem", chain.Bytes()) + if cert.OCSPStaple != nil { + envoyCert.OcspStaple = inlineBytes(cert.OCSPStaple) + } + if bs, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey); err == nil { + envoyCert.PrivateKey = inlineBytesAsFilename("tls-key.pem", pem.EncodeToMemory( + &pem.Block{ + Type: "PRIVATE KEY", + Bytes: bs, + }, + )) + } else { + log.Warn().Err(err).Msg("failed to marshal private key for tls config") + } + for _, scts := range cert.SignedCertificateTimestamps { + envoyCert.SignedCertificateTimestamp = append(envoyCert.SignedCertificateTimestamp, + inlineBytes(scts)) + } + return envoyCert +} + +var rootCABundle string + +func init() { + // from https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ssl#arch-overview-ssl-enabling-verification + knownRootLocations := []string{ + "/etc/ssl/certs/ca-certificates.crt", + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", + "/etc/pki/tls/certs/ca-bundle.crt", + "/etc/ssl/ca-bundle.pem", + "/usr/local/etc/ssl/cert.pem", + "/etc/ssl/cert.pem", + } + for _, path := range knownRootLocations { + if _, err := os.Stat(path); err == nil { + rootCABundle = path + break + } + } + if rootCABundle == "" { + log.Error().Strs("known-locations", knownRootLocations). + Msgf("no root certificates were found in any of the known locations") + } else { + + log.Info().Msgf("using %s as the system root certificate authority bundle", rootCABundle) + } +} + +func getRootCertificateAuthority() (string, error) { + if rootCABundle == "" { + return "", fmt.Errorf("root certificates not found") + } + return rootCABundle, nil +} diff --git a/internal/controlplane/xds_clusters.go b/internal/controlplane/xds_clusters.go index 94fbc8a66..ed651d5ed 100644 --- a/internal/controlplane/xds_clusters.go +++ b/internal/controlplane/xds_clusters.go @@ -1,23 +1,26 @@ package controlplane import ( + "encoding/base64" "net" "net/url" - "strings" "time" envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_config_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + envoy_extensions_transport_sockets_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + envoy_type_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "github.com/golang/protobuf/ptypes" "github.com/pomerium/pomerium/config" + "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/urlutil" ) func (srv *Server) buildClusters(options *config.Options) []*envoy_config_cluster_v3.Cluster { grpcURL := &url.URL{ - Scheme: "grpc", + Scheme: "http", Host: srv.GRPCListener.Addr().String(), } httpURL := &url.URL{ @@ -25,46 +28,116 @@ func (srv *Server) buildClusters(options *config.Options) []*envoy_config_cluste Host: srv.HTTPListener.Addr().String(), } authzURL := &url.URL{ - Scheme: strings.Replace(options.AuthorizeURL.Scheme, "http", "grpc", -1), + Scheme: options.AuthorizeURL.Scheme, Host: options.AuthorizeURL.Host, } clusters := []*envoy_config_cluster_v3.Cluster{ - srv.buildCluster("pomerium-control-plane-grpc", grpcURL), - srv.buildCluster("pomerium-control-plane-http", httpURL), - srv.buildCluster("pomerium-authz", authzURL), + srv.buildInternalCluster("pomerium-control-plane-grpc", grpcURL, true), + srv.buildInternalCluster("pomerium-control-plane-http", httpURL, false), + srv.buildInternalCluster("pomerium-authz", authzURL, true), } if config.IsProxy(options.Services) { - type clusterDestination struct { - name, scheme, hostport string - } - clusterDestinations := map[clusterDestination]struct{}{} for _, policy := range options.Policies { - name, scheme, hostport := srv.getClusterDetails(policy.Destination) - clusterDestinations[clusterDestination{name, scheme, hostport}] = struct{}{} - } - - for dst := range clusterDestinations { - name, scheme, hostport := dst.name, dst.scheme, dst.hostport - clusters = append(clusters, srv.buildCluster(name, &url.URL{ - Scheme: scheme, - Host: hostport, - })) + clusters = append(clusters, srv.buildPolicyCluster(&policy)) } } return clusters } -func (srv *Server) getClusterDetails(endpoint *url.URL) (name, scheme, hostport string) { - name = endpoint.Scheme + "-" + strings.Replace(endpoint.Host, ":", "--", -1) - return name, endpoint.Scheme, endpoint.Host +func (srv *Server) buildInternalCluster(name string, endpoint *url.URL, forceHTTP2 bool) *envoy_config_cluster_v3.Cluster { + var transportSocket *envoy_config_core_v3.TransportSocket + if endpoint.Scheme == "https" { + transportSocket = &envoy_config_core_v3.TransportSocket{ + Name: "tls", + } + } + return srv.buildCluster(name, endpoint, transportSocket, forceHTTP2) } -func (srv *Server) buildCluster(name string, endpoint *url.URL) *envoy_config_cluster_v3.Cluster { +func (srv *Server) buildPolicyCluster(policy *config.Policy) *envoy_config_cluster_v3.Cluster { + name := getPolicyName(policy) + return srv.buildCluster(name, policy.Destination, srv.buildPolicyTransportSocket(policy), false) +} + +func (srv *Server) buildPolicyTransportSocket(policy *config.Policy) *envoy_config_core_v3.TransportSocket { + if policy.Destination.Scheme != "https" { + return nil + } + + sni := policy.Destination.Hostname() + if policy.TLSServerName != "" { + sni = policy.TLSServerName + } + tlsContext := &envoy_extensions_transport_sockets_tls_v3.UpstreamTlsContext{ + CommonTlsContext: &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext{ + AlpnProtocols: []string{"http/1.1"}, + ValidationContextType: &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext_ValidationContext{ + ValidationContext: srv.buildPolicyValidationContext(policy), + }, + }, + Sni: sni, + } + if policy.ClientCertificate != nil { + tlsContext.CommonTlsContext.TlsCertificates = append(tlsContext.CommonTlsContext.TlsCertificates, + envoyTLSCertificateFromGoTLSCertificate(policy.ClientCertificate)) + } + + tlsConfig, _ := ptypes.MarshalAny(tlsContext) + return &envoy_config_core_v3.TransportSocket{ + Name: "tls", + ConfigType: &envoy_config_core_v3.TransportSocket_TypedConfig{ + TypedConfig: tlsConfig, + }, + } +} + +func (srv *Server) buildPolicyValidationContext(policy *config.Policy) *envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext { + sni := policy.Destination.Hostname() + if policy.TLSServerName != "" { + sni = policy.TLSServerName + } + validationContext := &envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext{ + MatchSubjectAltNames: []*envoy_type_matcher_v3.StringMatcher{{ + MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{ + Exact: sni, + }, + }}, + } + if policy.TLSCustomCAFile != "" { + validationContext.TrustedCa = inlineFilename(policy.TLSCustomCAFile) + } else if policy.TLSCustomCA != "" { + bs, err := base64.StdEncoding.DecodeString(policy.TLSCustomCA) + if err != nil { + log.Error().Err(err).Msg("invalid custom CA certificate") + } + validationContext.TrustedCa = inlineBytesAsFilename("custom-ca.pem", bs) + } else { + rootCA, err := getRootCertificateAuthority() + if err != nil { + log.Error().Err(err).Msg("unable to enable certificate verification because no root CAs were found") + } else { + validationContext.TrustedCa = inlineFilename(rootCA) + } + } + + if policy.TLSSkipVerify { + validationContext.TrustChainVerification = envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext_ACCEPT_UNTRUSTED + } + + return validationContext +} + +func (srv *Server) buildCluster( + name string, + endpoint *url.URL, + transportSocket *envoy_config_core_v3.TransportSocket, + forceHTTP2 bool, +) *envoy_config_cluster_v3.Cluster { defaultPort := 80 - if endpoint.Scheme == "https" || endpoint.Scheme == "grpcs" { + if transportSocket != nil && transportSocket.Name == "tls" { defaultPort = 443 } @@ -83,16 +156,13 @@ func (srv *Server) buildCluster(name string, endpoint *url.URL) *envoy_config_cl }}, }}, }, - RespectDnsTtl: true, + RespectDnsTtl: true, + TransportSocket: transportSocket, } - if endpoint.Scheme == "grpc" { - cluster.Http2ProtocolOptions = &envoy_config_core_v3.Http2ProtocolOptions{} - } - - if endpoint.Scheme == "https" || endpoint.Scheme == "grpcs" { - cluster.TransportSocket = &envoy_config_core_v3.TransportSocket{ - Name: "tls", + if forceHTTP2 { + cluster.Http2ProtocolOptions = &envoy_config_core_v3.Http2ProtocolOptions{ + AllowConnect: true, } } diff --git a/internal/controlplane/xds_listeners.go b/internal/controlplane/xds_listeners.go index c8ee1a619..62d627984 100644 --- a/internal/controlplane/xds_listeners.go +++ b/internal/controlplane/xds_listeners.go @@ -1,9 +1,6 @@ package controlplane import ( - "bytes" - "crypto/x509" - "encoding/pem" "sort" "time" @@ -309,33 +306,7 @@ func (srv *Server) buildDownstreamTLSContext(options *config.Options, domain str return nil } - envoyCert := &envoy_extensions_transport_sockets_tls_v3.TlsCertificate{} - var chain bytes.Buffer - for _, cbs := range cert.Certificate { - _ = pem.Encode(&chain, &pem.Block{ - Type: "CERTIFICATE", - Bytes: cbs, - }) - } - envoyCert.CertificateChain = inlineBytes(chain.Bytes()) - if cert.OCSPStaple != nil { - envoyCert.OcspStaple = inlineBytes(cert.OCSPStaple) - } - if bs, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey); err == nil { - envoyCert.PrivateKey = inlineBytes(pem.EncodeToMemory( - &pem.Block{ - Type: "PRIVATE KEY", - Bytes: bs, - }, - )) - } else { - log.Warn().Err(err).Msg("failed to marshal private key for tls config") - } - for _, scts := range cert.SignedCertificateTimestamps { - envoyCert.SignedCertificateTimestamp = append(envoyCert.SignedCertificateTimestamp, - inlineBytes(scts)) - } - + envoyCert := envoyTLSCertificateFromGoTLSCertificate(cert) return &envoy_extensions_transport_sockets_tls_v3.DownstreamTlsContext{ CommonTlsContext: &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext{ TlsCertificates: []*envoy_extensions_transport_sockets_tls_v3.TlsCertificate{envoyCert}, diff --git a/internal/controlplane/xds_routes.go b/internal/controlplane/xds_routes.go index 166daf5fe..30c3f6e56 100644 --- a/internal/controlplane/xds_routes.go +++ b/internal/controlplane/xds_routes.go @@ -121,7 +121,7 @@ func (srv *Server) buildPolicyRoutes(options *config.Options, domain string) []* match.PathSpecifier = &envoy_config_route_v3.RouteMatch_Prefix{Prefix: "/"} } - clusterName, _, _ := srv.getClusterDetails(policy.Destination) + clusterName := getPolicyName(&policy) var requestHeadersToAdd []*envoy_config_core_v3.HeaderValueOption for k, v := range policy.SetRequestHeaders { diff --git a/internal/cryptutil/certificates.go b/internal/cryptutil/certificates.go index b682c5857..5c098f09f 100644 --- a/internal/cryptutil/certificates.go +++ b/internal/cryptutil/certificates.go @@ -11,7 +11,6 @@ import ( "encoding/pem" "errors" "fmt" - "io/ioutil" "math/big" "net" "time" @@ -38,33 +37,6 @@ func CertificateFromFile(certFile, keyFile string) (*tls.Certificate, error) { return &cert, err } -// CertPoolFromBase64 takes a base64 encoded string and returns a new -// X509 certificate pool. -func CertPoolFromBase64(encPemCerts string) (*x509.CertPool, error) { - b, err := base64.StdEncoding.DecodeString(encPemCerts) - if err != nil { - return nil, fmt.Errorf("couldn't decode pem %v: %w", b, err) - } - return bytesToCertPool(b) -} - -// CertPoolFromFile reads a file and returns an X509 certificate pool. -func CertPoolFromFile(pemFile string) (*x509.CertPool, error) { - b, err := ioutil.ReadFile(pemFile) - if err != nil { - return nil, err - } - return bytesToCertPool(b) -} - -func bytesToCertPool(b []byte) (*x509.CertPool, error) { - certPool := x509.NewCertPool() - if ok := certPool.AppendCertsFromPEM(b); !ok { - return nil, fmt.Errorf("could append certs from PEM") - } - return certPool, nil -} - // DecodePublicKey decodes a PEM-encoded ECDSA public key. func DecodePublicKey(encodedKey []byte) (*ecdsa.PublicKey, error) { block, _ := pem.Decode(encodedKey) diff --git a/internal/cryptutil/certificates_test.go b/internal/cryptutil/certificates_test.go index efcb38ec4..ee895190c 100644 --- a/internal/cryptutil/certificates_test.go +++ b/internal/cryptutil/certificates_test.go @@ -64,47 +64,6 @@ func TestCertifcateFromBase64(t *testing.T) { } } -func TestCertPoolFromBase64(t *testing.T) { - tests := []struct { - name string - encPemCerts string - wantErr bool - }{ - {"good", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURlVENDQW1HZ0F3SUJBZ0lKQUszMmhoR0JIcmFtTUEwR0NTcUdTSWIzRFFFQkN3VUFNR0l4Q3pBSkJnTlYKQkFZVEFsVlRNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJZd0ZBWURWUVFIREExVFlXNGdSbkpoYm1OcApjMk52TVE4d0RRWURWUVFLREFaQ1lXUlRVMHd4RlRBVEJnTlZCQU1NRENvdVltRmtjM05zTG1OdmJUQWVGdzB4Ck9UQTJNVEl4TlRNeE5UbGFGdzB5TVRBMk1URXhOVE14TlRsYU1HSXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWUQKVlFRSURBcERZV3hwWm05eWJtbGhNUll3RkFZRFZRUUhEQTFUWVc0Z1JuSmhibU5wYzJOdk1ROHdEUVlEVlFRSwpEQVpDWVdSVFUwd3hGVEFUQmdOVkJBTU1EQ291WW1Ga2MzTnNMbU52YlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCCkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1JRTdQaU03Z1RDczloUTFYQll6Sk1ZNjF5b2FFbXdJclg1bFo2eEt5eDIKUG16QVMyQk1UT3F5dE1BUGdMYXcrWExKaGdMNVhFRmRFeXQvY2NSTHZPbVVMbEEzcG1jY1lZejJRVUxGUnRNVwpoeWVmZE9zS25SRlNKaUZ6YklSTWVWWGswV3ZvQmoxSUZWS3RzeWpicXY5dS8yQ1ZTbmRyT2ZFazBURzIzVTNBCnhQeFR1VzFDcmJWOC9xNzFGZEl6U09jaWNjZkNGSHBzS09vM1N0L3FiTFZ5dEg1YW9oYmNhYkZYUk5zS0VxdmUKd3c5SGRGeEJJdUdhK1J1VDVxMGlCaWt1c2JwSkhBd25ucVA3aS9kQWNnQ3NrZ2paakZlRVU0RUZ5K2IrYTFTWQpRQ2VGeHhDN2MzRHZhUmhCQjBWVmZQbGtQejBzdzZsODY1TWFUSWJSeW9VQ0F3RUFBYU15TURBd0NRWURWUjBUCkJBSXdBREFqQmdOVkhSRUVIREFhZ2d3cUxtSmhaSE56YkM1amIyMkNDbUpoWkhOemJDNWpiMjB3RFFZSktvWkkKaHZjTkFRRUxCUUFEZ2dFQkFJaTV1OXc4bWdUNnBwQ2M3eHNHK0E5ZkkzVzR6K3FTS2FwaHI1bHM3MEdCS2JpWQpZTEpVWVpoUGZXcGgxcXRra1UwTEhGUG04M1ZhNTJlSUhyalhUMFZlNEt0TzFuMElBZkl0RmFXNjJDSmdoR1luCmp6dzByeXpnQzRQeUZwTk1uTnRCcm9QdS9iUGdXaU1nTE9OcEVaaGlneDRROHdmMVkvVTlzK3pDQ3hvSmxhS1IKTVhidVE4N1g3bS85VlJueHhvNk56NVpmN09USFRwTk9JNlZqYTBCeGJtSUFVNnlyaXc5VXJnaWJYZk9qM2o2bgpNVExCdWdVVklCMGJCYWFzSnNBTUsrdzRMQU52YXBlWjBET1NuT1I0S0syNEowT3lvRjVmSG1wNTllTTE3SW9GClFxQmh6cG1RVWd1bmVjRVc4QlRxck5wRzc5UjF1K1YrNHd3Y2tQYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=", false}, - {"bad base64", "!", true}, - {"bad certificate", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURlVENDQW1HZ0F3SUJBZ0lKQUszMmhoR0JIcmFtTUEwR0NTcUdTSWIzRFFFQkN3VUFNR0l4Q3pBSkJnTlYKQkFZVEFsVlRNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJZd0ZBWURWUVFIREExVFlXNGdSbkpoYm1OcApjMk52TVE4d0RRWURWUVFLREFaQ1lXUlRVMHd4RlRBVEJnTlZCQU1NRENvdVltRmtjM05zTG1OdmJUQWVGdzB4Ck9UQTJNVEl4TlRNeE5UbGFGdzB5TVRBMk1URXhOVE14TlRsYU1HSXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWUQKVlFRSURBcERZV3hwWm05eWJtbGhNUll3RkFZRFZRUUhEQTFUWVc0Z1JuSmhibU5wYzJOdk1ROHdEUVlEVlFRSwpEQVpDWVdSVFUwd3hGVEFUQmdOVkJBTU1EQ291WW1Ga2MzTnNMbU52YlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCCkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1JRTdQaU03Z1RDczloUTFYQll6Sk1ZNjF5b2FFbXdJclg1bFo2eEt5eDIKUG16QVMyQk1UT3F5dE1BUGdMYXcrWExKaGdMNVhFRmRFeXQvY2NSTHZPbVVMbEEzcG1jY1lZejJRVUxGUnRNVwpoeWVmZE9zS25SRlNKaUZ6YklSTWVWWGswV3ZvQmoxSUZWS3RzeWpicXY5dS8yQ1ZTbmRyT2ZFazBURzIzVTNBCnhQeFR1VzFDcmJWOC9xNzFGZEl6U09jaWNjZkNGSHBzS09vM1N0L3FiTFZ5dEg1YW9oYmNhYkZYUk5zS0VxdmUKd3c5SGRGeEJJdUdhK1J1VDVxMGlCaWt1c2JwSkhBd25ucVA3aS9kQWNnQ3NrZ2paakZlRVU0RUZ5K2IrYTFTWQpRQ2VGeHhDN2MzRHZhUmhCQjBWVmZQbGtQejBzdzZsODY1TWFUSWJSeW9VQ0F3RUFBYU15TURBd0NRWURWUjBUCkJBSXdBREFqQmdOVkhSRUVIREFhZ2d3cUxtSmhaSE56YkM1amIyMkNDbUpoWkhOemJDNWpiMjB3RFFZSktvWkkKaHZjTkFRRUxCUUFEZ2dFQkFJaTV1OXc4bWdUNnBwQ2M3eHNHK0E5ZkkzVzR6K3FTS2FwaHI1bHM3MEdCS2JpWQpZTEpVWVpoUGZXcGgxcXRra1UwTEhGUG04M1ZhNTJlSUhyalhUMFZlNEt0TzFuMElBZkl0RmFXNjJDSmdoR1luCmp6dzByeXpnQzRQeUZwTk1uTnRCcm9QdS9iUGdXaU1nTE9OcEVaaGlneDRROHdmMVkvVTlzK3pDQ3hvSmxhS1IKTVhidVE4N1g3bS85VlJueHhvNk56NVpmN09USFRwTk9JNlZqYTBCeGJtSUFVNnlyaXc5VXJnaWJYZk9qM2o2bgpNVExCdWdVVklCMGJCYWFzSnNBTUsrdzRMQU52YXBlWjBET1NuT1I0S0syNEowT3lvRjVmSG1wNTllTTE3SW9GClFxQmh6cG1RVWd1bmVjRVD4QlRxck5wRzc5UjF1K1YrNHd3Y2tQYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=", true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := CertPoolFromBase64(tt.encPemCerts) - if (err != nil) != tt.wantErr { - t.Errorf("CertPoolFromBase64() error = %v, wantErr %v", err, tt.wantErr) - return - } - }) - } -} - -func TestCertPoolFromFile(t *testing.T) { - tests := []struct { - name string - pemFile string - wantErr bool - }{ - {"good", "./testdata/ca.pem", false}, - {"bad doesn't exist", "./testdata/no-exist.pem", true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := CertPoolFromFile(tt.pemFile) - if (err != nil) != tt.wantErr { - t.Errorf("CertPoolFromFile() error = %v, wantErr %v", err, tt.wantErr) - return - } - }) - } -} - func TestCertificateFromFile(t *testing.T) { cert, err := CertificateFromFile("testdata/example-cert.pem", "testdata/example-key.pem") if err != nil {