Support Gateway backend client certificate

This commit is contained in:
Nicolas Mengin authored and GitHub committed 2026-10-26 17:18:19 +01:00
1 parent f1f227eda7
commit b46d3ea648
19 files changed
+1711 -46

No files matched your search

@@ -140,6 +140,42 @@ spec:
from: Same
```
### Backend Client Certificate
The `tls.backend.clientCertificateRef` field on a `Gateway` (`spec.tls.backend`, a sibling of `spec.listeners`) sets the client certificate that Traefik presents to backends that require mutual TLS (mTLS), as defined by [GEP-3155](https://gateway-api.sigs.k8s.io/geps/gep-3155/).
Traefik presents the certificate to the backends selected by a [BackendTLSPolicy](https://gateway-api.sigs.k8s.io/reference/api-types/policy/backendtlspolicy/), for `HTTPRoute` and `GRPCRoute` routes.
Backends without a `BackendTLSPolicy` are not affected, because the field does not enable TLS by itself.
The listeners of a `ListenerSet` use the certificate of their parent `Gateway`.
The reference must point to a core `Secret` that contains the `tls.crt` and `tls.key` entries.
A `Secret` in another namespace requires a [ReferenceGrant](https://gateway-api.sigs.k8s.io/reference/api-types/referencegrant/) in that namespace.
```yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mtls-gateway
namespace: default
spec:
gatewayClassName: traefik
tls:
backend:
# Presented to every backend selected by a BackendTLSPolicy.
clientCertificateRef:
name: backend-client-tls
# Set namespace only for a Secret in another namespace, which requires a ReferenceGrant.
# namespace: certs
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
```
### ListenerSets
Traefik supports [ListenerSet](https://gateway-api.sigs.k8s.io/geps/gep-1713/) resources, which attach additional listeners to an existing `Gateway`.
Generated
+22 -5
View File
@@ -15,17 +15,17 @@
},
"nixpkgs-golangci": {
"locked": {
"lastModified": 1771861243,
"narHash": "sha256-mz05b1VyLWoTkNg8iJr3yXFJF7HDlwIUKI3ln39RacY=",
"lastModified": 1790981744,
"narHash": "sha256-sm6DclXJudZfP/pcDBQQsRqyMxBcQsuw+7yQr4rBBLE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "80d901ec0377e19ac3f7bb8c035201e2e098cc97",
"rev": "55ba7f49ef2962b42cbd126522b7df5f95037679",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "80d901ec0377e19ac3f7bb8c035201e2e098cc97",
"rev": "55ba7f49ef2962b42cbd126522b7df5f95037679",
"type": "github"
}
},
@@ -45,11 +45,28 @@
"type": "github"
}
},
"nixpkgs-shellcheck": {
"locked": {
"lastModified": 1790981744,
"narHash": "sha256-sm6DclXJudZfP/pcDBQQsRqyMxBcQsuw+7yQr4rBBLE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "55ba7f49ef2962b42cbd126522b7df5f95037679",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "55ba7f49ef2962b42cbd126522b7df5f95037679",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"nixpkgs-golangci": "nixpkgs-golangci",
"nixpkgs-kct": "nixpkgs-kct"
"nixpkgs-kct": "nixpkgs-kct",
"nixpkgs-shellcheck": "nixpkgs-shellcheck"
}
}
},
+68 -5
View File
@@ -5,13 +5,17 @@
# Main nixpkgs (used for gnused)
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
# Pinned nixpkgs for kubernetes-controller-tools
# Pinned nixpkgs for kubernetes-controller-tools (0.21.0)
# Search: https://www.nixhub.io/packages/kubernetes-controller-tools
nixpkgs-kct.url = "github:NixOS/nixpkgs/c27cdad491a991b11ed731760aa2ef8db0cb0410";
# Pinned nixpkgs for golangci-lint
# Pinned nixpkgs for golangci-lint (2.14.0)
# Search: https://www.nixhub.io/packages/golangci-lint
nixpkgs-golangci.url = "github:NixOS/nixpkgs/80d901ec0377e19ac3f7bb8c035201e2e098cc97";
nixpkgs-golangci.url = "github:NixOS/nixpkgs/55ba7f49ef2962b42cbd126522b7df5f95037679";
# Pinned nixpkgs for shellcheck (0.11.0)
# Search: https://www.nixhub.io/packages/shellcheck
nixpkgs-shellcheck.url = "github:NixOS/nixpkgs/55ba7f49ef2962b42cbd126522b7df5f95037679";
};
outputs =
@@ -19,24 +23,83 @@
nixpkgs,
nixpkgs-kct,
nixpkgs-golangci,
nixpkgs-shellcheck,
...
}:
let
inherit (nixpkgs.lib) genAttrs;
inherit (nixpkgs.lib) genAttrs attrNames;
forEachSystem = genAttrs nixpkgs.lib.systems.flakeExposed;
pkgsForEach = nixpkgs.legacyPackages;
pkgsKctForEach = nixpkgs-kct.legacyPackages;
pkgsGolangCiForEach = nixpkgs-golangci.legacyPackages;
pkgsShellcheckForEach = nixpkgs-shellcheck.legacyPackages;
# The CI uses the latest Go patch release (setup-go with check-latest),
# which nixpkgs does not always provide yet, so the official binary is used.
# Hashes: https://go.dev/dl/?mode=json&include=all
goVersion = "1.27.3";
goBinaries = {
x86_64-linux = {
platform = "linux-amd64";
sha256 = "65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40";
};
aarch64-linux = {
platform = "linux-arm64";
sha256 = "a06e99309dbcbdca1a8ccd2282c3ee146e55930886e26b4f1193269587165f49";
};
x86_64-darwin = {
platform = "darwin-amd64";
sha256 = "b6f8e931e4ab497fcde6da8ae4499a58f9d258e55c8a80824a516eae5950a750";
};
aarch64-darwin = {
platform = "darwin-arm64";
sha256 = "dd3908cf20959d798c1e06ebe5b72213f0505ce01373b40ce3110975a6cd99c2";
};
};
goForEach =
system:
let
pkgs = pkgsForEach.${system};
bin = goBinaries.${system};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "go-bin";
version = goVersion;
src = pkgs.fetchurl {
url = "https://go.dev/dl/go${goVersion}.${bin.platform}.tar.gz";
inherit (bin) sha256;
};
# The official binaries are used as is, fixup would alter them.
dontConfigure = true;
dontBuild = true;
dontFixup = true;
installPhase = ''
runHook preInstall
mkdir -p $out/share $out/bin
cp -r . $out/share/go
ln -s $out/share/go/bin/go $out/share/go/bin/gofmt $out/bin/
runHook postInstall
'';
};
in
{
devShells = forEachSystem (system: {
devShells = genAttrs (attrNames goBinaries) (system: {
default = pkgsForEach.${system}.mkShell {
packages = [
pkgsForEach.${system}.gnused
(goForEach system)
pkgsKctForEach.${system}.kubernetes-controller-tools
pkgsGolangCiForEach.${system}.golangci-lint
pkgsShellcheckForEach.${system}.shellcheck
];
# Matches the CI workflows (build, integration and conformance tests).
CGO_ENABLED = "0";
};
});
@@ -1,5 +1,5 @@
apiVersion: gateway.networking.k8s.io/v1
date: "2026-10-26T10:33:25+01:00"
date: "2026-10-26T16:48:00+01:00"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.7.0-rc.3
implementation:
@@ -28,6 +28,7 @@ profiles:
- BackendTLSPolicy
- BackendTLSPolicySANValidation
- GatewayAddressEmpty
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -40,7 +41,6 @@ profiles:
- ClusterTrustBundle
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
name: GATEWAY-GRPC
@@ -55,12 +55,13 @@ profiles:
result: success
statistics:
Failed: 0
Passed: 54
Passed: 56
Skipped: 0
supportedFeatures:
- BackendTLSPolicy
- BackendTLSPolicySANValidation
- GatewayAddressEmpty
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -94,7 +95,6 @@ profiles:
- ClusterTrustBundle
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
- HTTPRouteRequestMirror
@@ -118,6 +118,7 @@ profiles:
Skipped: 0
supportedFeatures:
- GatewayAddressEmpty
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -131,7 +132,6 @@ profiles:
- ClusterTrustBundle
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
name: GATEWAY-TCP
@@ -152,6 +152,7 @@ profiles:
- BackendTLSPolicy
- BackendTLSPolicySANValidation
- GatewayAddressEmpty
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -166,7 +167,6 @@ profiles:
- ClusterTrustBundle
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
name: GATEWAY-TLS
@@ -1,5 +1,5 @@
apiVersion: gateway.networking.k8s.io/v1
date: "2026-10-26T10:40:18+01:00"
date: "2026-10-26T16:50:54+01:00"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.7.0-rc.3
implementation:
@@ -31,6 +31,7 @@ profiles:
supportedFeatures:
- BackendTLSPolicy
- BackendTLSPolicySANValidation
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -43,7 +44,6 @@ profiles:
- GatewayAddressEmpty
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
- GatewayStaticAddresses
@@ -68,11 +68,12 @@ profiles:
- GatewayInvalidFrontendClientCertificateValidation
statistics:
Failed: 0
Passed: 47
Passed: 49
Skipped: 5
supportedFeatures:
- BackendTLSPolicy
- BackendTLSPolicySANValidation
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -106,7 +107,6 @@ profiles:
- GatewayAddressEmpty
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
- GatewayStaticAddresses
@@ -133,6 +133,7 @@ profiles:
Passed: 8
Skipped: 1
supportedFeatures:
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -146,7 +147,6 @@ profiles:
- GatewayAddressEmpty
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
- GatewayStaticAddresses
@@ -171,6 +171,7 @@ profiles:
supportedFeatures:
- BackendTLSPolicy
- BackendTLSPolicySANValidation
- GatewayBackendClientCertificate
- GatewayFrontendClientCertificateValidation
- GatewayFrontendClientCertificateValidationInsecureFallback
- GatewayHTTPListenerIsolation
@@ -185,7 +186,6 @@ profiles:
- GatewayAddressEmpty
- GatewayAddressRoutability
- GatewayAddressRoutabilityCluster
- GatewayBackendClientCertificate
- GatewayListenerFilters
- GatewayRouteHostnameIntersectionPrecedence
- GatewayStaticAddresses
@@ -829,6 +829,7 @@ var (
gatewayConditionTypes = []string{
string(gatev1.GatewayConditionAccepted),
string(gatev1.GatewayConditionProgrammed),
string(gatev1.GatewayConditionResolvedRefs),
}
listenerConditionTypes = []string{
string(gatev1.ListenerConditionAccepted),
@@ -398,6 +398,7 @@ func Test_listenerSetStatusEqual(t *testing.T) {
func Test_mergeGatewayStatus(t *testing.T) {
accepted := metav1.Condition{Type: string(gatev1.GatewayConditionAccepted), Status: metav1.ConditionTrue}
staleAccepted := metav1.Condition{Type: string(gatev1.GatewayConditionAccepted), Status: metav1.ConditionFalse}
staleResolvedRefs := metav1.Condition{Type: string(gatev1.GatewayConditionResolvedRefs), Status: metav1.ConditionFalse}
listenerAccepted := metav1.Condition{Type: string(gatev1.ListenerConditionAccepted), Status: metav1.ConditionTrue}
staleConflicted := metav1.Condition{Type: string(gatev1.ListenerConditionConflicted), Status: metav1.ConditionTrue}
foreign := metav1.Condition{Type: "example.com/Foreign", Status: metav1.ConditionTrue}
@@ -429,6 +430,13 @@ func Test_mergeGatewayStatus(t *testing.T) {
Listeners: desired.Listeners,
},
},
{
desc: "drops owned Gateway condition no longer desired",
current: gatev1.GatewayStatus{
Conditions: []metav1.Condition{staleAccepted, staleResolvedRefs},
},
expected: desired,
},
{
desc: "keeps foreign listener condition",
current: gatev1.GatewayStatus{
@@ -36,6 +36,7 @@ func extendedGatewayFeatures() sets.Set[features.Feature] {
features.GatewayHTTPListenerIsolationFeature,
features.GatewayHTTPSListenerDetectMisdirectedRequestsFeature,
features.ListenerSetFeature,
features.GatewayBackendClientCertificateFeature,
features.GatewayFrontendClientCertificateValidationFeature,
features.GatewayFrontendClientCertificateValidationInsecureFallbackFeature,
)
@@ -0,0 +1,57 @@
---
apiVersion: v1
kind: Secret
metadata:
name: client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
---
apiVersion: v1
kind: Secret
metadata:
name: malformed-cert
namespace: default
type: Opaque
data: {}
---
apiVersion: v1
kind: Secret
metadata:
name: client-cert
namespace: bar
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJwVENDQVV1Z0F3SUJBZ0lVVEVrVUVNQ21acnh4aUVrQ20wUFMrRnF1dXNRd0NnWUlLb1pJemowRUF3SXcKR2pFWU1CWUdBMVVFQXd3UFltRnlMbVY0WVcxd2JHVXVZMjl0TUI0WERUSTJNRGt4TlRFeE5ETXlORm9YRFRNMgpNRGt4TWpFeE5ETXlORm93R2pFWU1CWUdBMVVFQXd3UFltRnlMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJCnpqMENBUVlJS29aSXpqMERBUWNEUWdBRWJNdytOdEtlSEVuUUxsR0dLV04wQVUySGJ6ckcraTBYVlRMMEFqaTUKaXc2dTlsR1RiNENZQ21xTENzVmF0Q3R6UE0vRVk1dmJadXJ3SlVGVGxmM3lSS052TUcwd0hRWURWUjBPQkJZRQpGTCsvb1lFMkJiRkl1OTBuNjg1bXFEK1E0cFRVTUI4R0ExVWRJd1FZTUJhQUZMKy9vWUUyQmJGSXU5MG42ODVtCnFEK1E0cFRVTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3R2dZRFZSMFJCQk13RVlJUFltRnlMbVY0WVcxd2JHVXUKWTI5dE1Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lRREN0UTRzRGtkU0hRZVRYSk44a3BwTmxtNDBWQ2pxOEx2ZAppWGszR0NYbzhBSWdiczUxeVgybHQxZXBwZEhHRnBuNGRyV3g5dDV0UGNEZGR3U1lVUzdPQkpBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ09Uc2V0ZkxqTDNFbnVrK2gKbDNjV1dKSHZSUTE0UWx5QkI4Y0FycmNwVldHaFJBTkNBQVJzekQ0MjBwNGNTZEF1VVlZcFkzUUJUWWR2T3NiNgpMUmRWTXZRQ09MbUxEcTcyVVpOdmdKZ0thb3NLeFZxMEszTTh6OFJqbTl0bTZ2QWxRVk9WL2ZKRQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==
---
apiVersion: v1
kind: Secret
metadata:
name: ungranted-cert
namespace: bar
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJwVENDQVV1Z0F3SUJBZ0lVVEVrVUVNQ21acnh4aUVrQ20wUFMrRnF1dXNRd0NnWUlLb1pJemowRUF3SXcKR2pFWU1CWUdBMVVFQXd3UFltRnlMbVY0WVcxd2JHVXVZMjl0TUI0WERUSTJNRGt4TlRFeE5ETXlORm9YRFRNMgpNRGt4TWpFeE5ETXlORm93R2pFWU1CWUdBMVVFQXd3UFltRnlMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJCnpqMENBUVlJS29aSXpqMERBUWNEUWdBRWJNdytOdEtlSEVuUUxsR0dLV04wQVUySGJ6ckcraTBYVlRMMEFqaTUKaXc2dTlsR1RiNENZQ21xTENzVmF0Q3R6UE0vRVk1dmJadXJ3SlVGVGxmM3lSS052TUcwd0hRWURWUjBPQkJZRQpGTCsvb1lFMkJiRkl1OTBuNjg1bXFEK1E0cFRVTUI4R0ExVWRJd1FZTUJhQUZMKy9vWUUyQmJGSXU5MG42ODVtCnFEK1E0cFRVTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3R2dZRFZSMFJCQk13RVlJUFltRnlMbVY0WVcxd2JHVXUKWTI5dE1Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lRREN0UTRzRGtkU0hRZVRYSk44a3BwTmxtNDBWQ2pxOEx2ZAppWGszR0NYbzhBSWdiczUxeVgybHQxZXBwZEhHRnBuNGRyV3g5dDV0UGNEZGR3U1lVUzdPQkpBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ09Uc2V0ZkxqTDNFbnVrK2gKbDNjV1dKSHZSUTE0UWx5QkI4Y0FycmNwVldHaFJBTkNBQVJzekQ0MjBwNGNTZEF1VVlZcFkzUUJUWWR2T3NiNgpMUmRWTXZRQ09MbUxEcTcyVVpOdmdKZ0thb3NLeFZxMEszTTh6OFJqbTl0bTZ2QWxRVk9WL2ZKRQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==
---
kind: ReferenceGrant
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: secret-from-default
namespace: bar
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: default
to:
- group: ""
kind: Secret
name: client-cert
@@ -0,0 +1,89 @@
---
apiVersion: v1
kind: Secret
metadata:
name: client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners:
- name: web
protocol: HTTP
port: 80
allowedRoutes:
kinds:
- kind: GRPCRoute
group: gateway.networking.k8s.io
namespaces:
from: Same
tls:
backend:
clientCertificateRef:
group: ""
kind: Secret
name: client-cert
---
kind: GRPCRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: grpc-app-1
namespace: default
spec:
parentRefs:
- name: my-gateway
kind: Gateway
group: gateway.networking.k8s.io
hostnames:
- foo.com
rules:
- backendRefs:
- name: whoami
port: 80
weight: 1
---
kind: BackendTLSPolicy
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: policy-1
namespace: default
spec:
targetRefs:
- group: ""
kind: Service
name: whoami
validation:
hostname: whoami
caCertificateRefs:
- group: ""
kind: ConfigMap
name: ca-file
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-file
namespace: default
data:
ca.crt: "CA1"
@@ -0,0 +1,102 @@
---
apiVersion: v1
kind: Secret
metadata:
name: client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
tls:
backend:
clientCertificateRef:
group: ""
kind: Secret
name: client-cert
---
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: http-app-1
namespace: default
spec:
parentRefs:
- name: my-gateway
kind: Gateway
group: gateway.networking.k8s.io
hostnames:
- "foo.com"
rules:
- matches:
- path:
type: Exact
value: /bar
backendRefs:
- name: whoami
port: 80
weight: 1
kind: Service
group: ""
- matches:
- path:
type: Exact
value: /foo
backendRefs:
- name: whoami2
port: 8080
weight: 1
kind: Service
group: ""
---
kind: BackendTLSPolicy
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: policy-1
namespace: default
spec:
targetRefs:
- group: ""
kind: Service
name: whoami
validation:
hostname: whoami
caCertificateRefs:
- group: ""
kind: ConfigMap
name: ca-file
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-file
namespace: default
data:
ca.crt: "CA1"
@@ -0,0 +1,122 @@
---
apiVersion: v1
kind: Secret
metadata:
name: client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
allowedListeners:
namespaces:
from: All
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
tls:
backend:
clientCertificateRef:
group: ""
kind: Secret
name: client-cert
---
kind: ListenerSet
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-listenerset
namespace: default
spec:
parentRef:
name: my-gateway
namespace: default
kind: Gateway
group: gateway.networking.k8s.io
listeners:
- name: http-alt
protocol: HTTP
port: 8080
---
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: http-app-1
namespace: default
spec:
parentRefs:
- name: my-listenerset
kind: ListenerSet
group: gateway.networking.k8s.io
hostnames:
- "foo.com"
rules:
- matches:
- path:
type: Exact
value: /bar
backendRefs:
- name: whoami
port: 80
weight: 1
kind: Service
group: ""
- matches:
- path:
type: Exact
value: /foo
backendRefs:
- name: whoami2
port: 8080
weight: 1
kind: Service
group: ""
---
kind: BackendTLSPolicy
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: policy-1
namespace: default
spec:
targetRefs:
- group: ""
kind: Service
name: whoami
validation:
hostname: whoami
caCertificateRefs:
- group: ""
kind: ConfigMap
name: ca-file
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-file
namespace: default
data:
ca.crt: "CA1"
@@ -0,0 +1,138 @@
---
apiVersion: v1
kind: Secret
metadata:
name: client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
---
apiVersion: v1
kind: Secret
metadata:
name: other-client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJwVENDQVV1Z0F3SUJBZ0lVVEVrVUVNQ21acnh4aUVrQ20wUFMrRnF1dXNRd0NnWUlLb1pJemowRUF3SXcKR2pFWU1CWUdBMVVFQXd3UFltRnlMbVY0WVcxd2JHVXVZMjl0TUI0WERUSTJNRGt4TlRFeE5ETXlORm9YRFRNMgpNRGt4TWpFeE5ETXlORm93R2pFWU1CWUdBMVVFQXd3UFltRnlMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJCnpqMENBUVlJS29aSXpqMERBUWNEUWdBRWJNdytOdEtlSEVuUUxsR0dLV04wQVUySGJ6ckcraTBYVlRMMEFqaTUKaXc2dTlsR1RiNENZQ21xTENzVmF0Q3R6UE0vRVk1dmJadXJ3SlVGVGxmM3lSS052TUcwd0hRWURWUjBPQkJZRQpGTCsvb1lFMkJiRkl1OTBuNjg1bXFEK1E0cFRVTUI4R0ExVWRJd1FZTUJhQUZMKy9vWUUyQmJGSXU5MG42ODVtCnFEK1E0cFRVTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3R2dZRFZSMFJCQk13RVlJUFltRnlMbVY0WVcxd2JHVXUKWTI5dE1Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lRREN0UTRzRGtkU0hRZVRYSk44a3BwTmxtNDBWQ2pxOEx2ZAppWGszR0NYbzhBSWdiczUxeVgybHQxZXBwZEhHRnBuNGRyV3g5dDV0UGNEZGR3U1lVUzdPQkpBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ09Uc2V0ZkxqTDNFbnVrK2gKbDNjV1dKSHZSUTE0UWx5QkI4Y0FycmNwVldHaFJBTkNBQVJzekQ0MjBwNGNTZEF1VVlZcFkzUUJUWWR2T3NiNgpMUmRWTXZRQ09MbUxEcTcyVVpOdmdKZ0thb3NLeFZxMEszTTh6OFJqbTl0bTZ2QWxRVk9WL2ZKRQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
tls:
backend:
clientCertificateRef:
group: ""
kind: Secret
name: client-cert
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-other-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners:
- name: http
protocol: HTTP
port: 8080
allowedRoutes:
namespaces:
from: Same
tls:
backend:
clientCertificateRef:
group: ""
kind: Secret
name: other-client-cert
---
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: http-app-1
namespace: default
spec:
parentRefs:
- name: my-gateway
kind: Gateway
group: gateway.networking.k8s.io
- name: my-other-gateway
kind: Gateway
group: gateway.networking.k8s.io
hostnames:
- "foo.com"
rules:
- matches:
- path:
type: Exact
value: /bar
backendRefs:
- name: whoami
port: 80
weight: 1
kind: Service
group: ""
- matches:
- path:
type: Exact
value: /foo
backendRefs:
- name: whoami2
port: 8080
weight: 1
kind: Service
group: ""
---
kind: BackendTLSPolicy
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: policy-1
namespace: default
spec:
targetRefs:
- group: ""
kind: Service
name: whoami
validation:
hostname: whoami
caCertificateRefs:
- group: ""
kind: ConfigMap
name: ca-file
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-file
namespace: default
data:
ca.crt: "CA1"
@@ -0,0 +1,105 @@
---
apiVersion: v1
kind: Secret
metadata:
name: supersecret
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
---
apiVersion: v1
kind: Secret
metadata:
name: client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners:
- name: tls
protocol: TLS
port: 9001
hostname: foo.com
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: supersecret
group: ""
allowedRoutes:
kinds:
- kind: TLSRoute
group: gateway.networking.k8s.io
namespaces:
from: Same
tls:
backend:
clientCertificateRef:
group: ""
kind: Secret
name: client-cert
---
kind: TLSRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: tls-app-1
namespace: default
spec:
parentRefs:
- name: my-gateway
kind: Gateway
group: gateway.networking.k8s.io
rules:
- backendRefs:
- name: whoami
port: 80
weight: 1
---
kind: BackendTLSPolicy
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: policy-1
namespace: default
spec:
targetRefs:
- group: ""
kind: Service
name: whoami
validation:
hostname: whoami
caCertificateRefs:
- group: ""
kind: ConfigMap
name: ca-file
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-file
namespace: default
data:
ca.crt: "CA1"
+1 -1
View File
@@ -428,7 +428,7 @@ func (p *Provider) loadGRPCServers(gatewayName, gatewayNamespace, namespace stri
}
var resolvedRefCondition metav1.Condition
serversTransport, resolvedRefCondition = p.loadServersTransport(namespace, policy)
serversTransport, resolvedRefCondition = p.loadServersTransport(namespace, policy, listener.BackendClientCertificates)
policyAncestorStatus.Conditions = append(policyAncestorStatus.Conditions, resolvedRefCondition)
if resolvedRefCondition.Status == metav1.ConditionFalse {
+4 -3
View File
@@ -612,7 +612,7 @@ func (p *Provider) loadHTTPServers(gatewayName, gatewayNamespace, namespace stri
}
var resolvedRefCondition metav1.Condition
serversTransport, resolvedRefCondition = p.loadServersTransport(namespace, policy)
serversTransport, resolvedRefCondition = p.loadServersTransport(namespace, policy, listener.BackendClientCertificates)
policyAncestorStatus.Conditions = append(policyAncestorStatus.Conditions, resolvedRefCondition)
if resolvedRefCondition.Status == metav1.ConditionFalse {
@@ -678,9 +678,10 @@ func (p *Provider) loadHTTPServers(gatewayName, gatewayNamespace, namespace stri
return lb, serversTransport, nil
}
func (p *Provider) loadServersTransport(namespace string, policy *gatev1.BackendTLSPolicy) (*dynamic.ServersTransport, metav1.Condition) {
func (p *Provider) loadServersTransport(namespace string, policy *gatev1.BackendTLSPolicy, clientCertificates tls.Certificates) (*dynamic.ServersTransport, metav1.Condition) {
st := &dynamic.ServersTransport{
ServerName: string(policy.Spec.Validation.Hostname),
ServerName: string(policy.Spec.Validation.Hostname),
Certificates: clientCertificates,
}
if len(policy.Spec.Validation.SubjectAltNames) > 0 {
+118 -16
View File
@@ -28,6 +28,7 @@ import (
"github.com/traefik/traefik/v3/pkg/tls"
"github.com/traefik/traefik/v3/pkg/types"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
ktypes "k8s.io/apimachinery/pkg/types"
@@ -173,11 +174,17 @@ type gatewayListener struct {
// ListenerSet is the ListenerSet declaring this listener, nil when the Gateway declares it itself.
ListenerSet *gatev1.ListenerSet
// BackendClientCertificates are the client certificates the Gateway presents to the backends using TLS.
BackendClientCertificates tls.Certificates
}
type gatewayWithListeners struct {
gateway *gatev1.Gateway
listeners []gatewayListener
// errResolvedRefsCondition is the ResolvedRefs condition reporting an unresolved Gateway own reference,
// nil when they are all resolved.
errResolvedRefsCondition *metav1.Condition
}
// RegisterFilterFuncs registers an allowed Group, Kind, and builder for the Filter ExtensionRef objects.
@@ -472,9 +479,17 @@ func (p *Provider) loadConfigurationFromGateways(ctx context.Context) (*dynamic.
listeners = append(listeners, p.loadListenerSetListeners(logger.WithContext(ctx), gateway, listenerSets, claims, conf, statusReport)...)
// The backend TLS configuration is defined at the Gateway level,
// so the ListenerSet listeners use the one of their parent Gateway.
backendClientCertificates, errResolvedRefsCondition := p.loadGatewayBackendTLS(gateway)
for i := range listeners {
listeners[i].BackendClientCertificates = backendClientCertificates
}
gatewaysWithListeners = append(gatewaysWithListeners, gatewayWithListeners{
gateway: gateway,
listeners: listeners,
gateway: gateway,
listeners: listeners,
errResolvedRefsCondition: errResolvedRefsCondition,
})
}
@@ -508,7 +523,7 @@ func (p *Provider) loadConfigurationFromGateways(ctx context.Context) (*dynamic.
return len(listener.Status.Conditions) == 0
})
gatewayStatus, errConditions := p.makeGatewayStatus(gwl.gateway, gwl.listeners, addresses, accepted)
gatewayStatus, errConditions := p.makeGatewayStatus(gwl, addresses, accepted)
if len(errConditions) > 0 {
messages := map[string]struct{}{}
for _, condition := range errConditions {
@@ -1174,15 +1189,15 @@ func (p *Provider) resolveCACertificateRefs(gateway *gatev1.Gateway, refs []gate
return caCert, nil
}
func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewayListener, addresses []gatev1.GatewayStatusAddress, accepted bool) (gatev1.GatewayStatus, []metav1.Condition) {
func (p *Provider) makeGatewayStatus(gwl gatewayWithListeners, addresses []gatev1.GatewayStatusAddress, accepted bool) (gatev1.GatewayStatus, []metav1.Condition) {
gatewayStatus := gatev1.GatewayStatus{Addresses: addresses}
// The spec requires the InsecureFrontendValidationMode condition when any validation allows an insecure fallback.
if hasInsecureFrontendValidationMode(gateway) {
if hasInsecureFrontendValidationMode(gwl.gateway) {
gatewayStatus.Conditions = append(gatewayStatus.Conditions, metav1.Condition{
Type: string(gatev1.GatewayConditionInsecureFrontendValidationMode),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.GatewayReasonConfigurationChanged),
Message: "FrontendValidationMode is set to AllowInsecureFallback",
@@ -1190,7 +1205,8 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
}
var errorConditions []metav1.Condition
for _, listener := range listeners {
var unresolvedListenerRefs bool
for _, listener := range gwl.listeners {
// The ListenerSet listeners are reported in their own ListenerSet status.
if listener.ListenerSet != nil {
continue
@@ -1201,7 +1217,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
metav1.Condition{
Type: string(gatev1.ListenerConditionAccepted),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.ListenerReasonAccepted),
Message: conditionNoErrorMessage,
@@ -1209,7 +1225,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.ListenerReasonResolvedRefs),
Message: conditionNoErrorMessage,
@@ -1217,7 +1233,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
metav1.Condition{
Type: string(gatev1.ListenerConditionProgrammed),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.ListenerReasonProgrammed),
Message: conditionNoErrorMessage,
@@ -1231,15 +1247,22 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
errorConditions = append(errorConditions, listener.Status.Conditions...)
gatewayStatus.Listeners = append(gatewayStatus.Listeners, *listener.Status)
if meta.IsStatusConditionFalse(listener.Status.Conditions, string(gatev1.ListenerConditionResolvedRefs)) {
unresolvedListenerRefs = true
}
}
// The ResolvedRefs condition does not impact the Accepted and Programmed conditions.
gatewayStatus.Conditions = append(gatewayStatus.Conditions, makeGatewayResolvedRefsCondition(gwl, unresolvedListenerRefs))
// Traefik supports no infrastructure parameters, and the specification requires
// a parametersRef that cannot be resolved to be reported instead of ignored.
if gateway.Spec.Infrastructure != nil && gateway.Spec.Infrastructure.ParametersRef != nil {
if gwl.gateway.Spec.Infrastructure != nil && gwl.gateway.Spec.Infrastructure.ParametersRef != nil {
condition := metav1.Condition{
Type: string(gatev1.GatewayConditionAccepted),
Status: metav1.ConditionFalse,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.GatewayReasonInvalidParameters),
Message: "Gateway infrastructure parametersRef is not supported",
@@ -1255,7 +1278,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
metav1.Condition{
Type: string(gatev1.GatewayConditionAccepted),
Status: metav1.ConditionFalse,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
Reason: string(gatev1.GatewayReasonListenersNotValid),
Message: "At least one Listener must be valid",
LastTransitionTime: metav1.Now(),
@@ -1264,7 +1287,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
metav1.Condition{
Type: string(gatev1.GatewayConditionProgrammed),
Status: metav1.ConditionFalse,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
Reason: string(gatev1.GatewayReasonInvalid),
Message: "No Listener is valid",
LastTransitionTime: metav1.Now(),
@@ -1288,7 +1311,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
metav1.Condition{
Type: string(gatev1.GatewayConditionAccepted),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
Reason: string(acceptedConditionReason),
Message: acceptedConditionMessage,
LastTransitionTime: metav1.Now(),
@@ -1297,7 +1320,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
metav1.Condition{
Type: string(gatev1.GatewayConditionProgrammed),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
ObservedGeneration: gwl.gateway.Generation,
Reason: string(gatev1.GatewayReasonProgrammed),
Message: programmedConditionMessage,
LastTransitionTime: metav1.Now(),
@@ -1307,6 +1330,35 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa
return gatewayStatus, nil
}
// makeGatewayResolvedRefsCondition makes the Gateway ResolvedRefs condition,
// which the specification makes a summary of the listeners ResolvedRefs condition too.
func makeGatewayResolvedRefsCondition(gwl gatewayWithListeners, unresolvedListenerRefs bool) metav1.Condition {
// The Gateway own unresolved references take precedence over the listeners ones.
if gwl.errResolvedRefsCondition != nil {
return *gwl.errResolvedRefsCondition
}
if unresolvedListenerRefs {
return metav1.Condition{
Type: string(gatev1.GatewayConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: gwl.gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.GatewayReasonListenersNotResolved),
Message: "At least one Listener has unresolved references",
}
}
return metav1.Condition{
Type: string(gatev1.GatewayConditionResolvedRefs),
Status: metav1.ConditionTrue,
ObservedGeneration: gwl.gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.GatewayReasonResolvedRefs),
Message: conditionNoErrorMessage,
}
}
func (p *Provider) gatewayAddresses() ([]gatev1.GatewayStatusAddress, error) {
if p.StatusAddress == nil {
return nil, nil
@@ -1392,6 +1444,56 @@ func (p *Provider) isReferenceGranted(fromKind, fromNamespace, toGroup, toKind,
return nil
}
// loadGatewayBackendTLS resolves the client certificate the Gateway presents to the backends using TLS.
// It returns no certificate when the Gateway does not define one,
// and the ResolvedRefs condition to report when the reference cannot be resolved.
func (p *Provider) loadGatewayBackendTLS(gateway *gatev1.Gateway) (tls.Certificates, *metav1.Condition) {
if gateway.Spec.TLS == nil || gateway.Spec.TLS.Backend == nil || gateway.Spec.TLS.Backend.ClientCertificateRef == nil {
return nil, nil
}
certRef := gateway.Spec.TLS.Backend.ClientCertificateRef
group := ptr.Deref(certRef.Group, "")
kind := ptr.Deref(certRef.Kind, kindSecret)
if (group != "" && group != groupCore) || kind != kindSecret {
return nil, &metav1.Condition{
Type: string(gatev1.GatewayConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.GatewayReasonInvalidClientCertificateRef),
Message: fmt.Sprintf("Unsupported ClientCertificateRef group/kind: %s/%s", group, kind),
}
}
namespace := string(ptr.Deref(certRef.Namespace, gatev1.Namespace(gateway.Namespace)))
if err := p.isReferenceGranted(kindGateway, gateway.Namespace, groupCore, kindSecret, string(certRef.Name), namespace); err != nil {
return nil, &metav1.Condition{
Type: string(gatev1.GatewayConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.GatewayReasonRefNotPermitted),
Message: fmt.Sprintf("Cannot reference ClientCertificateRef %s/%s: %s", namespace, certRef.Name, err),
}
}
cert, err := p.getTLSCert(certRef.Name, namespace)
if err != nil {
return nil, &metav1.Condition{
Type: string(gatev1.GatewayConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: gateway.Generation,
LastTransitionTime: metav1.Now(),
Reason: string(gatev1.GatewayReasonInvalidClientCertificateRef),
Message: fmt.Sprintf("Cannot load ClientCertificateRef %s/%s: %s", namespace, certRef.Name, err),
}
}
return tls.Certificates{cert.Certificate}, nil
}
func (p *Provider) getTLSCert(secretName gatev1.ObjectName, namespace string) (*tls.CertAndStores, error) {
secret, err := p.client.GetSecret(namespace, string(secretName))
if err != nil {
@@ -66,6 +66,24 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgnwgL5DY4UB14sM6f
DikQdtqh2QW1ArfF4fc1UFzifdGhRANCAAQzq4sN2UO8CIVq2z0uiRUE9206DQhd
NL0leX2m+k218i/LZbBq3k0SBdhMILLXjDpMRiikpQ77mg8KvKf6lftL
-----END PRIVATE KEY-----`
otherClientCert string = `-----BEGIN CERTIFICATE-----
MIIBpTCCAUugAwIBAgIUTEkUEMCmZrxxiEkCm0PS+FquusQwCgYIKoZIzj0EAwIw
GjEYMBYGA1UEAwwPYmFyLmV4YW1wbGUuY29tMB4XDTI2MDkxNTExNDMyNFoXDTM2
MDkxMjExNDMyNFowGjEYMBYGA1UEAwwPYmFyLmV4YW1wbGUuY29tMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAEbMw+NtKeHEnQLlGGKWN0AU2HbzrG+i0XVTL0Aji5
iw6u9lGTb4CYCmqLCsVatCtzPM/EY5vbZurwJUFTlf3yRKNvMG0wHQYDVR0OBBYE
FL+/oYE2BbFIu90n685mqD+Q4pTUMB8GA1UdIwQYMBaAFL+/oYE2BbFIu90n685m
qD+Q4pTUMA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0RBBMwEYIPYmFyLmV4YW1wbGUu
Y29tMAoGCCqGSM49BAMCA0gAMEUCIQDCtQ4sDkdSHQeTXJN8kppNlm40VCjq8Lvd
iXk3GCXo8AIgbs51yX2lt1eppdHGFpn4drWx9t5tPcDddwSYUS7OBJA=
-----END CERTIFICATE-----
`
otherClientKey string = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgOTsetfLjL3Enuk+h
l3cWWJHvRQ14QlyBB8cArrcpVWGhRANCAARszD420p4cSdAuUYYpY3QBTYdvOsb6
LRdVMvQCOLmLDq72UZNvgJgKaosKxVq0K3M8z8Rjm9tm6vAlQVOV/fJE
-----END PRIVATE KEY-----
`
)
func TestGatewayClassLabelSelector(t *testing.T) {
@@ -3429,6 +3447,441 @@ func TestLoadHTTPRoutes(t *testing.T) {
},
},
},
{
desc: "HTTPRoute and BackendTLSPolicy with Gateway backend client certificate",
paths: []string{"services.yml", "httproute/with_gateway_backend_client_certificate.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100009,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-wrr",
Rule: `Host("foo.com") && Path("/foo")`,
Priority: 100008,
RuleSyntax: "default",
},
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-svc-default-whoami2-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-svc-default-whoami2-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
},
Certificates: tls.Certificates{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute and BackendTLSPolicy attached to two Gateways with different backend client certificates",
paths: []string{"services.yml", "httproute/with_gateway_backend_client_certificate_two_gateways.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"web-alt": {Address: ":8080"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100009,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-wrr",
Rule: `Host("foo.com") && Path("/foo")`,
Priority: 100008,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-0-8643b4eec05412c1ca31": {
ParentRefs: []string{"listener-web-alt-http-f9685282dacd58da3164"},
Service: "httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-0-8643b4eec05412c1ca31-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100009,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-1-598fb9b61886d4edc744": {
ParentRefs: []string{"listener-web-alt-http-f9685282dacd58da3164"},
Service: "httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-1-598fb9b61886d4edc744-wrr",
Rule: `Host("foo.com") && Path("/foo")`,
Priority: 100008,
RuleSyntax: "default",
},
"listener-web-alt-http-f9685282dacd58da3164": {
EntryPoints: []string{"web-alt"},
Rule: `Host("*")`,
},
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-svc-default-whoami2-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-37f0249f693ead70647a-svc-default-whoami2-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-0-8643b4eec05412c1ca31-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-0-8643b4eec05412c1ca31-svc-default-whoami-0",
},
},
"httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-0-8643b4eec05412c1ca31-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-0-8643b4eec05412c1ca31-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-1-598fb9b61886d4edc744-svc-default-whoami2-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-1-598fb9b61886d4edc744-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-1-598fb9b61886d4edc744-svc-default-whoami2-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
},
Certificates: tls.Certificates{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
"httproute-default-http-app-1-gw-default-my-other-gateway-ep-web-alt-0-8643b4eec05412c1ca31-svc-default-whoami-0": {
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
},
Certificates: tls.Certificates{
{
CertFile: types.FileOrContent(otherClientCert),
KeyFile: types.FileOrContent(otherClientKey),
},
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute and BackendTLSPolicy attached to a ListenerSet with Gateway backend client certificate",
paths: []string{"services.yml", "httproute/with_gateway_backend_client_certificate_listenerset.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"web-alt": {Address: ":8080"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-0-6d65ece8c8e5c1272ab8": {
ParentRefs: []string{"listener-web-alt-http-f9685282dacd58da3164"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-0-6d65ece8c8e5c1272ab8-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100009,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-1-cc507dc61ee70b0f1b77": {
ParentRefs: []string{"listener-web-alt-http-f9685282dacd58da3164"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-1-cc507dc61ee70b0f1b77-wrr",
Rule: `Host("foo.com") && Path("/foo")`,
Priority: 100008,
RuleSyntax: "default",
},
"listener-web-alt-http-f9685282dacd58da3164": {
EntryPoints: []string{"web-alt"},
Rule: `Host("*")`,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-0-6d65ece8c8e5c1272ab8-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-0-6d65ece8c8e5c1272ab8-svc-default-whoami-0",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-0-6d65ece8c8e5c1272ab8-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-0-6d65ece8c8e5c1272ab8-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-1-cc507dc61ee70b0f1b77-svc-default-whoami2-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-1-cc507dc61ee70b0f1b77-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-1-cc507dc61ee70b0f1b77-svc-default-whoami2-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-alt-0-6d65ece8c8e5c1272ab8-svc-default-whoami-0": {
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
},
Certificates: tls.Certificates{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute and BackendTLSPolicy with subjectAltNames",
paths: []string{"services.yml", "httproute/with_backend_tls_policy_san.yml"},
@@ -5402,6 +5855,88 @@ func TestLoadGRPCRoutes(t *testing.T) {
},
},
},
{
desc: "GRPCRoute and BackendTLSPolicy with Gateway backend client certificate",
paths: []string{"services.yml", "grpcroute/with_gateway_backend_client_certificate.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
},
Certificates: tls.Certificates{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple GRPCRoute and BackendTLSPolicy with subjectAltNames",
paths: []string{"services.yml", "grpcroute/with_backend_tls_policy_san.yml"},
@@ -9068,6 +9603,99 @@ func TestLoadTLSRoutes(t *testing.T) {
},
},
},
{
desc: "TLSRoute and BackendTLSPolicy with Gateway backend client certificate",
paths: []string{"services.yml", "tlsroute/with_gateway_backend_client_certificate.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr",
Rule: `HostSNI("foo.com")`,
Priority: 7,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:80",
TLS: true,
},
{
Address: "10.10.0.2:80",
TLS: true,
},
},
ServersTransport: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
TLS: &dynamic.TLSClientConfig{
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
},
Certificates: tls.Certificates{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Simple TLSRoute and BackendTLSPolicy with subjectAltNames",
paths: []string{"services.yml", "tlsroute/with_backend_tls_policy_san.yml"},
@@ -13278,7 +13906,9 @@ func Test_makeGatewayStatus(t *testing.T) {
p := Provider{}
gateway := &gatev1.Gateway{Spec: gatev1.GatewaySpec{Infrastructure: test.infrastructure}}
status, _ := p.makeGatewayStatus(gateway, test.listeners, nil, slices.ContainsFunc(test.listeners, func(listener gatewayListener) bool {
gwl := gatewayWithListeners{gateway: gateway, listeners: test.listeners}
status, _ := p.makeGatewayStatus(gwl, nil, slices.ContainsFunc(test.listeners, func(listener gatewayListener) bool {
return len(listener.Status.Conditions) == 0
}))
@@ -13513,6 +14143,109 @@ func Test_resolveFrontendValidation(t *testing.T) {
}
}
func Test_loadGatewayBackendTLS(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"gateway_backend_client_certificate_refs.yml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
// just wait for the first event
<-eventCh
p := Provider{client: client}
testCases := []struct {
desc string
certRef *gatev1.SecretObjectReference
expectedCerts tls.Certificates
expectedReason gatev1.GatewayConditionReason
}{
{
desc: "no client certificate reference",
},
{
desc: "Secret in the Gateway namespace",
certRef: &gatev1.SecretObjectReference{Name: "client-cert"},
expectedCerts: tls.Certificates{
{CertFile: types.FileOrContent(listenerCert), KeyFile: types.FileOrContent(listenerKey)},
},
},
{
desc: "Secret with an explicit core group",
certRef: &gatev1.SecretObjectReference{Group: new(gatev1.Group("")), Name: "client-cert"},
expectedCerts: tls.Certificates{
{CertFile: types.FileOrContent(listenerCert), KeyFile: types.FileOrContent(listenerKey)},
},
},
{
desc: "Secret in another namespace with ReferenceGrant",
certRef: &gatev1.SecretObjectReference{Name: "client-cert", Namespace: new(gatev1.Namespace("bar"))},
expectedCerts: tls.Certificates{
{CertFile: types.FileOrContent(otherClientCert), KeyFile: types.FileOrContent(otherClientKey)},
},
},
{
desc: "Secret in another namespace without ReferenceGrant",
certRef: &gatev1.SecretObjectReference{Name: "ungranted-cert", Namespace: new(gatev1.Namespace("bar"))},
expectedReason: gatev1.GatewayReasonRefNotPermitted,
},
{
desc: "missing Secret",
certRef: &gatev1.SecretObjectReference{Name: "missing-cert"},
expectedReason: gatev1.GatewayReasonInvalidClientCertificateRef,
},
{
desc: "malformed Secret",
certRef: &gatev1.SecretObjectReference{Name: "malformed-cert"},
expectedReason: gatev1.GatewayReasonInvalidClientCertificateRef,
},
{
desc: "unsupported kind",
certRef: &gatev1.SecretObjectReference{Kind: new(gatev1.Kind("ConfigMap")), Name: "client-cert"},
expectedReason: gatev1.GatewayReasonInvalidClientCertificateRef,
},
{
desc: "unsupported group",
certRef: &gatev1.SecretObjectReference{Group: new(gatev1.Group("apps")), Name: "client-cert"},
expectedReason: gatev1.GatewayReasonInvalidClientCertificateRef,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
gateway := &gatev1.Gateway{
Name: "my-gateway",
Namespace: "default",
Generation: 1,
}
if test.certRef != nil {
gateway.Spec.TLS = &gatev1.GatewayTLSConfig{
Backend: &gatev1.GatewayBackendTLS{ClientCertificateRef: test.certRef},
}
}
certs, errCondition := p.loadGatewayBackendTLS(gateway)
assert.Equal(t, test.expectedCerts, certs)
if test.expectedReason == "" {
assert.Nil(t, errCondition)
return
}
require.NotNil(t, errCondition)
assert.Equal(t, string(gatev1.GatewayConditionResolvedRefs), errCondition.Type)
assert.Equal(t, metav1.ConditionFalse, errCondition.Status)
assert.Equal(t, string(test.expectedReason), errCondition.Reason)
})
}
}
func Test_loadGatewayListeners(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"frontend_validation_ca_refs.yml"})
@@ -14187,6 +14920,95 @@ func Test_loadConfigurationFromGateways_BackendTLSPolicyAncestorNamespace(t *tes
}
}
func Test_makeGatewayStatus_resolvedRefs(t *testing.T) {
resolvedListener := func() gatewayListener {
return gatewayListener{Status: &gatev1.ListenerStatus{Name: "resolved"}}
}
unresolvedListener := func() gatewayListener {
return gatewayListener{Status: &gatev1.ListenerStatus{
Name: "unresolved",
Conditions: []metav1.Condition{{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
Reason: string(gatev1.ListenerReasonInvalidCertificateRef),
}},
}}
}
unresolvedListenerSetListener := func() gatewayListener {
listener := unresolvedListener()
listener.ListenerSet = &gatev1.ListenerSet{}
return listener
}
clientCertificateCondition := &metav1.Condition{
Type: string(gatev1.GatewayConditionResolvedRefs),
Status: metav1.ConditionFalse,
Reason: string(gatev1.GatewayReasonInvalidClientCertificateRef),
}
testCases := []struct {
desc string
listeners []gatewayListener
errResolvedRefsCondition *metav1.Condition
wantStatus metav1.ConditionStatus
wantReason gatev1.GatewayConditionReason
}{
{
desc: "all references resolved",
listeners: []gatewayListener{resolvedListener()},
wantStatus: metav1.ConditionTrue,
wantReason: gatev1.GatewayReasonResolvedRefs,
},
{
desc: "unresolved listener of a Gateway not accepted",
listeners: []gatewayListener{unresolvedListener()},
wantStatus: metav1.ConditionFalse,
wantReason: gatev1.GatewayReasonListenersNotResolved,
},
{
desc: "unresolved ListenerSet listener",
listeners: []gatewayListener{resolvedListener(), unresolvedListenerSetListener()},
wantStatus: metav1.ConditionTrue,
wantReason: gatev1.GatewayReasonResolvedRefs,
},
{
desc: "unresolved client certificate",
listeners: []gatewayListener{resolvedListener()},
errResolvedRefsCondition: clientCertificateCondition,
wantStatus: metav1.ConditionFalse,
wantReason: gatev1.GatewayReasonInvalidClientCertificateRef,
},
{
desc: "unresolved client certificate and listener",
listeners: []gatewayListener{resolvedListener(), unresolvedListener()},
errResolvedRefsCondition: clientCertificateCondition,
wantStatus: metav1.ConditionFalse,
wantReason: gatev1.GatewayReasonInvalidClientCertificateRef,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
p := Provider{}
gwl := gatewayWithListeners{
gateway: &gatev1.Gateway{},
listeners: test.listeners,
errResolvedRefsCondition: test.errResolvedRefsCondition,
}
status, _ := p.makeGatewayStatus(gwl, nil, slices.ContainsFunc(test.listeners, func(listener gatewayListener) bool {
return len(listener.Status.Conditions) == 0
}))
condition := meta.FindStatusCondition(status.Conditions, string(gatev1.GatewayConditionResolvedRefs))
require.NotNil(t, condition)
assert.Equal(t, test.wantStatus, condition.Status)
assert.Equal(t, string(test.wantReason), condition.Reason)
})
}
}
func certificateCommonName(t *testing.T, certFile types.FileOrContent) string {
t.Helper()
+4 -3
View File
@@ -416,7 +416,7 @@ func (p *Provider) loadTLSServers(gatewayName, gatewayNamespace, namespace strin
}
var resolvedRefCondition metav1.Condition
serversTransport, resolvedRefCondition = p.loadTCPServersTransport(namespace, policy)
serversTransport, resolvedRefCondition = p.loadTCPServersTransport(namespace, policy, listener.BackendClientCertificates)
policyAncestorStatus.Conditions = append(policyAncestorStatus.Conditions, resolvedRefCondition)
if resolvedRefCondition.Status == metav1.ConditionFalse {
@@ -478,10 +478,11 @@ func (p *Provider) loadTLSServers(gatewayName, gatewayNamespace, namespace strin
return lb, serversTransport, nil
}
func (p *Provider) loadTCPServersTransport(namespace string, policy *gatev1.BackendTLSPolicy) (*dynamic.TCPServersTransport, metav1.Condition) {
func (p *Provider) loadTCPServersTransport(namespace string, policy *gatev1.BackendTLSPolicy, clientCertificates tls.Certificates) (*dynamic.TCPServersTransport, metav1.Condition) {
st := &dynamic.TCPServersTransport{
TLS: &dynamic.TLSClientConfig{
ServerName: string(policy.Spec.Validation.Hostname),
ServerName: string(policy.Spec.Validation.Hostname),
Certificates: clientCertificates,
},
}