This is an automated email from the ASF dual-hosted git repository.

ronething pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git


The following commit(s) were added to refs/heads/master by this push:
     new e7d192b6 feat: support forward-auth for ingress annotations (#2641)
e7d192b6 is described below

commit e7d192b6f8c2b754584cb7d6e63f907ea59202a7
Author: AlinsRan <[email protected]>
AuthorDate: Tue Nov 4 16:49:15 2025 +0800

    feat: support forward-auth for ingress annotations (#2641)
    
    Co-authored-by: Ashing Zheng <[email protected]>
---
 .../translator/annotations/plugins/forward-auth.go | 48 +++++++++++++++++
 .../adc/translator/annotations/plugins/plugins.go  |  1 +
 internal/adc/translator/annotations/types.go       |  4 ++
 internal/adc/translator/annotations_test.go        | 41 ++++++++++++++
 internal/webhook/v1/ingress_webhook.go             |  5 --
 test/e2e/framework/manifests/nginx.yaml            | 13 +++++
 test/e2e/ingress/annotations.go                    | 63 ++++++++++++++++++++++
 7 files changed, 170 insertions(+), 5 deletions(-)

diff --git a/internal/adc/translator/annotations/plugins/forward-auth.go 
b/internal/adc/translator/annotations/plugins/forward-auth.go
new file mode 100644
index 00000000..61fad5ec
--- /dev/null
+++ b/internal/adc/translator/annotations/plugins/forward-auth.go
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package plugins
+
+import (
+       adctypes "github.com/apache/apisix-ingress-controller/api/adc"
+       
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations"
+)
+
+type forwardAuth struct{}
+
+// NewForwardAuthHandler creates a handler to convert
+// annotations about forward authentication to APISIX forward-auth plugin.
+func NewForwardAuthHandler() PluginAnnotationsHandler {
+       return &forwardAuth{}
+}
+
+func (i *forwardAuth) PluginName() string {
+       return "forward-auth"
+}
+
+func (i *forwardAuth) Handle(e annotations.Extractor) (any, error) {
+       uri := e.GetStringAnnotation(annotations.AnnotationsForwardAuthURI)
+       sslVerify := 
e.GetStringAnnotation(annotations.AnnotationsForwardAuthSSLVerify) != 
annotations.FalseString
+       if len(uri) > 0 {
+               return &adctypes.ForwardAuthConfig{
+                       URI:             uri,
+                       SSLVerify:       sslVerify,
+                       RequestHeaders:  
e.GetStringsAnnotation(annotations.AnnotationsForwardAuthRequestHeaders),
+                       UpstreamHeaders: 
e.GetStringsAnnotation(annotations.AnnotationsForwardAuthUpstreamHeaders),
+                       ClientHeaders:   
e.GetStringsAnnotation(annotations.AnnotationsForwardAuthClientHeaders),
+               }, nil
+       }
+
+       return nil, nil
+}
diff --git a/internal/adc/translator/annotations/plugins/plugins.go 
b/internal/adc/translator/annotations/plugins/plugins.go
index b48bfff5..2475ef55 100644
--- a/internal/adc/translator/annotations/plugins/plugins.go
+++ b/internal/adc/translator/annotations/plugins/plugins.go
@@ -45,6 +45,7 @@ var (
                NewKeyAuthHandler(),
                NewResponseRewriteHandler(),
                NewIPRestrictionHandler(),
+               NewForwardAuthHandler(),
        }
 )
 
diff --git a/internal/adc/translator/annotations/types.go 
b/internal/adc/translator/annotations/types.go
index 1ce19783..61ef517f 100644
--- a/internal/adc/translator/annotations/types.go
+++ b/internal/adc/translator/annotations/types.go
@@ -91,6 +91,10 @@ const (
        AnnotationsSvcNamespace = AnnotationsPrefix + "svc-namespace"
 )
 
+const (
+       FalseString = "false"
+)
+
 // Handler abstracts the behavior so that the apisix-ingress-controller knows
 type IngressAnnotationsParser interface {
        // Handle parses the target annotation and converts it to the 
type-agnostic structure.
diff --git a/internal/adc/translator/annotations_test.go 
b/internal/adc/translator/annotations_test.go
index 46d4550a..6fe51da9 100644
--- a/internal/adc/translator/annotations_test.go
+++ b/internal/adc/translator/annotations_test.go
@@ -288,6 +288,47 @@ func TestTranslateIngressAnnotations(t *testing.T) {
                                ServiceNamespace: "custom-namespace",
                        },
                },
+               {
+                       name: "forward auth",
+                       anno: map[string]string{
+                               annotations.AnnotationsForwardAuthURI:          
   "http://127.0.0.1:9080";,
+                               
annotations.AnnotationsForwardAuthRequestHeaders:  "Authorization",
+                               
annotations.AnnotationsForwardAuthClientHeaders:   "Location",
+                               
annotations.AnnotationsForwardAuthUpstreamHeaders: "X-User-ID",
+                       },
+                       expected: &IngressConfig{
+                               Plugins: adctypes.Plugins{
+                                       "forward-auth": 
&adctypes.ForwardAuthConfig{
+                                               URI:             
"http://127.0.0.1:9080";,
+                                               SSLVerify:       true,
+                                               RequestHeaders:  
[]string{"Authorization"},
+                                               UpstreamHeaders: 
[]string{"X-User-ID"},
+                                               ClientHeaders:   
[]string{"Location"},
+                                       },
+                               },
+                       },
+               },
+               {
+                       name: "forward auth with ssl-verify false",
+                       anno: map[string]string{
+                               annotations.AnnotationsForwardAuthURI:          
   "http://127.0.0.1:9080";,
+                               annotations.AnnotationsForwardAuthSSLVerify:    
   "false",
+                               
annotations.AnnotationsForwardAuthRequestHeaders:  "Authorization",
+                               
annotations.AnnotationsForwardAuthClientHeaders:   "Location",
+                               
annotations.AnnotationsForwardAuthUpstreamHeaders: "X-User-ID",
+                       },
+                       expected: &IngressConfig{
+                               Plugins: adctypes.Plugins{
+                                       "forward-auth": 
&adctypes.ForwardAuthConfig{
+                                               URI:             
"http://127.0.0.1:9080";,
+                                               SSLVerify:       false,
+                                               RequestHeaders:  
[]string{"Authorization"},
+                                               UpstreamHeaders: 
[]string{"X-User-ID"},
+                                               ClientHeaders:   
[]string{"Location"},
+                                       },
+                               },
+                       },
+               },
        }
 
        for _, tt := range tests {
diff --git a/internal/webhook/v1/ingress_webhook.go 
b/internal/webhook/v1/ingress_webhook.go
index 740c25a3..2dfc6de5 100644
--- a/internal/webhook/v1/ingress_webhook.go
+++ b/internal/webhook/v1/ingress_webhook.go
@@ -40,11 +40,6 @@ var ingresslog = logf.Log.WithName("ingress-resource")
 // ref: 
https://apisix.apache.org/docs/ingress-controller/upgrade-guide/#limited-support-for-ingress-annotations
 var unsupportedAnnotations = []string{
        "k8s.apisix.apache.org/use-regex",
-       "k8s.apisix.apache.org/auth-uri",
-       "k8s.apisix.apache.org/auth-ssl-verify",
-       "k8s.apisix.apache.org/auth-request-headers",
-       "k8s.apisix.apache.org/auth-upstream-headers",
-       "k8s.apisix.apache.org/auth-client-headers",
        "k8s.apisix.apache.org/auth-type",
 }
 
diff --git a/test/e2e/framework/manifests/nginx.yaml 
b/test/e2e/framework/manifests/nginx.yaml
index e75c1dae..abdc7e9e 100644
--- a/test/e2e/framework/manifests/nginx.yaml
+++ b/test/e2e/framework/manifests/nginx.yaml
@@ -51,6 +51,19 @@ data:
           }
         }
 
