Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion internal/gatewayapi/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func parseCertsFromTLSSecretsData(secrets []*corev1.Secret) ([]*corev1.Secret, [

keyData := secret.Data[corev1.TLSPrivateKeyKey]

keyBlock, _ := pem.Decode(keyData)
keyBlock := firstSupportedPrivateKeyBlock(keyData)
Comment thread
jvlxz marked this conversation as resolved.
if keyBlock == nil {
errs = append(errs, fmt.Errorf("%s/%s must contain valid %s and %s, unable to decode pem data in %s",
secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSPrivateKeyKey))
Expand Down Expand Up @@ -211,6 +211,29 @@ func parseCertsFromTLSSecretsData(secrets []*corev1.Secret) ([]*corev1.Secret, [
return validSecrets, certs, nil
}

// firstSupportedPrivateKeyBlock returns the first supported private key PEM block,
// skipping preceding non-key blocks such as OpenSSL's "EC PARAMETERS" block.
// It returns the first block when no supported key is found so the caller can
// report the unexpected block type.
func firstSupportedPrivateKeyBlock(data []byte) *pem.Block {
var firstBlock *pem.Block
for len(data) > 0 {
block, rest := pem.Decode(data)
if block == nil {
return firstBlock
}
if firstBlock == nil {
firstBlock = block
}
switch block.Type {
case "PRIVATE KEY", "RSA PRIVATE KEY", "EC PRIVATE KEY":
Comment thread
jvlxz marked this conversation as resolved.
return block
}
data = rest
}
return firstBlock
}

// validateCertBlock parses the certificate(s) in a single PEM block and returns
// an error if the block is not a valid certificate or if any certificate in it
// is expired or not yet valid (outside its NotBefore/NotAfter window).
Expand Down
31 changes: 31 additions & 0 deletions internal/gatewayapi/tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,37 @@ func TestValidateTLSSecretsData(t *testing.T) {
}
}

func TestValidateTLSSecretsDataWithECParameters(t *testing.T) {
const ecParametersPEM = "-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n"

t.Run("ec-parameters-before-private-key", func(t *testing.T) {
secrets := createTestSecrets(t, []string{"ecdsa-p256-cert.pem"}, []string{"ecdsa-p256.key"})
keyData := append([]byte(nil), secrets[0].Data[corev1.TLSPrivateKeyKey]...)
secrets[0].Data[corev1.TLSPrivateKeyKey] = append([]byte(ecParametersPEM), keyData...)

validSecrets, certs, err := parseCertsFromTLSSecretsData(secrets)
require.NoError(t, err)
require.Len(t, validSecrets, 1)
require.Len(t, certs, 1)

keyBlock, _ := pem.Decode(keyData)
require.NotNil(t, keyBlock)
require.Equal(t, pem.EncodeToMemory(keyBlock), validSecrets[0].Data[corev1.TLSPrivateKeyKey])
})

t.Run("ec-parameters-without-private-key", func(t *testing.T) {
secrets := createTestSecrets(t, []string{"ecdsa-p256-cert.pem"}, []string{"ecdsa-p256.key"})
secrets[0].Data[corev1.TLSPrivateKeyKey] = []byte(ecParametersPEM)

validSecrets, certs, err := parseCertsFromTLSSecretsData(secrets)
require.Error(t, err)
require.Equal(t, "test/secret must contain valid tls.crt and tls.key, EC PARAMETERS key format found in tls.key, supported formats are PKCS1, PKCS8 or EC", err.Error())
require.Equal(t, gwapiv1.ListenerReasonInvalidCertificateRef, err.Reason())
require.Empty(t, validSecrets)
require.Empty(t, certs)
})
}

func TestFilterValidCertificates(t *testing.T) {
type testCase struct {
Name string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed Gateway TLS Secret validation rejecting valid ECDSA private keys when the `tls.key` includes an `EC PARAMETERS` PEM block before the private key.