Skip to content

Commit

Permalink
1.17: projects/utils/ssl: Add a check for ssl secrets that will be re…
Browse files Browse the repository at this point in the history
…jected by envoy but not go (#10048)
  • Loading branch information
nfuden committed Sep 16, 2024
1 parent 26e7e9c commit e658203
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 0 deletions.
9 changes: 9 additions & 0 deletions changelog/v1.17.8/strict-tls-validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
changelog:
- type: FIX
issueLink: https://github.com/solo-io/solo-projects/issues/6772
resolvesIssue: false
description: >-
Plugs a gap where go would check a secret for validity per spec but envoy is more aggressive.
For example a tls secret with a certchain that contains an invalid pem block will be rejected by envoy but not go.
Prior to this pr these types of secrets would be accepted by gloo and nacked by envoy.
24 changes: 24 additions & 0 deletions projects/gateway2/translator/sslutils/ssl_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package sslutils
import (
"crypto/tls"
"fmt"
"strings"

"github.com/rotisserie/eris"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/util/cert"
)

var (
Expand Down Expand Up @@ -45,5 +47,27 @@ func isValidSslKeyPair(certChain, privateKey, rootCa []byte) error {
}

_, err := tls.X509KeyPair([]byte(certChain), []byte(privateKey))
if err != nil {
return err
}
// validate that the parsed piece is valid
// this is still faster than a call out to openssl despite this second parsing pass of the cert
// pem parsing in go is permissive while envoy is not
// this might not be needed once we have larger envoy validation
candidateCert, err := cert.ParseCertsPEM(certChain)
if err != nil {
// return err rather than sanitize. This is to maintain UX with older versions and to prevent a large refactor
// for gatewayv2 secret validation code.
return err
}
reencoded, err := cert.EncodeCertificates(candidateCert...)
if err != nil {
return err
}
trimmedEncoded := strings.TrimSpace(string(reencoded))
if trimmedEncoded != strings.TrimSpace(string(certChain)) {
return fmt.Errorf("certificate chain does not match parsed certificate")
}

return err
}
26 changes: 26 additions & 0 deletions projects/gloo/pkg/utils/ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package utils
import (
"crypto/tls"
"fmt"
"strings"

envoycore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoygrpccredential "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v3"
Expand All @@ -14,6 +15,7 @@ import (
v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
"github.com/solo-io/gloo/projects/gloo/pkg/api/v1/ssl"
"github.com/solo-io/solo-kit/pkg/api/v1/resources/core"
"k8s.io/client-go/util/cert"
)

//go:generate mockgen -destination mocks/mock_ssl.go github.com/solo-io/gloo/projects/gloo/pkg/utils SslConfigTranslator
Expand Down Expand Up @@ -433,7 +435,31 @@ func isValidSslKeyPair(certChain, privateKey, rootCa string) error {
return nil
}

// validate that the cert and key are a valid pair
_, err := tls.X509KeyPair([]byte(certChain), []byte(privateKey))
if err != nil {

return err
}

// validate that the parsed piece is valid
// this is still faster than a call out to openssl despite this second parsing pass of the cert
// pem parsing in go is permissive while envoy is not
// this might not be needed once we have larger envoy validation
candidateCert, err := cert.ParseCertsPEM([]byte(certChain))
if err != nil {
// return err rather than sanitize. This is to maintain UX with older versions and to keep in line with gateway2 pkg.
return err
}
reencoded, err := cert.EncodeCertificates(candidateCert...)
if err != nil {
return err
}
trimmedEncoded := strings.TrimSpace(string(reencoded))
if trimmedEncoded != strings.TrimSpace(certChain) {
return fmt.Errorf("certificate chain does not match parsed certificate")
}

return err
}

Expand Down
12 changes: 12 additions & 0 deletions projects/gloo/pkg/utils/ssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,18 @@ var _ = Describe("Ssl", func() {
Entry("upstreamCfg", func() utils.CertSource { return upstreamCfg }),
Entry("downstreamCfg", func() utils.CertSource { return downstreamCfg }),
)
DescribeTable("should fail if invalid for envoy cert is provided",
func(c func() utils.CertSource) {
// unterminated cert in chain is valid for go b ut not envoy
tlsSecret.CertChain += `-----BEGIN CERTIFICATE-----
MIID6TCCA1ICAQEwDQYJKoZIhvcNAQEFBQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYD`
_, err := resolveCommonSslConfig(c(), secrets)
Expect(err).To(HaveOccurred())

},
Entry("upstreamCfg", func() utils.CertSource { return upstreamCfg }),
Entry("downstreamCfg", func() utils.CertSource { return downstreamCfg }),
)
DescribeTable("should not have validation context if no rootca",
func(c func() utils.CertSource) {
tlsSecret.RootCa = ""
Expand Down

0 comments on commit e658203

Please sign in to comment.