Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ gateways:
conditions:
- lastTransitionTime: null
message: Secret envoy-gateway/tls-secret-ecdsa-2 public key algorithm must
be unique, matched certificate FQDN [foo.bar.com] has a conflicting algorithm
[ECDSA].
be unique, certificate domain foo.bar.com has a conflicting algorithm [ECDSA].
reason: InvalidCertificateRef
status: "False"
type: ResolvedRefs
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
creationTimestamp: null
name: gateway-1
namespace: envoy-gateway
spec:
gatewayClassName: envoy-gateway-class
listeners:
- allowedRoutes:
namespaces:
from: All
hostname: example.com
name: tls
port: 443
protocol: HTTPS
tls:
certificateRefs:
- group: null
kind: null
name: tls-secret-1
mode: Terminate
status:
listeners:
- attachedRoutes: 1
conditions:
- lastTransitionTime: null
message: Sending translated listener configuration to the data plane
reason: Programmed
status: "True"
type: Programmed
- lastTransitionTime: null
message: Listener has been successfully translated
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: null
message: Listener references have been resolved
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
name: tls
supportedKinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
- group: gateway.networking.k8s.io
kind: GRPCRoute
httpRoutes:
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
creationTimestamp: null
name: httproute-1
namespace: default
spec:
parentRefs:
- name: gateway-1
namespace: envoy-gateway
rules:
- backendRefs:
- name: service-1
port: 8080
matches:
- path:
value: /
status:
parents:
- conditions:
- lastTransitionTime: null
message: Route is accepted
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: null
message: Resolved all the Object references for the Route
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parentRef:
name: gateway-1
namespace: envoy-gateway
infraIR:
envoy-gateway/gateway-1:
proxy:
listeners:
- address: null
name: envoy-gateway/gateway-1/tls
ports:
- containerPort: 10443
name: https-443
protocol: HTTPS
servicePort: 443
metadata:
labels:
gateway.envoyproxy.io/owning-gateway-name: gateway-1
gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
ownerReference:
kind: GatewayClass
name: envoy-gateway-class
name: envoy-gateway/gateway-1
namespace: envoy-gateway-system
xdsIR:
envoy-gateway/gateway-1:
accessLog:
json:
- path: /dev/stdout
globalResources:
proxyServiceCluster:
name: envoy-gateway/gateway-1
settings:
- addressType: IP
endpoints:
- host: 7.6.5.4
port: 8080
zone: zone1
metadata:
name: envoy-envoy-gateway-gateway-1-196ae069
namespace: envoy-gateway-system
sectionName: "8080"
name: envoy-gateway/gateway-1
protocol: TCP
http:
- address: 0.0.0.0
externalPort: 443
hostnames:
- example.com
isHTTP2: false
metadata:
kind: Gateway
name: gateway-1
namespace: envoy-gateway
sectionName: tls
name: envoy-gateway/gateway-1/tls
path:
escapedSlashesAction: UnescapeAndRedirect
mergeSlashes: true
port: 10443
routes:
- destination:
metadata:
kind: HTTPRoute
name: httproute-1
namespace: default
name: httproute/default/httproute-1/rule/0
settings:
- addressType: IP
endpoints:
- host: 7.7.7.7
port: 8080
metadata:
name: service-1
namespace: default
sectionName: "8080"
name: httproute/default/httproute-1/rule/0/backend/0
protocol: HTTP
weight: 1
hostname: example.com
isHTTP2: false
metadata:
kind: HTTPRoute
name: httproute-1
namespace: default
name: httproute/default/httproute-1/rule/0/match/0/example_com
pathMatch:
distinct: false
name: ""
prefix: /
tls:
alpnProtocols: null
certificates:
- certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
name: envoy-gateway/tls-secret-1
privateKey: '[redacted]'
readyListener:
address: 0.0.0.0
ipFamily: IPv4
path: /ready
port: 19003
54 changes: 22 additions & 32 deletions internal/gatewayapi/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@ import (
"time"

corev1 "k8s.io/api/core/v1"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
)

