github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/secrets/validation.go (about) 1 package secrets 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 "encoding/pem" 7 "fmt" 8 "regexp" 9 10 api_v1 "k8s.io/api/core/v1" 11 ) 12 13 // JWTKeyKey is the key of the data field of a Secret where the JWK must be stored. 14 const JWTKeyKey = "jwk" 15 16 // CAKey is the key of the data field of a Secret where the certificate authority must be stored. 17 const CAKey = "ca.crt" 18 19 // ClientSecretKey is the key of the data field of a Secret where the OIDC client secret must be stored. 20 const ClientSecretKey = "client-secret" 21 22 // SecretTypeCA contains a certificate authority for TLS certificate verification. #nosec G101 23 const SecretTypeCA api_v1.SecretType = "nginx.org/ca" 24 25 // SecretTypeJWK contains a JWK (JSON Web Key) for validating JWTs (JSON Web Tokens). #nosec G101 26 const SecretTypeJWK api_v1.SecretType = "nginx.org/jwk" 27 28 // SecretTypeOIDC contains an OIDC client secret for use in oauth flows. #nosec G101 29 const SecretTypeOIDC api_v1.SecretType = "nginx.org/oidc" 30 31 // ValidateTLSSecret validates the secret. If it is valid, the function returns nil. 32 func ValidateTLSSecret(secret *api_v1.Secret) error { 33 if secret.Type != api_v1.SecretTypeTLS { 34 return fmt.Errorf("TLS Secret must be of the type %v", api_v1.SecretTypeTLS) 35 } 36 37 // Kubernetes ensures that 'tls.crt' and 'tls.key' are present for secrets of api_v1.SecretTypeTLS type 38 39 _, err := tls.X509KeyPair(secret.Data[api_v1.TLSCertKey], secret.Data[api_v1.TLSPrivateKeyKey]) 40 if err != nil { 41 return fmt.Errorf("Failed to validate TLS cert and key: %w", err) 42 } 43 44 return nil 45 } 46 47 // ValidateJWKSecret validates the secret. If it is valid, the function returns nil. 48 func ValidateJWKSecret(secret *api_v1.Secret) error { 49 if secret.Type != SecretTypeJWK { 50 return fmt.Errorf("JWK secret must be of the type %v", SecretTypeJWK) 51 } 52 53 if _, exists := secret.Data[JWTKeyKey]; !exists { 54 return fmt.Errorf("JWK secret must have the data field %v", JWTKeyKey) 55 } 56 57 // we don't validate the contents of secret.Data[JWTKeyKey], because invalid contents will not make NGINX Plus 58 // fail to reload: NGINX Plus will return 500 responses for the affected URLs. 59 60 return nil 61 } 62 63 // ValidateCASecret validates the secret. If it is valid, the function returns nil. 64 func ValidateCASecret(secret *api_v1.Secret) error { 65 if secret.Type != SecretTypeCA { 66 return fmt.Errorf("CA secret must be of the type %v", SecretTypeCA) 67 } 68 69 if _, exists := secret.Data[CAKey]; !exists { 70 return fmt.Errorf("CA secret must have the data field %v", CAKey) 71 } 72 73 block, _ := pem.Decode(secret.Data[CAKey]) 74 if block == nil { 75 return fmt.Errorf("The data field %s must hold a valid CERTIFICATE PEM block", CAKey) 76 } 77 if block.Type != "CERTIFICATE" { 78 return fmt.Errorf("The data field %s must hold a valid CERTIFICATE PEM block, but got '%s'", CAKey, block.Type) 79 } 80 81 _, err := x509.ParseCertificate(block.Bytes) 82 if err != nil { 83 return fmt.Errorf("Failed to validate certificate: %w", err) 84 } 85 86 return nil 87 } 88 89 // ValidateOIDCSecret validates the secret. If it is valid, the function returns nil. 90 func ValidateOIDCSecret(secret *api_v1.Secret) error { 91 if secret.Type != SecretTypeOIDC { 92 return fmt.Errorf("OIDC secret must be of the type %v", SecretTypeOIDC) 93 } 94 95 clientSecret, exists := secret.Data[ClientSecretKey] 96 if !exists { 97 return fmt.Errorf("OIDC secret must have the data field %v", ClientSecretKey) 98 } 99 100 if msg, ok := isValidClientSecretValue(string(clientSecret)); !ok { 101 return fmt.Errorf("OIDC client secret is invalid: %s", msg) 102 } 103 return nil 104 } 105 106 // IsSupportedSecretType checks if the secret type is supported. 107 func IsSupportedSecretType(secretType api_v1.SecretType) bool { 108 return secretType == api_v1.SecretTypeTLS || 109 secretType == SecretTypeCA || 110 secretType == SecretTypeJWK || 111 secretType == SecretTypeOIDC 112 } 113 114 // ValidateSecret validates the secret. If it is valid, the function returns nil. 115 func ValidateSecret(secret *api_v1.Secret) error { 116 switch secret.Type { 117 case api_v1.SecretTypeTLS: 118 return ValidateTLSSecret(secret) 119 case SecretTypeJWK: 120 return ValidateJWKSecret(secret) 121 case SecretTypeCA: 122 return ValidateCASecret(secret) 123 case SecretTypeOIDC: 124 return ValidateOIDCSecret(secret) 125 } 126 127 return fmt.Errorf("Secret is of the unsupported type %v", secret.Type) 128 } 129 130 var clientSecretValueFmtRegexp = regexp.MustCompile(`^([^"$\\\s]|\\[^$])*$`) 131 132 func isValidClientSecretValue(s string) (string, bool) { 133 if ok := clientSecretValueFmtRegexp.MatchString(s); !ok { 134 return `It must contain valid ASCII characters, must have all '"' escaped and must not contain any '$' or whitespaces ('\n', '\t' etc.) or end with an unescaped '\'`, false 135 } 136 return "", true 137 }