@@ -3,6 +3,7 @@ package utils
3
3
import (
4
4
"crypto/tls"
5
5
"fmt"
6
+ "strings"
6
7
7
8
envoycore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
8
9
envoygrpccredential "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v3"
@@ -14,6 +15,7 @@ import (
14
15
v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
15
16
"github.com/solo-io/gloo/projects/gloo/pkg/api/v1/ssl"
16
17
"github.com/solo-io/solo-kit/pkg/api/v1/resources/core"
18
+ "k8s.io/client-go/util/cert"
17
19
)
18
20
19
21
//go:generate mockgen -destination mocks/mock_ssl.go github.com/solo-io/gloo/projects/gloo/pkg/utils SslConfigTranslator
@@ -429,6 +431,10 @@ func getSslSecrets(ref core.ResourceRef, secrets v1.SecretList) (string, string,
429
431
rootCa := sslSecret .Tls .GetRootCa ()
430
432
ocspStaple := sslSecret .Tls .GetOcspStaple ()
431
433
434
+ // we always return an error when the certChain and/or privateKey are invalid
435
+ // in theory we could propagate only the valid blocks of the certChain (ie the output of cert.ParseCertsPEM(certChain))º
436
+ // and this would be accepted by Envoy, however we choose to maintain consistency between the secret at rest and in
437
+ // Envoy, which also maintains consistency with existing UX
432
438
err = isValidSslKeyPair (certChain , privateKey , rootCa )
433
439
if err != nil {
434
440
return "" , "" , "" , nil , InvalidTlsSecretError (secret .GetMetadata ().Ref (), err )
@@ -437,13 +443,37 @@ func getSslSecrets(ref core.ResourceRef, secrets v1.SecretList) (string, string,
437
443
return certChain , privateKey , rootCa , ocspStaple , nil
438
444
}
439
445
446
+ // isValidSslKeyPair validates that the cert and key are a valid pair
447
+ // It previously only checked in go but now also checks that nothing is lost in cert encoding
440
448
func isValidSslKeyPair (certChain , privateKey , rootCa string ) error {
441
449
// in the case where we _only_ provide a rootCa, we do not want to validate tls.key+tls.cert
442
450
if (certChain == "" ) && (privateKey == "" ) && (rootCa != "" ) {
443
451
return nil
444
452
}
445
453
454
+ // validate that the cert and key are a valid pair
446
455
_ , err := tls .X509KeyPair ([]byte (certChain ), []byte (privateKey ))
456
+ if err != nil {
457
+ return err
458
+ }
459
+
460
+ // validate that the parsed piece is valid
461
+ // this is still faster than a call out to openssl despite this second parsing pass of the cert
462
+ // pem parsing in go is permissive while envoy is not
463
+ // this might not be needed once we have larger envoy validation
464
+ candidateCert , err := cert .ParseCertsPEM ([]byte (certChain ))
465
+ if err != nil {
466
+ return err
467
+ }
468
+ reencoded , err := cert .EncodeCertificates (candidateCert ... )
469
+ if err != nil {
470
+ return err
471
+ }
472
+ trimmedEncoded := strings .TrimSpace (string (reencoded ))
473
+ if trimmedEncoded != strings .TrimSpace (certChain ) {
474
+ return fmt .Errorf ("certificate chain does not match parsed certificate" )
475
+ }
476
+
447
477
return err
448
478
}
449
479
0 commit comments