// validateTLSSecretData ensures the cert and key provided in a secret
// is not malformed and can be properly parsed
func validateTLSSecretsData(secrets []*corev1.Secret, host *gwapiv1.Hostname) ([]*x509.Certificate, error) {
func validateTLSSecretsData(secrets []*corev1.Secret) ([]*x509.Certificate, error) {
var publicKeyAlgorithm string
var certs []*x509.Certificate
var parseErr error

pkaSecretSet := make(map[string][]string)
pkaSecretSet := make(map[string]string)
for _, secret := range secrets {
certData := secret.Data[corev1.TLSCertKey]

Expand All @@ -48,18 +47,29 @@ func validateTLSSecretsData(secrets []*corev1.Secret, host *gwapiv1.Hostname) ([
return nil, 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)
}

matchedFQDN, err := verifyHostname(cert, host)
if err != nil {
return nil, fmt.Errorf("%s/%s must contain valid %s and %s, hostname %s does not match Common Name or DNS Names in the certificate %s", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, string(*host), corev1.TLSCertKey)
// SNI and SAN/Cert Domain mismatch is allowed
// Consider converting this into a warning once
// https://github.com/envoyproxy/gateway/issues/6717 is in

// Extract certificate domains (SANs or CN) for uniqueness checking
var certDomains []string
if len(cert.DNSNames) > 0 {
certDomains = cert.DNSNames
} else if cert.Subject.CommonName != "" {
certDomains = []string{cert.Subject.CommonName}
}
pkaSecretKey := fmt.Sprintf("%s/%s", publicKeyAlgorithm, matchedFQDN)

// Check whether the public key algorithm and matched certificate FQDN in the referenced secrets are unique.
if matchedFQDN, ok := pkaSecretSet[pkaSecretKey]; ok {
return nil, fmt.Errorf("%s/%s public key algorithm must be unique, matched certificate FQDN %s has a conflicting algorithm [%s]",
secret.Namespace, secret.Name, matchedFQDN, publicKeyAlgorithm)
// Check uniqueness for each domain in the certificate with this algorithm
for _, domain := range certDomains {
pkaSecretKey := fmt.Sprintf("%s/%s", publicKeyAlgorithm, domain)

// Check whether the public key algorithm and certificate domain are unique
if _, ok := pkaSecretSet[pkaSecretKey]; ok {
return nil, fmt.Errorf("%s/%s public key algorithm must be unique, certificate domain %s has a conflicting algorithm [%s]",
secret.Namespace, secret.Name, domain, publicKeyAlgorithm)
}
pkaSecretSet[pkaSecretKey] = domain
}
pkaSecretSet[pkaSecretKey] = matchedFQDN

switch keyBlock.Type {
case "PRIVATE KEY":
Expand All @@ -86,26 +96,6 @@ func validateTLSSecretsData(secrets []*corev1.Secret, host *gwapiv1.Hostname) ([
return certs, parseErr
}

// verifyHostname checks if the listener Hostname matches any domain in the certificate, returns a list of matched hosts.
func verifyHostname(cert *x509.Certificate, host *gwapiv1.Hostname) ([]string, error) {
var matchedHosts []string

listenerContext := ListenerContext{
Listener: &gwapiv1.Listener{Hostname: host},
}
if len(cert.DNSNames) > 0 {
matchedHosts = computeHosts(cert.DNSNames, &listenerContext)
} else {
matchedHosts = computeHosts([]string{cert.Subject.CommonName}, &listenerContext)
}

if len(matchedHosts) > 0 {
return matchedHosts, nil
}

return nil, x509.HostnameError{Certificate: cert, Host: string(*host)}
}

func validateCertificate(data []byte) error {
block, _ := pem.Decode(data)
if block == nil {
Expand Down
Loading