+        location /auth {
+          content_by_lua_block {
+            local auth = ngx.req.get_headers()["Authorization"]
+            if auth == "123" then
+              ngx.header["X-User-ID"] = "user-123"
+              ngx.exit(200)
+            else
+              ngx.header["Location"] = "http://example.com/auth";
+              ngx.exit(401)
+            end
+          }
+        }
+
         location /ws {
           content_by_lua_block {
             local server = require "resty.websocket.server"
diff --git a/test/e2e/ingress/annotations.go b/test/e2e/ingress/annotations.go
index 049d51bb..b29aeaee 100644
--- a/test/e2e/ingress/annotations.go
+++ b/test/e2e/ingress/annotations.go
@@ -573,6 +573,30 @@ spec:
             name: httpbin-service-e2e-test
             port:
               number: 80
+`
+                       ingressForwardAuth = `
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: forward-auth
+  annotations:
+    k8s.apisix.apache.org/auth-uri: %s
+    k8s.apisix.apache.org/auth-request-headers: Authorization
+    k8s.apisix.apache.org/auth-upstream-headers: X-User-ID
+    k8s.apisix.apache.org/auth-client-headers: Location
+spec:
+  ingressClassName: %s
+  rules:
+  - host: httpbin.example
+    http:
+      paths:
+      - path: /get
+        pathType: Exact
+        backend:
+          service:
+            name: httpbin-service-e2e-test
+            port:
+              number: 80
 `
                )
                BeforeEach(func() {
@@ -1017,6 +1041,45 @@ spec:
                                Check:  
scaffold.WithExpectedStatus(http.StatusForbidden),
                        })
                })
+               It("forward-auth", func() {
+                       s.DeployNginx(framework.NginxOptions{
+                               Namespace: s.Namespace(),
+                               Replicas:  ptr.To(int32(1)),
+                       })
+
+                       
Expect(s.CreateResourceFromString(fmt.Sprintf(ingressForwardAuth, 
"http://nginx/auth";, s.Namespace()))).
+                               ShouldNot(HaveOccurred(), "creating 
ApisixConsumer for forwardAuth")
+
+                       tests := []*scaffold.RequestAssert{
+                               {
+                                       Method: "GET",
+                                       Path:   "/get",
+                                       Host:   "httpbin.example",
+                                       Headers: map[string]string{
+                                               "Authorization": "123",
+                                       },
+                                       Checks: []scaffold.ResponseCheckFunc{
+                                               
scaffold.WithExpectedStatus(http.StatusOK),
+                                               
scaffold.WithExpectedBodyContains(`"X-User-Id": "user-123"`),
+                                       },
+                               },
+                               {
+                                       Method: "GET",
+                                       Path:   "/get",
+                                       Host:   "httpbin.example",
+                                       Headers: map[string]string{
+                                               "Authorization": "456",
+                                       },
+                                       Checks: []scaffold.ResponseCheckFunc{
+                                               
scaffold.WithExpectedStatus(http.StatusUnauthorized),
+                                               
scaffold.WithExpectedHeader("Location", "http://example.com/auth";),
+                                       },
+                               },
+                       }
+                       for _, test := range tests {
+                               s.RequestAssert(test)
+                       }
+               })
        })
 
        Context("Service Namespace", func() {

Reply via email to