You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by zh...@apache.org on 2022/05/11 03:26:15 UTC

[apisix-ingress-controller] branch master updated: feat:add authorization-annotation the ingress resource (#985)

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

zhangjintao 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 670d671d feat:add authorization-annotation the ingress resource (#985)
670d671d is described below

commit 670d671d436701ec8083248c6b23713a50ec0c4c
Author: Xin Rong <79...@users.noreply.github.com>
AuthorDate: Wed May 11 11:26:11 2022 +0800

    feat:add authorization-annotation the ingress resource (#985)
---
 pkg/kube/translation/annotations.go               |   2 +
 pkg/kube/translation/annotations/authorization.go |  64 +++++
 pkg/types/apisix/v1/plugin_types.go               |  10 +
 pkg/types/apisix/v1/zz_generated.deepcopy.go      |  32 +++
 test/e2e/scaffold/consumer.go                     |  26 +-
 test/e2e/suite-annotations/authorization.go       | 310 ++++++++++++++++++++++
 6 files changed, 441 insertions(+), 3 deletions(-)

diff --git a/pkg/kube/translation/annotations.go b/pkg/kube/translation/annotations.go
index 767f7da0..3f06b813 100644
--- a/pkg/kube/translation/annotations.go
+++ b/pkg/kube/translation/annotations.go
@@ -29,6 +29,8 @@ var (
 		annotations.NewRewriteHandler(),
 		annotations.NewRedirectHandler(),
 		annotations.NewForwardAuthHandler(),
+		annotations.NewBasicAuthHandler(),
+		annotations.NewKeyAuthHandler(),
 	}
 )
 
diff --git a/pkg/kube/translation/annotations/authorization.go b/pkg/kube/translation/annotations/authorization.go
new file mode 100644
index 00000000..c97567a4
--- /dev/null
+++ b/pkg/kube/translation/annotations/authorization.go
@@ -0,0 +1,64 @@
+// 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 annotations
+
+import (
+	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+const (
+	// auth-type: keyAuth | basicAuth
+	_authType = AnnotationsPrefix + "auth-type"
+)
+
+type basicAuth struct{}
+
+// NewkeyBasicHandler creates a handler to convert
+// annotations about basicAuth control to APISIX basic-auth plugin.
+func NewBasicAuthHandler() Handler {
+	return &basicAuth{}
+}
+
+func (b *basicAuth) PluginName() string {
+	return "basic-auth"
+}
+
+func (b *basicAuth) Handle(e Extractor) (interface{}, error) {
+	if e.GetStringAnnotation(_authType) != "basicAuth" {
+		return nil, nil
+	}
+	plugin := apisixv1.BasicAuthConfig{}
+	return &plugin, nil
+}
+
+type keyAuth struct{}
+
+// NewkeyAuthHandler creates a handler to convert
+// annotations about keyAuth control to APISIX key-auth plugin.
+func NewKeyAuthHandler() Handler {
+	return &keyAuth{}
+}
+
+func (k *keyAuth) PluginName() string {
+	return "key-auth"
+}
+
+func (k *keyAuth) Handle(e Extractor) (interface{}, error) {
+	if e.GetStringAnnotation(_authType) != "keyAuth" {
+		return nil, nil
+	}
+	plugin := apisixv1.KeyAuthConfig{}
+	return &plugin, nil
+}
diff --git a/pkg/types/apisix/v1/plugin_types.go b/pkg/types/apisix/v1/plugin_types.go
index 51346b78..b0f927c9 100644
--- a/pkg/types/apisix/v1/plugin_types.go
+++ b/pkg/types/apisix/v1/plugin_types.go
@@ -99,3 +99,13 @@ type ForwardAuthConfig struct {
 	UpstreamHeaders []string `json:"upstream_headers,omitempty"`
 	ClientHeaders   []string `json:"client_headers,omitempty"`
 }
+
+// BasicAuthConfig is the rule config for basic-auth plugin.
+// +k8s:deepcopy-gen=true
+type BasicAuthConfig struct {
+}
+
+// KeyAuthConfig is the rule config for key-auth plugin.
+// +k8s:deepcopy-gen=true
+type KeyAuthConfig struct {
+}
diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go b/pkg/types/apisix/v1/zz_generated.deepcopy.go
index eabfe8b9..4016a713 100644
--- a/pkg/types/apisix/v1/zz_generated.deepcopy.go
+++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go
@@ -19,6 +19,22 @@
 
 package v1
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConfig.
+func (in *BasicAuthConfig) DeepCopy() *BasicAuthConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(BasicAuthConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BasicAuthConsumerConfig) DeepCopyInto(out *BasicAuthConsumerConfig) {
 	*out = *in
@@ -165,6 +181,22 @@ func (in *IPRestrictConfig) DeepCopy() *IPRestrictConfig {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *KeyAuthConfig) DeepCopyInto(out *KeyAuthConfig) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConfig.
+func (in *KeyAuthConfig) DeepCopy() *KeyAuthConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(KeyAuthConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *KeyAuthConsumerConfig) DeepCopyInto(out *KeyAuthConsumerConfig) {
 	*out = *in
diff --git a/test/e2e/scaffold/consumer.go b/test/e2e/scaffold/consumer.go
index 3c44a7c7..5cee9b24 100644
--- a/test/e2e/scaffold/consumer.go
+++ b/test/e2e/scaffold/consumer.go
@@ -16,8 +16,8 @@ package scaffold
 
 import "fmt"
 
-func (s *Scaffold) ApisixConsumerBasicAuthCreated(name, username, password string) error {
-	ac := fmt.Sprintf(`
+var (
+	_apisixConsumerBasicAuth = `
 apiVersion: apisix.apache.org/v2beta3
 kind: ApisixConsumer
 metadata:
@@ -28,6 +28,26 @@ spec:
       value:
         username: %s
         password: %s
-`, name, username, password)
+`
+	_apisixConsumerKeyAuth = `
+  apiVersion: apisix.apache.org/v2beta3
+  kind: ApisixConsumer
+  metadata:
+    name: %s
+  spec:
+    authParameter:
+      keyAuth:
+        value:
+          key: %s
+  `
+)
+
+func (s *Scaffold) ApisixConsumerBasicAuthCreated(name, username, password string) error {
+	ac := fmt.Sprintf(_apisixConsumerBasicAuth, name, username, password)
+	return s.CreateResourceFromString(ac)
+}
+
+func (s *Scaffold) ApisixConsumerKeyAuthCreated(name, key string) error {
+	ac := fmt.Sprintf(_apisixConsumerKeyAuth, name, key)
 	return s.CreateResourceFromString(ac)
 }
diff --git a/test/e2e/suite-annotations/authorization.go b/test/e2e/suite-annotations/authorization.go
new file mode 100644
index 00000000..d1818651
--- /dev/null
+++ b/test/e2e/suite-annotations/authorization.go
@@ -0,0 +1,310 @@
+// 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 annotations
+
+import (
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/onsi/ginkgo"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = ginkgo.Describe("suite-annotations: authorization annotations", func() {
+	s := scaffold.NewDefaultScaffold()
+
+	ginkgo.It("enable keyAuth in ingress networking/v1", func() {
+		err := s.ApisixConsumerKeyAuthCreated("foo", "bar")
+		assert.Nil(ginkgo.GinkgoT(), err, "creating keyAuth ApisixConsumer")
+
+		// Wait until the ApisixConsumer create event was delivered.
+		time.Sleep(6 * time.Second)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/auth-type: "keyAuth"
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          service:
+            name: %s
+            port:
+              number: %d
+`, backendSvc, backendPort[0])
+		err = s.CreateResourceFromString(ing)
+		assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+		time.Sleep(5 * time.Second)
+
+		msg401 := s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusUnauthorized).
+			Body().
+			Raw()
+		assert.Contains(ginkgo.GinkgoT(), msg401, "Missing API key found in request")
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			WithHeader("apikey", "bar").
+			Expect().
+			Status(http.StatusOK)
+	})
+
+	ginkgo.It("enable keyAuth in ingress networking/v1beta1", func() {
+		err := s.ApisixConsumerKeyAuthCreated("foo", "bar")
+		assert.Nil(ginkgo.GinkgoT(), err, "creating keyAuth ApisixConsumer")
+
+		// Wait until the ApisixConsumer create event was delivered.
+		time.Sleep(6 * time.Second)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/auth-type: "keyAuth"
+  name: ingress-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+		err = s.CreateResourceFromString(ing)
+		assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+		time.Sleep(5 * time.Second)
+
+		msg401 := s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusUnauthorized).
+			Body().
+			Raw()
+		assert.Contains(ginkgo.GinkgoT(), msg401, "Missing API key found in request")
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			WithHeader("apikey", "bar").
+			Expect().
+			Status(http.StatusOK)
+	})
+
+	ginkgo.It("enable keyAuth in ingress extensions/v1beta1", func() {
+		err := s.ApisixConsumerKeyAuthCreated("foo", "bar")
+		assert.Nil(ginkgo.GinkgoT(), err, "creating keyAuth ApisixConsumer")
+
+		// Wait until the ApisixConsumer create event was delivered.
+		time.Sleep(6 * time.Second)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/auth-type: "keyAuth"
+  name: ingress-extensions-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+		err = s.CreateResourceFromString(ing)
+		assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+		time.Sleep(5 * time.Second)
+
+		msg401 := s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusUnauthorized).
+			Body().
+			Raw()
+		assert.Contains(ginkgo.GinkgoT(), msg401, "Missing API key found in request")
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			WithHeader("apikey", "bar").
+			Expect().
+			Status(http.StatusOK)
+	})
+
+	ginkgo.It("enable basicAuth in ingress networking/v1", func() {
+		err := s.ApisixConsumerBasicAuthCreated("jack1", "jack1-username", "jack1-password")
+		assert.Nil(ginkgo.GinkgoT(), err, "creating keyAuth ApisixConsumer")
+
+		// Wait until the ApisixConsumer create event was delivered.
+		time.Sleep(6 * time.Second)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/auth-type: "basicAuth"
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          service:
+            name: %s
+            port:
+              number: %d
+`, backendSvc, backendPort[0])
+		err = s.CreateResourceFromString(ing)
+		assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+		time.Sleep(5 * time.Second)
+
+		msg401 := s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusUnauthorized).
+			Body().
+			Raw()
+		assert.Contains(ginkgo.GinkgoT(), msg401, "Missing authorization in request")
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			WithHeader("Authorization", "Basic amFjazEtdXNlcm5hbWU6amFjazEtcGFzc3dvcmQ=").
+			Expect().
+			Status(http.StatusOK)
+	})
+
+	ginkgo.It("enable basicAuth in ingress networking/v1beta1", func() {
+		err := s.ApisixConsumerBasicAuthCreated("jack1", "jack1-username", "jack1-password")
+		assert.Nil(ginkgo.GinkgoT(), err, "creating keyAuth ApisixConsumer")
+
+		// Wait until the ApisixConsumer create event was delivered.
+		time.Sleep(6 * time.Second)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/auth-type: "basicAuth"
+  name: ingress-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+		err = s.CreateResourceFromString(ing)
+		assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+		time.Sleep(5 * time.Second)
+
+		msg401 := s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusUnauthorized).
+			Body().
+			Raw()
+		assert.Contains(ginkgo.GinkgoT(), msg401, "Missing authorization in request")
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			WithHeader("Authorization", "Basic amFjazEtdXNlcm5hbWU6amFjazEtcGFzc3dvcmQ=").
+			Expect().
+			Status(http.StatusOK)
+	})
+
+	ginkgo.It("enable basicAuth in ingress networking/v1beta1", func() {
+		err := s.ApisixConsumerBasicAuthCreated("jack1", "jack1-username", "jack1-password")
+		assert.Nil(ginkgo.GinkgoT(), err, "creating keyAuth ApisixConsumer")
+
+		// Wait until the ApisixConsumer create event was delivered.
+		time.Sleep(6 * time.Second)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/auth-type: "basicAuth"
+  name: ingress-extensions-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /ip
+        pathType: Exact
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+		err = s.CreateResourceFromString(ing)
+		assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+		time.Sleep(5 * time.Second)
+
+		msg401 := s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusUnauthorized).
+			Body().
+			Raw()
+		assert.Contains(ginkgo.GinkgoT(), msg401, "Missing authorization in request")
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			WithHeader("Authorization", "Basic amFjazEtdXNlcm5hbWU6amFjazEtcGFzc3dvcmQ=").
+			Expect().
+			Status(http.StatusOK)
+	})
+})