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

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

commit fea8f564fdce1e33f12ef511aa914d72c5121c34
Author: Ashing Zheng <[email protected]>
AuthorDate: Fri Oct 31 14:42:40 2025 +0800

    feat: support ip restriction annotations for ingress
    
    Signed-off-by: Ashing Zheng <[email protected]>
---
 .../annotations/plugins/ip_restriction.go          | 47 ++++++++++++++
 .../annotations/plugins/ip_restriction_test.go     | 72 ++++++++++++++++++++++
 .../adc/translator/annotations/plugins/plugins.go  |  1 +
 test/e2e/ingress/annotations.go                    | 66 ++++++++++++++++++++
 4 files changed, 186 insertions(+)

diff --git a/internal/adc/translator/annotations/plugins/ip_restriction.go 
b/internal/adc/translator/annotations/plugins/ip_restriction.go
new file mode 100644
index 00000000..2b1e279a
--- /dev/null
+++ b/internal/adc/translator/annotations/plugins/ip_restriction.go
@@ -0,0 +1,47 @@
+// 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 ipRestriction struct{}
+
+// NewIPRestrictionHandler creates a handler to convert
+// annotations about client IP control to APISIX ip-restriction plugin.
+func NewIPRestrictionHandler() PluginAnnotationsHandler {
+       return &ipRestriction{}
+}
+
+func (i *ipRestriction) PluginName() string {
+       return "ip-restriction"
+}
+
+func (i *ipRestriction) Handle(e annotations.Extractor) (any, error) {
+       allowlist := 
e.GetStringsAnnotation(annotations.AnnotationsAllowlistSourceRange)
+       blocklist := 
e.GetStringsAnnotation(annotations.AnnotationsBlocklistSourceRange)
+
+       if allowlist == nil && blocklist == nil {
+               return nil, nil
+       }
+
+       return &adctypes.IPRestrictConfig{
+               Allowlist: allowlist,
+               Blocklist: blocklist,
+       }, nil
+}
diff --git a/internal/adc/translator/annotations/plugins/ip_restriction_test.go 
b/internal/adc/translator/annotations/plugins/ip_restriction_test.go
new file mode 100644
index 00000000..526850c4
--- /dev/null
+++ b/internal/adc/translator/annotations/plugins/ip_restriction_test.go
@@ -0,0 +1,72 @@
+// 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 (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+
+       adctypes "github.com/apache/apisix-ingress-controller/api/adc"
+       
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations"
+)
+
+func TestIPRestrictionHandler(t *testing.T) {
+       // Test with allowlist only
+       anno := map[string]string{
+               annotations.AnnotationsAllowlistSourceRange: 
"10.2.2.2,192.168.0.0/16",
+       }
+       p := NewIPRestrictionHandler()
+       out, err := p.Handle(annotations.NewExtractor(anno))
+       assert.Nil(t, err, "checking given error")
+       assert.NotNil(t, out, "checking output is not nil")
+       config := out.(*adctypes.IPRestrictConfig)
+       assert.Len(t, config.Allowlist, 2, "checking size of allowlist")
+       assert.Equal(t, "10.2.2.2", config.Allowlist[0])
+       assert.Equal(t, "192.168.0.0/16", config.Allowlist[1])
+       assert.Nil(t, config.Blocklist, "checking blocklist is nil")
+       assert.Equal(t, "ip-restriction", p.PluginName())
+
+       // Test with both allowlist and blocklist
+       anno[annotations.AnnotationsBlocklistSourceRange] = 
"172.17.0.0/16,127.0.0.1"
+       out, err = p.Handle(annotations.NewExtractor(anno))
+       assert.Nil(t, err, "checking given error")
+       assert.NotNil(t, out, "checking output is not nil")
+       config = out.(*adctypes.IPRestrictConfig)
+       assert.Len(t, config.Allowlist, 2, "checking size of allowlist")
+       assert.Equal(t, "10.2.2.2", config.Allowlist[0])
+       assert.Equal(t, "192.168.0.0/16", config.Allowlist[1])
+       assert.Len(t, config.Blocklist, 2, "checking size of blocklist")
+       assert.Equal(t, "172.17.0.0/16", config.Blocklist[0])
+       assert.Equal(t, "127.0.0.1", config.Blocklist[1])
+
+       // Test with blocklist only
+       delete(anno, annotations.AnnotationsAllowlistSourceRange)
+       out, err = p.Handle(annotations.NewExtractor(anno))
+       assert.Nil(t, err, "checking given error")
+       assert.NotNil(t, out, "checking output is not nil")
+       config = out.(*adctypes.IPRestrictConfig)
+       assert.Nil(t, config.Allowlist, "checking allowlist is nil")
+       assert.Len(t, config.Blocklist, 2, "checking size of blocklist")
+       assert.Equal(t, "172.17.0.0/16", config.Blocklist[0])
+       assert.Equal(t, "127.0.0.1", config.Blocklist[1])
+
+       // Test with neither allowlist nor blocklist
+       delete(anno, annotations.AnnotationsBlocklistSourceRange)
+       out, err = p.Handle(annotations.NewExtractor(anno))
+       assert.Nil(t, err, "checking given error")
+       assert.Nil(t, out, "checking the given ip-restriction plugin config is 
nil")
+}
diff --git a/internal/adc/translator/annotations/plugins/plugins.go 
b/internal/adc/translator/annotations/plugins/plugins.go
index 06bc87f4..b48bfff5 100644
--- a/internal/adc/translator/annotations/plugins/plugins.go
+++ b/internal/adc/translator/annotations/plugins/plugins.go
@@ -44,6 +44,7 @@ var (
                NewBasicAuthHandler(),
                NewKeyAuthHandler(),
                NewResponseRewriteHandler(),
+               NewIPRestrictionHandler(),
        }
 )
 
