From 265ddf2750601b81e53340232e876adf741e315c Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 10 Dec 2025 23:14:37 -0600 Subject: [PATCH 1/3] support auth-secret-type Signed-off-by: omar --- pkg/i2gw/implementations/kgateway/README.md | 1 + pkg/i2gw/implementations/kgateway/emitter.go | 12 ++- .../kgateway/emitter_integration_test.go | 9 ++ .../testing/testdata/input/basic_auth.yaml | 47 +++++++++ .../testing/testdata/output/basic_auth.yaml | 99 +++++++++++++++++++ .../intermediate/provider_ingressnginx.go | 4 + pkg/i2gw/providers/ingressnginx/README.md | 1 + .../providers/ingressnginx/external_auth.go | 11 +++ 8 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml create mode 100644 pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml diff --git a/pkg/i2gw/implementations/kgateway/README.md b/pkg/i2gw/implementations/kgateway/README.md index 8f9d61d6b..8fa4d32ca 100644 --- a/pkg/i2gw/implementations/kgateway/README.md +++ b/pkg/i2gw/implementations/kgateway/README.md @@ -84,6 +84,7 @@ The command should generate Gateway API and Kgateway resources. - `nginx.ingress.kubernetes.io/auth-type`: Must be set to `"basic"` to enable basic authentication. Maps to `TrafficPolicy.spec.basicAuth`. - `nginx.ingress.kubernetes.io/auth-secret`: Specifies the secret containing basic auth credentials in `namespace/name` format (or just `name` if in the same namespace). Maps to `TrafficPolicy.spec.basicAuth.secretRef.name`. +- `nginx.ingress.kubernetes.io/auth-secret-type`: Specifies the format of the secret. Values: `"auth-file"` (default) or `"auth-map"`. For `"auth-file"`, the secret contains an htpasswd file in the key `"auth"`. For `"auth-map"`, the keys of the secret are usernames and values are hashed passwords. When set to `"auth-file"` (or default), maps to `TrafficPolicy.spec.basicAuth.secretRef.key` set to `"auth"`. ### Backend TLS diff --git a/pkg/i2gw/implementations/kgateway/emitter.go b/pkg/i2gw/implementations/kgateway/emitter.go index f5e74d690..407473455 100644 --- a/pkg/i2gw/implementations/kgateway/emitter.go +++ b/pkg/i2gw/implementations/kgateway/emitter.go @@ -776,6 +776,7 @@ func applyAccessLogPolicy( // // Semantics: // - If BasicAuth is configured, set spec.basicAuth.secretRef.name in TrafficPolicy. +// - If AuthType is "auth-file" (default), also set spec.basicAuth.secretRef.key to "auth". func applyBasicAuthPolicy( pol intermediate.Policy, ingressName, namespace string, @@ -786,10 +787,15 @@ func applyBasicAuthPolicy( } t := ensureTrafficPolicy(tp, ingressName, namespace) + secretRef := &kgateway.SecretReference{ + Name: gwv1.ObjectName(pol.BasicAuth.SecretName), + } + // Set Key field to "auth" when AuthType is "auth-file" (default format) + if pol.BasicAuth.AuthType == "auth-file" { + secretRef.Key = ptr.To("auth") + } t.Spec.BasicAuth = &kgateway.BasicAuthPolicy{ - SecretRef: &kgateway.SecretReference{ - Name: gwv1.ObjectName(pol.BasicAuth.SecretName), - }, + SecretRef: secretRef, } return true } diff --git a/pkg/i2gw/implementations/kgateway/emitter_integration_test.go b/pkg/i2gw/implementations/kgateway/emitter_integration_test.go index de9754ec2..3300ac022 100644 --- a/pkg/i2gw/implementations/kgateway/emitter_integration_test.go +++ b/pkg/i2gw/implementations/kgateway/emitter_integration_test.go @@ -242,6 +242,15 @@ func TestKgatewayIngressNginxIntegration_Golden(t *testing.T) { "pkg", "i2gw", "implementations", "kgateway", "testing", "testdata", "output", "service_upstream.yaml", ), }, + { + name: "basic_auth", + inputRel: filepath.Join( + "pkg", "i2gw", "implementations", "kgateway", "testing", "testdata", "input", "basic_auth.yaml", + ), + goldenRel: filepath.Join( + "pkg", "i2gw", "implementations", "kgateway", "testing", "testdata", "output", "basic_auth.yaml", + ), + }, } for _, tt := range tests { diff --git a/pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml b/pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml new file mode 100644 index 000000000..4efe1424b --- /dev/null +++ b/pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml @@ -0,0 +1,47 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + ingress2gateway.kubernetes.io/implementation: kgateway + nginx.ingress.kubernetes.io/auth-type: "basic" + nginx.ingress.kubernetes.io/auth-secret: "default/basic-auth-secret" + name: ingress-basic-auth-file-default + namespace: default +spec: + ingressClassName: nginx + rules: + - host: app1.example.org + http: + paths: + - backend: + service: + name: app1 + port: + number: 80 + path: / + pathType: Prefix +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + ingress2gateway.kubernetes.io/implementation: kgateway + nginx.ingress.kubernetes.io/auth-type: "basic" + nginx.ingress.kubernetes.io/auth-secret: "default/basic-auth-secret" + nginx.ingress.kubernetes.io/auth-secret-type: "auth-file" + name: ingress-basic-auth-file-explicit + namespace: default +spec: + ingressClassName: nginx + rules: + - host: app2.example.org + http: + paths: + - backend: + service: + name: app2 + port: + number: 80 + path: / + pathType: Prefix +--- diff --git a/pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml b/pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml new file mode 100644 index 000000000..e045d838e --- /dev/null +++ b/pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml @@ -0,0 +1,99 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + annotations: + gateway.networking.k8s.io/generator: ingress2gateway-dev + name: nginx + namespace: default +spec: + gatewayClassName: kgateway + listeners: + - hostname: app1.example.org + name: app1-example-org-http + port: 80 + protocol: HTTP + - hostname: app2.example.org + name: app2-example-org-http + port: 80 + protocol: HTTP +status: {} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + annotations: + gateway.networking.k8s.io/generator: ingress2gateway-dev + name: ingress-basic-auth-file-default-app1-example-org + namespace: default +spec: + hostnames: + - app1.example.org + parentRefs: + - name: nginx + rules: + - backendRefs: + - name: app1 + port: 80 + matches: + - path: + type: PathPrefix + value: / +status: + parents: [] +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + annotations: + gateway.networking.k8s.io/generator: ingress2gateway-dev + name: ingress-basic-auth-file-explicit-app2-example-org + namespace: default +spec: + hostnames: + - app2.example.org + parentRefs: + - name: nginx + rules: + - backendRefs: + - name: app2 + port: 80 + matches: + - path: + type: PathPrefix + value: / +status: + parents: [] +--- +apiVersion: gateway.kgateway.dev/v1alpha1 +kind: TrafficPolicy +metadata: + name: ingress-basic-auth-file-default + namespace: default +spec: + basicAuth: + secretRef: + key: auth + name: basic-auth-secret + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: ingress-basic-auth-file-default-app1-example-org +status: + ancestors: null +--- +apiVersion: gateway.kgateway.dev/v1alpha1 +kind: TrafficPolicy +metadata: + name: ingress-basic-auth-file-explicit + namespace: default +spec: + basicAuth: + secretRef: + key: auth + name: basic-auth-secret + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: ingress-basic-auth-file-explicit-app2-example-org +status: + ancestors: null diff --git a/pkg/i2gw/intermediate/provider_ingressnginx.go b/pkg/i2gw/intermediate/provider_ingressnginx.go index 88b3f74ac..71d50eb19 100644 --- a/pkg/i2gw/intermediate/provider_ingressnginx.go +++ b/pkg/i2gw/intermediate/provider_ingressnginx.go @@ -84,6 +84,10 @@ type ExtAuthPolicy struct { type BasicAuthPolicy struct { // SecretName defines the name of the secret containing basic auth credentials. SecretName string + // AuthType defines the format of the secret: "auth-file" (default) or "auth-map". + // For "auth-file", the secret contains an htpasswd file in a specific key. + // For "auth-map", the keys of the secret are usernames and values are hashed passwords. + AuthType string } // SessionAffinityPolicy defines a session affinity policy that has been extracted from ingress-nginx annotations. diff --git a/pkg/i2gw/providers/ingressnginx/README.md b/pkg/i2gw/providers/ingressnginx/README.md index 9487ef46e..7bf4b2727 100644 --- a/pkg/i2gw/providers/ingressnginx/README.md +++ b/pkg/i2gw/providers/ingressnginx/README.md @@ -78,6 +78,7 @@ The ingress-nginx provider currently supports translating the following annotati - `nginx.ingress.kubernetes.io/auth-type`: Must be set to `"basic"` to enable basic authentication. For the Kgateway implementation, this maps to `TrafficPolicy.spec.basicAuth`. - `nginx.ingress.kubernetes.io/auth-secret`: Specifies the secret containing basic auth credentials in `namespace/name` format (or just `name` if in the same namespace). For the Kgateway implementation, this maps to `TrafficPolicy.spec.basicAuth.secretRef.name`. +- `nginx.ingress.kubernetes.io/auth-secret-type`: Specifies the format of the secret. Values: `"auth-file"` (default) or `"auth-map"`. For `"auth-file"`, the secret contains an htpasswd file in the key `"auth"`. For `"auth-map"`, the keys of the secret are usernames and values are hashed passwords. For the Kgateway implementation, when set to `"auth-file"` (or default), this maps to `TrafficPolicy.spec.basicAuth.secretRef.key` set to `"auth"`. --- diff --git a/pkg/i2gw/providers/ingressnginx/external_auth.go b/pkg/i2gw/providers/ingressnginx/external_auth.go index f9752c6ef..454e87cac 100644 --- a/pkg/i2gw/providers/ingressnginx/external_auth.go +++ b/pkg/i2gw/providers/ingressnginx/external_auth.go @@ -32,6 +32,7 @@ const ( authResponseHeadersAnnotation = "nginx.ingress.kubernetes.io/auth-response-headers" authTypeAnnotation = "nginx.ingress.kubernetes.io/auth-type" authSecretAnnotation = "nginx.ingress.kubernetes.io/auth-secret" + authSecretTypeAnnotation = "nginx.ingress.kubernetes.io/auth-secret-type" ) // extAuthFeature extracts the "auth-url" and "auth-response-headers" annotations and @@ -167,6 +168,7 @@ func basicAuthFeature( ing := &ingresses[i] authTypeRaw := strings.TrimSpace(ing.Annotations[authTypeAnnotation]) authSecretRaw := strings.TrimSpace(ing.Annotations[authSecretAnnotation]) + authSecretTypeRaw := strings.TrimSpace(ing.Annotations[authSecretTypeAnnotation]) // Only process if auth-type is "basic" and auth-secret is present if authTypeRaw != "basic" || authSecretRaw == "" { @@ -191,8 +193,17 @@ func basicAuthFeature( } } + // Determine auth type based on auth-secret-type annotation + // auth-file (default): htpasswd file in key "auth" + // auth-map: keys are usernames, values are hashed passwords + authType := authSecretTypeRaw + if authType == "" { + authType = "auth-file" // default + } + pol.BasicAuth = &intermediate.BasicAuthPolicy{ SecretName: secretName, + AuthType: authType, } } From 278dfc59276884258611a62a09364d19d931cb5d Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 11 Dec 2025 10:03:54 -0600 Subject: [PATCH 2/3] test Signed-off-by: omar --- .../testing/testdata/input/basic_auth.yaml | 24 +++++++++++ .../testing/testdata/output/basic_auth.yaml | 43 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml b/pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml index 4efe1424b..3b132e9f5 100644 --- a/pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml +++ b/pkg/i2gw/implementations/kgateway/testing/testdata/input/basic_auth.yaml @@ -45,3 +45,27 @@ spec: path: / pathType: Prefix --- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + ingress2gateway.kubernetes.io/implementation: kgateway + nginx.ingress.kubernetes.io/auth-type: "basic" + nginx.ingress.kubernetes.io/auth-secret: "auth-map-secret" + nginx.ingress.kubernetes.io/auth-secret-type: "auth-map" + name: ingress-basic-auth-map + namespace: default +spec: + ingressClassName: nginx + rules: + - host: app3.example.org + http: + paths: + - backend: + service: + name: app3 + port: + number: 80 + path: / + pathType: Prefix + diff --git a/pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml b/pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml index e045d838e..b826cef6a 100644 --- a/pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml +++ b/pkg/i2gw/implementations/kgateway/testing/testdata/output/basic_auth.yaml @@ -16,6 +16,10 @@ spec: name: app2-example-org-http port: 80 protocol: HTTP + - hostname: app3.example.org + name: app3-example-org-http + port: 80 + protocol: HTTP status: {} --- apiVersion: gateway.networking.k8s.io/v1 @@ -64,6 +68,29 @@ spec: status: parents: [] --- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + annotations: + gateway.networking.k8s.io/generator: ingress2gateway-dev + name: ingress-basic-auth-map-app3-example-org + namespace: default +spec: + hostnames: + - app3.example.org + parentRefs: + - name: nginx + rules: + - backendRefs: + - name: app3 + port: 80 + matches: + - path: + type: PathPrefix + value: / +status: + parents: [] +--- apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: @@ -97,3 +124,19 @@ spec: name: ingress-basic-auth-file-explicit-app2-example-org status: ancestors: null +--- +apiVersion: gateway.kgateway.dev/v1alpha1 +kind: TrafficPolicy +metadata: + name: ingress-basic-auth-map + namespace: default +spec: + basicAuth: + secretRef: + name: auth-map-secret + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: ingress-basic-auth-map-app3-example-org +status: + ancestors: null From 5ed18d486553ed312c75dea1388a95e79643ba51 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 11 Dec 2025 10:09:27 -0600 Subject: [PATCH 3/3] remove basic auth from golden Signed-off-by: omar --- .../testing/testdata/input/golden.yaml | 2 - .../testing/testdata/output/golden.yaml | 65 +++++++++---------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/pkg/i2gw/implementations/kgateway/testing/testdata/input/golden.yaml b/pkg/i2gw/implementations/kgateway/testing/testdata/input/golden.yaml index 89172dd14..4f3fb7118 100644 --- a/pkg/i2gw/implementations/kgateway/testing/testdata/input/golden.yaml +++ b/pkg/i2gw/implementations/kgateway/testing/testdata/input/golden.yaml @@ -63,8 +63,6 @@ metadata: nginx.ingress.kubernetes.io/proxy-send-timeout: "90s" nginx.ingress.kubernetes.io/proxy-read-timeout: "90s" nginx.ingress.kubernetes.io/proxy-connect-timeout: "120" - nginx.ingress.kubernetes.io/auth-type: "basic" - nginx.ingress.kubernetes.io/auth-secret: "default/basic-auth-secret" name: ingress-myserviceb namespace: default spec: diff --git a/pkg/i2gw/implementations/kgateway/testing/testdata/output/golden.yaml b/pkg/i2gw/implementations/kgateway/testing/testdata/output/golden.yaml index 7f6a94979..3c517f7d7 100644 --- a/pkg/i2gw/implementations/kgateway/testing/testdata/output/golden.yaml +++ b/pkg/i2gw/implementations/kgateway/testing/testdata/output/golden.yaml @@ -31,21 +31,40 @@ kind: HTTPRoute metadata: annotations: gateway.networking.k8s.io/generator: ingress2gateway-dev - name: ingress-myserviceb-myserviceb-foo-org + name: ingress-myservicea1-myservicea-foo-org namespace: default spec: hostnames: - - myserviceb.foo.org + - myservicea.foo.org parentRefs: - name: nginx rules: - backendRefs: - - name: myserviceb + - filters: + - extensionRef: + group: gateway.kgateway.dev + kind: TrafficPolicy + name: ingress-myservicea1 + type: ExtensionRef + name: myservicea port: 80 matches: - path: type: PathPrefix value: / + - backendRefs: + - filters: + - extensionRef: + group: gateway.kgateway.dev + kind: TrafficPolicy + name: ingress-myservicea2 + type: ExtensionRef + name: myservicea + port: 80 + matches: + - path: + type: PathPrefix + value: /2 status: parents: [] --- @@ -54,16 +73,16 @@ kind: HTTPRoute metadata: annotations: gateway.networking.k8s.io/generator: ingress2gateway-dev - name: ingress-sessionaffinity-sessionaffinity-example-org + name: ingress-myserviceb-myserviceb-foo-org namespace: default spec: hostnames: - - sessionaffinity.example.org + - myserviceb.foo.org parentRefs: - name: nginx rules: - backendRefs: - - name: httpbin + - name: myserviceb port: 80 matches: - path: @@ -77,16 +96,16 @@ kind: HTTPRoute metadata: annotations: gateway.networking.k8s.io/generator: ingress2gateway-dev - name: ingress-backendtls-tls-example-org + name: ingress-sessionaffinity-sessionaffinity-example-org namespace: default spec: hostnames: - - tls.example.org + - sessionaffinity.example.org parentRefs: - name: nginx rules: - backendRefs: - - name: httpbin2 + - name: httpbin port: 80 matches: - path: @@ -100,40 +119,21 @@ kind: HTTPRoute metadata: annotations: gateway.networking.k8s.io/generator: ingress2gateway-dev - name: ingress-myservicea1-myservicea-foo-org + name: ingress-backendtls-tls-example-org namespace: default spec: hostnames: - - myservicea.foo.org + - tls.example.org parentRefs: - name: nginx rules: - backendRefs: - - filters: - - extensionRef: - group: gateway.kgateway.dev - kind: TrafficPolicy - name: ingress-myservicea1 - type: ExtensionRef - name: myservicea + - name: httpbin2 port: 80 matches: - path: type: PathPrefix value: / - - backendRefs: - - filters: - - extensionRef: - group: gateway.kgateway.dev - kind: TrafficPolicy - name: ingress-myservicea2 - type: ExtensionRef - name: myservicea - port: 80 - matches: - - path: - type: PathPrefix - value: /2 status: parents: [] --- @@ -270,9 +270,6 @@ metadata: name: ingress-myserviceb namespace: default spec: - basicAuth: - secretRef: - name: basic-auth-secret buffer: maxRequestSize: 100m rateLimit: