diff --git a/docs/latest/user/tls-termination.md b/docs/latest/user/tls-termination.md new file mode 100644 index 00000000000..ebea433faf1 --- /dev/null +++ b/docs/latest/user/tls-termination.md @@ -0,0 +1,83 @@ +# TLS Termination for TCP + +This guide will walk through the steps required to configure TLS Terminate mode for TCP traffic via Envoy Gateway. The guide uses a self-signed CA, so it should be used for testing and demonstration purposes only. + +## Prerequisites + +- OpenSSL to generate TLS assets. + +## Installation + +Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway. + +## TLS Certificates +Generate the certificates and keys used by the Gateway to terminate client TLS connections. + +Create a root certificate and private key to sign certificates: + +```shell +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt +``` + +Create a certificate and a private key for `www.example.com`: + +```shell +openssl req -out www.example.com.csr -newkey rsa:2048 -nodes -keyout www.example.com.key -subj "/CN=www.example.com/O=example organization" +openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in www.example.com.csr -out www.example.com.crt +``` + +Store the cert/key in a Secret: + +```shell +kubectl create secret tls example-cert --key=www.example.com.key --cert=www.example.com.crt +``` + +Install the TLS Termination for TCP example resources: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/tls-termination.yaml +``` + +Verify the Gateway status: + +```shell +kubectl get gateway/eg -o yaml +``` + +## Testing + +### Clusters without External LoadBalancer Support + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8443:443 & +``` + +Query the example app through Envoy proxy: + +```shell +curl -v -HHost:www.example.com --resolve "www.example.com:8443:127.0.0.1" \ +--cacert example.com.crt https://www.example.com:8443/get +``` + +### Clusters with External LoadBalancer Support + +Get the External IP of the Gateway: + +```shell +export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}') +``` + +Query the example app through the Gateway: + +```shell +curl -v -HHost:www.example.com --resolve "www.example.com:443:${GATEWAY_HOST}" \ +--cacert example.com.crt https://www.example.com/get +``` diff --git a/examples/kubernetes/tls-termination.yaml b/examples/kubernetes/tls-termination.yaml new file mode 100644 index 00000000000..ab69765ef1f --- /dev/null +++ b/examples/kubernetes/tls-termination.yaml @@ -0,0 +1,96 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: eg +spec: + gatewayClassName: eg + listeners: + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: TLS + tls: + certificateRefs: + - group: "" + kind: Secret + name: example-cert + mode: Terminate +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: backend +--- +apiVersion: v1 +kind: Service +metadata: + name: backend + labels: + app: backend + service: backend +spec: + ports: + - name: http + port: 3000 + targetPort: 3000 + selector: + app: backend +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend +spec: + replicas: 1 + selector: + matchLabels: + app: backend + version: v1 + template: + metadata: + labels: + app: backend + version: v1 + spec: + serviceAccountName: backend + containers: + - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e + imagePullPolicy: IfNotPresent + name: backend + ports: + - containerPort: 3000 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TCPRoute +metadata: + name: backend +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 diff --git a/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml b/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml index e3631ea1d8b..252a27f7873 100644 --- a/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml +++ b/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml @@ -34,6 +34,8 @@ gateways: type: Programmed name: tls supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute - group: gateway.networking.k8s.io kind: TLSRoute httpRoutes: diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index da7b085bd79..00139d20824 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -428,3 +428,11 @@ func irTLSConfigs(tlsSecrets []*v1.Secret) []*ir.TLSListenerConfig { func irTLSListenerConfigName(secret *v1.Secret) string { return fmt.Sprintf("%s-%s", secret.Namespace, secret.Name) } + +func protocolSliceToStringSlice(protocols []v1beta1.ProtocolType) []string { + var protocolStrings []string + for _, protocol := range protocols { + protocolStrings = append(protocolStrings, string(protocol)) + } + return protocolStrings +} diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index bd6865f1bbd..6ef2168b0ef 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -22,7 +22,7 @@ type ListenersTranslator interface { func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap, infraIR InfraIRMap, resources *Resources) { t.validateConflictedLayer7Listeners(gateways) - t.validateConflictedLayer4Listeners(gateways, v1beta1.TCPProtocolType) + t.validateConflictedLayer4Listeners(gateways, v1beta1.TCPProtocolType, v1beta1.TLSProtocolType) t.validateConflictedLayer4Listeners(gateways, v1beta1.UDPProtocolType) // Iterate through all listeners to validate spec @@ -50,7 +50,18 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap // Process protocol & supported kinds switch listener.Protocol { case v1beta1.TLSProtocolType: - t.validateAllowedRoutes(listener, KindTLSRoute) + if listener.TLS != nil { + switch *listener.TLS.Mode { + case v1beta1.TLSModePassthrough: + t.validateAllowedRoutes(listener, KindTLSRoute) + case v1beta1.TLSModeTerminate: + t.validateAllowedRoutes(listener, KindTCPRoute) + default: + t.validateAllowedRoutes(listener, KindTCPRoute, KindTLSRoute) + } + } else { + t.validateAllowedRoutes(listener, KindTCPRoute, KindTLSRoute) + } case v1beta1.HTTPProtocolType, v1beta1.HTTPSProtocolType: t.validateAllowedRoutes(listener, KindHTTPRoute, KindGRPCRoute) case v1beta1.TCPProtocolType: diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 859ad2a0297..84840a5aefa 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -663,9 +663,9 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour Name: irTLSListenerName(listener, tlsRoute), Address: "0.0.0.0", Port: uint32(containerPort), - TLS: &ir.TLSInspectorConfig{ + TLS: &ir.TLS{Passthrough: &ir.TLSInspectorConfig{ SNIs: hosts, - }, + }}, Destinations: routeDestinations, } gwXdsIR := xdsIR[irKey] @@ -946,6 +946,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour Address: "0.0.0.0", Port: uint32(containerPort), Destinations: routeDestinations, + TLS: &ir.TLS{Terminate: irTLSConfigs(listener.tlsSecrets)}, } gwXdsIR := xdsIR[irKey] gwXdsIR.TCP = append(gwXdsIR.TCP, irListener) diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml index 31b694f626b..9ee5719fe7f 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml @@ -146,8 +146,9 @@ xdsIR: address: 0.0.0.0 port: 10090 tls: - snis: - - "foo.com" + passthrough: + snis: + - "foo.com" destinations: - host: 7.7.7.7 port: 8080 diff --git a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml index 306fec79cae..b5d71b6c41c 100644 --- a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml @@ -95,6 +95,7 @@ xdsIR: destinations: - host: "7.7.7.7" port: 8163 + tls: {} infraIR: envoy-gateway-gateway-1: diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-port.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml similarity index 97% rename from internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-port.in.yaml rename to internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml index a53076da009..fc0744606b6 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-port.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml @@ -14,7 +14,7 @@ gateways: namespaces: from: All - name: tcp2 - protocol: TCP + protocol: TLS port: 162 allowedRoutes: namespaces: diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-port.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml similarity index 91% rename from internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-port.out.yaml rename to internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml index 68f4f4441fd..70c1028c078 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-port.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml @@ -14,7 +14,7 @@ gateways: namespaces: from: All - name: tcp2 - protocol: TCP + protocol: TLS port: 162 allowedRoutes: namespaces: @@ -39,16 +39,18 @@ gateways: supportedKinds: - group: gateway.networking.k8s.io kind: TCPRoute + - group: gateway.networking.k8s.io + kind: TLSRoute AttachedRoutes: 0 conditions: - type: Conflicted status: "True" reason: ProtocolConflict - message: Only one TCP listener is allowed in a given port + message: Only one TCP/TLS listener is allowed in a given port - type: Programmed status: "False" reason: Invalid - message: Listener is invalid, see other Conditions for details. + message: Listener must have TLS set when protocol is TLS. tcpRoutes: - apiVersion: gateway.networking.k8s.io/v1alpha2 kind: TCPRoute @@ -87,6 +89,7 @@ xdsIR: destinations: - host: "7.7.7.7" port: 8163 + tls: {} infraIR: envoy-gateway-gateway-1: proxy: diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml index 8ee45377b78..85271bd3ffe 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml @@ -134,6 +134,7 @@ xdsIR: - name: "envoy-gateway-gateway-1-tcp-tcproute-1" address: "0.0.0.0" port: 10080 + tls: {} destinations: - host: "7.7.7.7" port: 8163 diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml index 7f27e8b0fc6..96601933ba5 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml @@ -116,12 +116,14 @@ xdsIR: - name: "envoy-gateway-gateway-1-tcp1-tcproute-1" address: "0.0.0.0" port: 10162 + tls: {} destinations: - host: "7.7.7.7" port: 8163 - name: "envoy-gateway-gateway-1-tcp2-tcproute-2" address: "0.0.0.0" port: 10163 + tls: {} destinations: - host: "7.7.7.7" port: 8163 diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml index 47bc975712f..ca91c153c1a 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml @@ -112,12 +112,14 @@ xdsIR: - name: "envoy-gateway-gateway-1-tcp1-tcproute-1" address: "0.0.0.0" port: 10161 + tls: {} destinations: - host: "7.7.7.7" port: 8163 - name: "envoy-gateway-gateway-1-tcp2-tcproute-1" address: "0.0.0.0" port: 10162 + tls: {} destinations: - host: "7.7.7.7" port: 8163 diff --git a/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml new file mode 100644 index 00000000000..92954ea1b89 --- /dev/null +++ b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml @@ -0,0 +1,46 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + port: 90 + tls: + certificateRefs: + - group: "" + kind: Secret + name: tls-secret-1 + mode: Terminate + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K diff --git a/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml new file mode 100644 index 00000000000..2835d1145f0 --- /dev/null +++ b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml @@ -0,0 +1,107 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 90 + protocol: TLS + tls: + certificateRefs: + - group: "" + kind: Secret + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: "2023-05-19T21:35:48Z" + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: "2023-05-19T21:35:48Z" + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: "2023-05-19T21:35:48Z" + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: "2023-05-19T21:35:48Z" + 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: + Addresses: null + Config: null + Listeners: + - Address: "" + Ports: + - ContainerPort: 10090 + Name: tls + Protocol: TLS + ServicePort: 90 + Metadata: + Labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + Name: envoy-gateway-gateway-1 +XdsIR: + envoy-gateway-gateway-1: + HTTP: null + TCP: + - Address: 0.0.0.0 + Destinations: + - Host: 7.7.7.7 + Port: 8080 + Weight: null + Name: envoy-gateway-gateway-1-tls-tcproute-1 + Port: 10090 + TLS: + Terminate: + - Name: envoy-gateway-tls-secret-1 + PrivateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + ServerCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + UDP: null diff --git a/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml b/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml index e5fb79bea1a..5d773067b38 100644 --- a/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml @@ -68,8 +68,9 @@ xdsIR: address: 0.0.0.0 port: 10090 tls: - snis: - - foo.com + passthrough: + snis: + - "foo.com" destinations: - host: 7.7.7.7 port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml b/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml index a0b74436144..11b503afc42 100644 --- a/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml @@ -99,8 +99,9 @@ xdsIR: address: 0.0.0.0 port: 10091 tls: - snis: - - foo.com + passthrough: + snis: + - foo.com destinations: - host: 7.7.7.7 port: 8080 @@ -109,8 +110,9 @@ xdsIR: address: 0.0.0.0 port: 10091 tls: - snis: - - bar.com + passthrough: + snis: + - bar.com destinations: - host: 7.7.7.7 port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-incorrect-mode.in.yaml b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-incorrect-mode.in.yaml deleted file mode 100644 index d8a62c2adf3..00000000000 --- a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-incorrect-mode.in.yaml +++ /dev/null @@ -1,31 +0,0 @@ -gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: tls - protocol: TLS - tls: - mode: Terminate - port: 90 - allowedRoutes: - namespaces: - from: All -tlsRoutes: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: TLSRoute - metadata: - namespace: default - name: tlsroute-1 - spec: - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - rules: - - backendRefs: - - name: service-1 - port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-incorrect-mode.out.yaml b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-incorrect-mode.out.yaml deleted file mode 100644 index ada844a1b35..00000000000 --- a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-incorrect-mode.out.yaml +++ /dev/null @@ -1,71 +0,0 @@ -gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: tls - protocol: TLS - tls: - mode: Terminate - port: 90 - allowedRoutes: - namespaces: - from: All - status: - listeners: - - name: tls - supportedKinds: - - group: gateway.networking.k8s.io - kind: TLSRoute - attachedRoutes: 0 - conditions: - - type: Programmed - status: "False" - reason: UnsupportedTLSMode - message: TLS mode "Terminate" is not supported, TLS mode must be Passthrough. -tlsRoutes: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: TLSRoute - metadata: - namespace: default - name: tlsroute-1 - spec: - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - rules: - - backendRefs: - - name: service-1 - port: 8080 - status: - parents: - - parentRef: - namespace: envoy-gateway - name: gateway-1 - controllerName: gateway.envoyproxy.io/gatewayclass-controller - conditions: - - type: Accepted - status: "False" - reason: NoReadyListeners - message: There are no ready listeners for this parent ref - - type: ResolvedRefs - status: "True" - reason: ResolvedRefs - message: Resolved all the Object references for the Route -xdsIR: - envoy-gateway-gateway-1: {} -infraIR: - envoy-gateway-gateway-1: - proxy: - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway-gateway-1 - - listeners: - - address: "" diff --git a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml index 3119bb270e1..b61bd11693b 100644 --- a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml @@ -17,6 +17,8 @@ gateways: listeners: - name: tls supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute - group: gateway.networking.k8s.io kind: TLSRoute attachedRoutes: 0 diff --git a/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml index 7b8992d0d0f..344b6a0fe93 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml @@ -69,8 +69,9 @@ xdsIR: address: 0.0.0.0 port: 10090 tls: - snis: - - foo.com + passthrough: + snis: + - "foo.com" destinations: - host: 7.7.7.7 port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml index 19c25f7a5c5..b2a6a9e0955 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml @@ -67,8 +67,9 @@ xdsIR: address: 0.0.0.0 port: 10091 tls: - snis: - - "*" + passthrough: + snis: + - "*" destinations: - host: 7.7.7.7 port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml index b7a1954dd90..fdca4145e33 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml @@ -69,8 +69,9 @@ xdsIR: address: 0.0.0.0 port: 10091 tls: - snis: - - foo.com + passthrough: + snis: + - foo.com destinations: - host: 7.7.7.7 port: 8080 diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 4d9065cb185..b2883bb28bc 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -8,6 +8,7 @@ package gatewayapi import ( "fmt" "net/netip" + "strings" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -208,147 +209,128 @@ func (t *Translator) validateAllowedNamespaces(listener *ListenerContext) { } } -func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resources *Resources) { - switch listener.Protocol { - case v1beta1.HTTPProtocolType, v1beta1.UDPProtocolType, v1beta1.TCPProtocolType: - if listener.TLS != nil { - listener.SetCondition( - v1beta1.ListenerConditionProgrammed, - metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, - fmt.Sprintf("Listener must not have TLS set when protocol is %s.", listener.Protocol), - ) - } - case v1beta1.HTTPSProtocolType: - if listener.TLS == nil { - listener.SetCondition( - v1beta1.ListenerConditionProgrammed, - metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, - fmt.Sprintf("Listener must have TLS set when protocol is %s.", listener.Protocol), - ) - break - } +func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerContext, resources *Resources) []*v1.Secret { + if len(listener.TLS.CertificateRefs) == 0 { + listener.SetCondition( + v1beta1.ListenerConditionProgrammed, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalid, + "Listener must have at least 1 TLS certificate ref", + ) + return nil + } - if listener.TLS.Mode != nil && *listener.TLS.Mode != v1beta1.TLSModeTerminate { + secrets := make([]*v1.Secret, 0) + for _, certificateRef := range listener.TLS.CertificateRefs { + if certificateRef.Group != nil && string(*certificateRef.Group) != "" { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + v1beta1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - "UnsupportedTLSMode", - fmt.Sprintf("TLS %s mode is not supported, TLS mode must be Terminate.", *listener.TLS.Mode), + v1beta1.ListenerReasonInvalidCertificateRef, + "Listener's TLS certificate ref group must be unspecified/empty.", ) break } - if len(listener.TLS.CertificateRefs) == 0 { + if certificateRef.Kind != nil && string(*certificateRef.Kind) != KindSecret { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + v1beta1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, - "Listener must have at least 1 TLS certificate ref", + v1beta1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Listener's TLS certificate ref kind must be %s.", KindSecret), ) break } - secrets := make([]*v1.Secret, 0) - for _, certificateRef := range listener.TLS.CertificateRefs { - if certificateRef.Group != nil && string(*certificateRef.Group) != "" { - listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, - metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, - "Listener's TLS certificate ref group must be unspecified/empty.", - ) - break - } - - if certificateRef.Kind != nil && string(*certificateRef.Kind) != KindSecret { + secretNamespace := listener.gateway.Namespace + + if certificateRef.Namespace != nil && string(*certificateRef.Namespace) != "" && string(*certificateRef.Namespace) != listener.gateway.Namespace { + if !t.validateCrossNamespaceRef( + crossNamespaceFrom{ + group: v1beta1.GroupName, + kind: KindGateway, + namespace: listener.gateway.Namespace, + }, + crossNamespaceTo{ + group: "", + kind: KindSecret, + namespace: string(*certificateRef.Namespace), + name: string(certificateRef.Name), + }, + resources.ReferenceGrants, + ) { listener.SetCondition( v1beta1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, - fmt.Sprintf("Listener's TLS certificate ref kind must be %s.", KindSecret), + v1beta1.ListenerReasonRefNotPermitted, + fmt.Sprintf("Certificate ref to secret %s/%s not permitted by any ReferenceGrant.", *certificateRef.Namespace, certificateRef.Name), ) break } - secretNamespace := listener.gateway.Namespace - - if certificateRef.Namespace != nil && string(*certificateRef.Namespace) != "" && string(*certificateRef.Namespace) != listener.gateway.Namespace { - if !t.validateCrossNamespaceRef( - crossNamespaceFrom{ - group: v1beta1.GroupName, - kind: KindGateway, - namespace: listener.gateway.Namespace, - }, - crossNamespaceTo{ - group: "", - kind: KindSecret, - namespace: string(*certificateRef.Namespace), - name: string(certificateRef.Name), - }, - resources.ReferenceGrants, - ) { - listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, - metav1.ConditionFalse, - v1beta1.ListenerReasonRefNotPermitted, - fmt.Sprintf("Certificate ref to secret %s/%s not permitted by any ReferenceGrant.", *certificateRef.Namespace, certificateRef.Name), - ) - break - } - - secretNamespace = string(*certificateRef.Namespace) - } + secretNamespace = string(*certificateRef.Namespace) + } - secret := resources.GetSecret(secretNamespace, string(certificateRef.Name)) + secret := resources.GetSecret(secretNamespace, string(certificateRef.Name)) - if secret == nil { - listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, - metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, - fmt.Sprintf("Secret %s/%s does not exist.", listener.gateway.Namespace, certificateRef.Name), - ) - break - } + if secret == nil { + listener.SetCondition( + v1beta1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s/%s does not exist.", listener.gateway.Namespace, certificateRef.Name), + ) + break + } - if secret.Type != v1.SecretTypeTLS { - listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, - metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, - fmt.Sprintf("Secret %s/%s must be of type %s.", listener.gateway.Namespace, certificateRef.Name, v1.SecretTypeTLS), - ) - break - } + if secret.Type != v1.SecretTypeTLS { + listener.SetCondition( + v1beta1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s/%s must be of type %s.", listener.gateway.Namespace, certificateRef.Name, v1.SecretTypeTLS), + ) + break + } - if len(secret.Data[v1.TLSCertKey]) == 0 || len(secret.Data[v1.TLSPrivateKeyKey]) == 0 { - listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, - metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, - fmt.Sprintf("Secret %s/%s must contain %s and %s.", listener.gateway.Namespace, certificateRef.Name, v1.TLSCertKey, v1.TLSPrivateKeyKey), - ) - break - } + if len(secret.Data[v1.TLSCertKey]) == 0 || len(secret.Data[v1.TLSPrivateKeyKey]) == 0 { + listener.SetCondition( + v1beta1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s/%s must contain %s and %s.", listener.gateway.Namespace, certificateRef.Name, v1.TLSCertKey, v1.TLSPrivateKeyKey), + ) + break + } - err := validateTLSSecretData(secret) - if err != nil { - listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, - metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, - fmt.Sprintf("Secret %s/%s must contain valid %s and %s, %s.", listener.gateway.Namespace, certificateRef.Name, v1.TLSCertKey, v1.TLSPrivateKeyKey, err.Error()), - ) - break - } - secrets = append(secrets, secret) + err := validateTLSSecretData(secret) + if err != nil { + listener.SetCondition( + v1beta1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s/%s must contain valid %s and %s, %s.", listener.gateway.Namespace, certificateRef.Name, v1.TLSCertKey, v1.TLSPrivateKeyKey, err.Error()), + ) + break } + secrets = append(secrets, secret) + } - listener.SetTLSSecrets(secrets) + return secrets +} - case v1beta1.TLSProtocolType: +func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resources *Resources) { + switch listener.Protocol { + case v1beta1.HTTPProtocolType, v1beta1.UDPProtocolType, v1beta1.TCPProtocolType: + if listener.TLS != nil { + listener.SetCondition( + v1beta1.ListenerConditionProgrammed, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalid, + fmt.Sprintf("Listener must not have TLS set when protocol is %s.", listener.Protocol), + ) + } + case v1beta1.HTTPSProtocolType: if listener.TLS == nil { listener.SetCondition( v1beta1.ListenerConditionProgrammed, @@ -359,25 +341,55 @@ func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resourc break } - if listener.TLS.Mode != nil && *listener.TLS.Mode != v1beta1.TLSModePassthrough { + if listener.TLS.Mode != nil && *listener.TLS.Mode != v1beta1.TLSModeTerminate { listener.SetCondition( v1beta1.ListenerConditionProgrammed, metav1.ConditionFalse, "UnsupportedTLSMode", - fmt.Sprintf("TLS mode %q is not supported, TLS mode must be Passthrough.", *listener.TLS.Mode), + fmt.Sprintf("TLS %s mode is not supported, TLS mode must be Terminate.", *listener.TLS.Mode), ) break } - if len(listener.TLS.CertificateRefs) > 0 { + secrets := t.validateTerminateModeAndGetTLSSecrets(listener, resources) + listener.SetTLSSecrets(secrets) + + case v1beta1.TLSProtocolType: + if listener.TLS == nil { listener.SetCondition( v1beta1.ListenerConditionProgrammed, metav1.ConditionFalse, v1beta1.ListenerReasonInvalid, - "Listener must not have TLS certificate refs set for TLS mode Passthrough.", + fmt.Sprintf("Listener must have TLS set when protocol is %s.", listener.Protocol), ) break } + + if listener.TLS.Mode != nil && *listener.TLS.Mode == v1beta1.TLSModePassthrough { + if len(listener.TLS.CertificateRefs) > 0 { + listener.SetCondition( + v1beta1.ListenerConditionProgrammed, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalid, + "Listener must not have TLS certificate refs set for TLS mode Passthrough.", + ) + break + } + } + + if listener.TLS.Mode != nil && *listener.TLS.Mode == v1beta1.TLSModeTerminate { + if len(listener.TLS.CertificateRefs) == 0 { + listener.SetCondition( + v1beta1.ListenerConditionProgrammed, + metav1.ConditionFalse, + v1beta1.ListenerReasonInvalid, + "Listener must have TLS certificate refs set for TLS mode Terminate.", + ) + break + } + secrets := t.validateTerminateModeAndGetTLSSecrets(listener, resources) + listener.SetTLSSecrets(secrets) + } } } @@ -526,16 +538,18 @@ func (t *Translator) validateConflictedLayer7Listeners(gateways []*GatewayContex } } -func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContext, protocol v1beta1.ProtocolType) { +func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContext, protocols ...v1beta1.ProtocolType) { // Iterate through all layer-4(TCP UDP) listeners and check if there are more than one listener on the same port for _, gateway := range gateways { portListenerInfo := map[v1beta1.PortNumber]*portListeners{} for _, listener := range gateway.listeners { - if listener.Protocol == protocol { - if portListenerInfo[listener.Port] == nil { - portListenerInfo[listener.Port] = &portListeners{} + for _, protocol := range protocols { + if listener.Protocol == protocol { + if portListenerInfo[listener.Port] == nil { + portListenerInfo[listener.Port] = &portListeners{} + } + portListenerInfo[listener.Port].listeners = append(portListenerInfo[listener.Port].listeners, listener) } - portListenerInfo[listener.Port].listeners = append(portListenerInfo[listener.Port].listeners, listener) } } @@ -547,7 +561,7 @@ func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContex v1beta1.ListenerConditionConflicted, metav1.ConditionTrue, v1beta1.ListenerReasonProtocolConflict, - fmt.Sprintf("Only one %s listener is allowed in a given port", protocol), + fmt.Sprintf("Only one %s listener is allowed in a given port", strings.Join(protocolSliceToStringSlice(protocols), "/")), ) } } diff --git a/internal/ir/xds.go b/internal/ir/xds.go index eabf85d46c3..672e739582c 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -610,13 +610,22 @@ type TCPListener struct { Address string // Port on which the service can be expected to be accessed by clients. Port uint32 - // TLS information required for TLS Passthrough, If provided, incoming - // connections' server names are inspected and routed to backends accordingly. - TLS *TLSInspectorConfig + // TLS holds information for configuring TLS on a listener + TLS *TLS // Destinations associated with TCP traffic to the service. Destinations []*RouteDestination } +// TLS holds information for configuring TLS on a listener +// +k8s:deepcopy-gen=true +type TLS struct { + // TLS information required for TLS Passthrough, If provided, incoming + // connections' server names are inspected and routed to backends accordingly. + Passthrough *TLSInspectorConfig + // TLS information required for TLS Termination + Terminate []*TLSListenerConfig +} + // Validate the fields within the TCPListener structure func (h TCPListener) Validate() error { var errs error @@ -629,11 +638,20 @@ func (h TCPListener) Validate() error { if h.Port == 0 { errs = multierror.Append(errs, ErrListenerPortInvalid) } - if h.TLS != nil { - if err := h.TLS.Validate(); err != nil { + if h.TLS != nil && h.TLS.Passthrough != nil { + if err := h.TLS.Passthrough.Validate(); err != nil { errs = multierror.Append(errs, err) } } + + if h.TLS != nil && h.TLS.Terminate != nil { + for t := range h.TLS.Terminate { + if err := h.TLS.Terminate[t].Validate(); err != nil { + errs = multierror.Append(errs, err) + } + } + } + for _, route := range h.Destinations { if err := route.Validate(); err != nil { errs = multierror.Append(errs, err) diff --git a/internal/ir/xds_test.go b/internal/ir/xds_test.go index dca8dffeb5b..374b3e7f4de 100644 --- a/internal/ir/xds_test.go +++ b/internal/ir/xds_test.go @@ -69,9 +69,22 @@ var ( Name: "happy", Address: "0.0.0.0", Port: 80, - TLS: &TLSInspectorConfig{SNIs: []string{"example.com"}}, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, Destinations: []*RouteDestination{&happyRouteDestination}, } + + happyTCPListenerTLSTerminate = TCPListener{ + Name: "happy", + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Terminate: []*TLSListenerConfig{{ + Name: "happy", + ServerCertificate: []byte("server-cert"), + PrivateKey: []byte("priv-key"), + }}}, + Destinations: []*RouteDestination{&happyRouteDestination}, + } + emptySNITCPListenerTLSPassthrough = TCPListener{ Name: "empty-sni", Address: "0.0.0.0", @@ -81,20 +94,20 @@ var ( invalidNameTCPListenerTLSPassthrough = TCPListener{ Address: "0.0.0.0", Port: 80, - TLS: &TLSInspectorConfig{SNIs: []string{"example.com"}}, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, Destinations: []*RouteDestination{&happyRouteDestination}, } invalidAddrTCPListenerTLSPassthrough = TCPListener{ Name: "invalid-addr", Address: "1.0.0", Port: 80, - TLS: &TLSInspectorConfig{SNIs: []string{"example.com"}}, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, Destinations: []*RouteDestination{&happyRouteDestination}, } invalidSNITCPListenerTLSPassthrough = TCPListener{ Address: "0.0.0.0", Port: 80, - TLS: &TLSInspectorConfig{SNIs: []string{}}, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{}}}, Destinations: []*RouteDestination{&happyRouteDestination}, } @@ -469,12 +482,19 @@ func TestValidateXds(t *testing.T) { want: nil, }, { - name: "happy tls", + name: "happy tls passthrough", input: Xds{ TCP: []*TCPListener{&happyTCPListenerTLSPassthrough}, }, want: nil, }, + { + name: "happy tls terminate", + input: Xds{ + TCP: []*TCPListener{&happyTCPListenerTLSTerminate}, + }, + want: nil, + }, { name: "invalid listener", input: Xds{ diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index d4652d6031f..5db9e6637b5 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -632,7 +632,7 @@ func (in *TCPListener) DeepCopyInto(out *TCPListener) { *out = *in if in.TLS != nil { in, out := &in.TLS, &out.TLS - *out = new(TLSInspectorConfig) + *out = new(TLS) (*in).DeepCopyInto(*out) } if in.Destinations != nil { @@ -658,6 +658,37 @@ func (in *TCPListener) DeepCopy() *TCPListener { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLS) DeepCopyInto(out *TLS) { + *out = *in + if in.Passthrough != nil { + in, out := &in.Passthrough, &out.Passthrough + *out = new(TLSInspectorConfig) + (*in).DeepCopyInto(*out) + } + if in.Terminate != nil { + in, out := &in.Terminate, &out.Terminate + *out = make([]*TLSListenerConfig, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TLSListenerConfig) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS. +func (in *TLS) DeepCopy() *TLS { + if in == nil { + return nil + } + out := new(TLS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSInspectorConfig) DeepCopyInto(out *TLSInspectorConfig) { *out = *in diff --git a/internal/provider/kubernetes/helpers.go b/internal/provider/kubernetes/helpers.go index 734a9d793ed..d96f17170c3 100644 --- a/internal/provider/kubernetes/helpers.go +++ b/internal/provider/kubernetes/helpers.go @@ -181,7 +181,8 @@ func gatewaysOfClass(gc *gwapiv1b1.GatewayClass, gwList *gwapiv1b1.GatewayList) // for TLS termination. func terminatesTLS(listener *gwapiv1b1.Listener) bool { if listener.TLS != nil && - listener.Protocol == gwapiv1b1.HTTPSProtocolType && + (listener.Protocol == gwapiv1b1.HTTPSProtocolType || + listener.Protocol == gwapiv1b1.TLSProtocolType) && listener.TLS.Mode != nil && *listener.TLS.Mode == gwapiv1b1.TLSModeTerminate { return true diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go index 2d6a8563e81..8fd0ce56f50 100644 --- a/internal/xds/translator/listener.go +++ b/internal/xds/translator/listener.go @@ -246,11 +246,17 @@ func addXdsTCPFilterChain(xdsListener *listenerv3.Listener, irListener *ir.TCPLi return errors.New("tcp listener is nil") } + isTLSPassthrough := irListener.TLS != nil && irListener.TLS.Passthrough != nil + isTLSTerminate := irListener.TLS != nil && irListener.TLS.Terminate != nil statPrefix := "tcp" - if irListener.TLS != nil { + if isTLSPassthrough { statPrefix = "passthrough" } + if isTLSTerminate { + statPrefix = "terminate" + } + accesslogAny, err := anypb.New(stdoutFileAccessLog) if err != nil { return err @@ -282,10 +288,18 @@ func addXdsTCPFilterChain(xdsListener *listenerv3.Listener, irListener *ir.TCPLi }}, } - if irListener.TLS != nil { - if err := addServerNamesMatch(xdsListener, filterChain, irListener.TLS.SNIs); err != nil { + if isTLSPassthrough { + if err := addServerNamesMatch(xdsListener, filterChain, irListener.TLS.Passthrough.SNIs); err != nil { + return err + } + } + + if isTLSTerminate { + tSocket, err := buildXdsDownstreamTLSSocket(irListener.TLS.Terminate) + if err != nil { return err } + filterChain.TransportSocket = tSocket } xdsListener.FilterChains = append(xdsListener.FilterChains, filterChain) diff --git a/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml b/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml index 396cf06ec83..746c06db4b1 100644 --- a/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml @@ -56,8 +56,9 @@ tcp: address: "0.0.0.0" port: 10080 tls: - snis: - - bar.com + passthrough: + snis: + - bar.com destinations: - host: "1.2.3.4" port: 50000 @@ -65,8 +66,9 @@ tcp: address: "0.0.0.0" port: 10080 tls: - snis: - - bar.net + passthrough: + snis: + - bar.net destinations: - host: "1.2.3.4" port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml index 1de28530c7d..f4f0afdecad 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml @@ -3,10 +3,11 @@ tcp: address: "0.0.0.0" port: 10080 tls: - snis: - - foo.com - - bar.com - - example.com + passthrough: + snis: + - foo.com + - bar.com + - example.com destinations: - host: "1.2.3.4" port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml new file mode 100644 index 00000000000..281b171dc87 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml @@ -0,0 +1,14 @@ +tcp: +- name: "tls-terminate" + address: "0.0.0.0" + port: 10080 + tls: + terminate: + - Name: envoy-gateway-tls-secret-1 + PrivateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + ServerCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + destinations: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml index fe02dec82c7..f983764b3f9 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml @@ -3,10 +3,11 @@ tcp: address: "0.0.0.0" port: 10080 tls: - snis: - - foo.com - - bar.com - - example.com + passthrough: + snis: + - foo.com + - bar.com + - example.com destinations: - host: "1.1.1.1" port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml b/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml index c7f59633067..40b409f59f4 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml @@ -3,8 +3,9 @@ tcp: address: "0.0.0.0" port: 10080 tls: - snis: - - foo.com + passthrough: + snis: + - foo.com destinations: - host: "1.2.3.4" port: 50000 diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml new file mode 100644 index 00000000000..87243f47d0d --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml @@ -0,0 +1,13 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-terminate + name: tls-terminate + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml new file mode 100644 index 00000000000..bcc2a6bc1d8 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml @@ -0,0 +1,15 @@ +- clusterName: tls-terminate + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml new file mode 100644 index 00000000000..f16fb74c120 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml @@ -0,0 +1,37 @@ +- accessLog: + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/stdout + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/stdout + cluster: tls-terminate + statPrefix: terminate + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + tlsCertificateSdsSecretConfigs: + - name: envoy-gateway-tls-secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: tls-terminate + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.routes.yaml new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go index 5f792e4d9e8..32a74a7dd31 100644 --- a/internal/xds/translator/translator.go +++ b/internal/xds/translator/translator.go @@ -207,6 +207,12 @@ func processTCPListenerXdsTranslation(tCtx *types.ResourceVersionTable, tcpListe endpoint: Static, }) + if tcpListener.TLS != nil && tcpListener.TLS.Terminate != nil { + for _, s := range tcpListener.TLS.Terminate { + secret := buildXdsDownstreamTLSSecret(s) + tCtx.AddXdsResource(resourcev3.SecretType, secret) + } + } // Search for an existing listener, if it does not exist, create one. xdsListener := findXdsListener(tCtx, tcpListener.Address, tcpListener.Port, corev3.SocketAddress_TCP) if xdsListener == nil { diff --git a/internal/xds/translator/translator_test.go b/internal/xds/translator/translator_test.go index 80eb0a51d45..fd84ecbf30c 100644 --- a/internal/xds/translator/translator_test.go +++ b/internal/xds/translator/translator_test.go @@ -85,6 +85,9 @@ func TestTranslateXds(t *testing.T) { { name: "tcp-route-complex", }, + { + name: "tcp-route-tls-terminate", + }, { name: "multiple-simple-tcp-route-same-port", },