diff --git a/test/e2e/ingress/annotations.go b/test/e2e/ingress/annotations.go
index 98a0bcaf..36a51321 100644
--- a/test/e2e/ingress/annotations.go
+++ b/test/e2e/ingress/annotations.go
@@ -530,6 +530,50 @@ spec:
             port:
               number: 80
 `
+
+                       ingressAllowlist = `
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: allowlist
+  annotations:
+    k8s.apisix.apache.org/allowlist-source-range: "10.0.5.0/16"
+spec:
+  ingressClassName: %s
+  rules:
+  - host: httpbin.example
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          service:
+            name: httpbin-service-e2e-test
+            port:
+              number: 80
+`
+
+                       ingressBlocklist = `
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: blocklist
+  annotations:
+    k8s.apisix.apache.org/blocklist-source-range: "127.0.0.1"
+spec:
+  ingressClassName: %s
+  rules:
+  - host: httpbin-block.example
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          service:
+            name: httpbin-service-e2e-test
+            port:
+              number: 80
+`
                )
                BeforeEach(func() {
                        By("create GatewayProxy")
@@ -951,5 +995,27 @@ spec:
                        
Expect(rewriteConfig["status_code"]).To(Equal(float64(400)), "checking status 
code")
                        Expect(rewriteConfig["body_base64"]).To(BeTrue(), 
"checking body_base64")
                })
+
+               It("ip-restriction", func() {
+                       By("Test allowlist - create ingress with IP allowlist")
+                       
Expect(s.CreateResourceFromString(fmt.Sprintf(ingressAllowlist, 
s.Namespace()))).ShouldNot(HaveOccurred(), "creating Ingress with allowlist")
+
+                       s.RequestAssert(&scaffold.RequestAssert{
+                               Method: "GET",
+                               Path:   "/ip",
+                               Host:   "httpbin.example",
+                               Check:  
scaffold.WithExpectedStatus(http.StatusForbidden),
+                       })
+
+                       By("Test blocklist - create ingress with IP blocklist")
+                       
Expect(s.CreateResourceFromString(fmt.Sprintf(ingressBlocklist, 
s.Namespace()))).ShouldNot(HaveOccurred(), "creating Ingress with blocklist")
+
+                       s.RequestAssert(&scaffold.RequestAssert{
+                               Method: "GET",
+                               Path:   "/ip",
+                               Host:   "httpbin-block.example",
+                               Check:  
scaffold.WithExpectedStatus(http.StatusForbidden),
+                       })
+               })
        })
 })

Reply via email to