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/26 14:23:55 UTC
[apisix-ingress-controller] branch master updated: feat: add hmac-auth authorization method (#1035)
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 59ba41a8 feat: add hmac-auth authorization method (#1035)
59ba41a8 is described below
commit 59ba41a8c5780b9273610e944aabda700078a3db
Author: Fatpa <fa...@gmail.com>
AuthorDate: Thu May 26 22:23:50 2022 +0800
feat: add hmac-auth authorization method (#1035)
---
.gitignore | 1 +
pkg/kube/apisix/apis/config/v2/types.go | 20 ++
.../apisix/apis/config/v2/zz_generated.deepcopy.go | 52 +++++
pkg/kube/apisix/apis/config/v2beta3/types.go | 20 ++
.../apis/config/v2beta3/zz_generated.deepcopy.go | 52 +++++
pkg/kube/translation/apisix_consumer.go | 12 ++
pkg/kube/translation/apisix_consumer_test.go | 46 +++++
pkg/kube/translation/apisix_route.go | 8 +
pkg/kube/translation/plugin.go | 217 +++++++++++++++++++++
pkg/kube/translation/plugin_test.go | 104 +++++++++-
pkg/types/apisix/v1/plugin_types.go | 15 ++
pkg/types/apisix/v1/zz_generated.deepcopy.go | 21 ++
samples/deploy/crd/v1/ApisixConsumer.yaml | 43 +++-
samples/deploy/crd/v1/ApisixRoute.yaml | 118 +++++++----
test/e2e/suite-features/consumer.go | 202 +++++++++++++++++++
tools.go | 1 +
16 files changed, 887 insertions(+), 45 deletions(-)
diff --git a/.gitignore b/.gitignore
index 5fbca57d..aa6584f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@
*.out
release
+.vscode
.idea
.DS_Store
coverage.txt
diff --git a/pkg/kube/apisix/apis/config/v2/types.go b/pkg/kube/apisix/apis/config/v2/types.go
index aeace4d6..bd841298 100644
--- a/pkg/kube/apisix/apis/config/v2/types.go
+++ b/pkg/kube/apisix/apis/config/v2/types.go
@@ -341,6 +341,7 @@ type ApisixConsumerAuthParameter struct {
KeyAuth *ApisixConsumerKeyAuth `json:"keyAuth,omitempty" yaml:"keyAuth"`
WolfRBAC *ApisixConsumerWolfRBAC `json:"wolfRBAC,omitempty" yaml:"wolfRBAC"`
JwtAuth *ApisixConsumerJwtAuth `json:"jwtAuth,omitempty" yaml:"jwtAuth"`
+ HMACAuth *ApisixConsumerHMACAuth `json:"hmacAuth,omitempty" yaml:"hmacAuth"`
}
// ApisixConsumerBasicAuth defines the configuration for basic auth.
@@ -396,6 +397,25 @@ type ApisixConsumerJwtAuthValue struct {
Base64Secret bool `json:"base64_secret,omitempty" yaml:"base64_secret,omitempty"`
}
+// ApisixConsumerHMACAuth defines the configuration for the hmac auth.
+type ApisixConsumerHMACAuth struct {
+ SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty" yaml:"secretRef,omitempty"`
+ Value *ApisixConsumerHMACAuthValue `json:"value,omitempty" yaml:"value,omitempty"`
+}
+
+// ApisixConsumerHMACAuthValue defines the in-place configuration for hmac auth.
+type ApisixConsumerHMACAuthValue struct {
+ AccessKey string `json:"access_key" yaml:"access_key"`
+ SecretKey string `json:"secret_key" yaml:"secret_key"`
+ Algorithm string `json:"algorithm,omitempty" yaml:"algorithm,omitempty"`
+ ClockSkew int64 `json:"clock_skew,omitempty" yaml:"clock_skew,omitempty"`
+ SignedHeaders []string `json:"signed_headers,omitempty" yaml:"signed_headers,omitempty"`
+ KeepHeaders bool `json:"keep_headers,omitempty" yaml:"keep_headers,omitempty"`
+ EncodeURIParams bool `json:"encode_uri_params,omitempty" yaml:"encode_uri_params,omitempty"`
+ ValidateRequestBody bool `json:"validate_request_body,omitempty" yaml:"validate_request_body,omitempty"`
+ MaxReqBody int64 `json:"max_req_body,omitempty" yaml:"max_req_body,omitempty"`
+}
+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ApisixConsumerList contains a list of ApisixConsumer.
diff --git a/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go b/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go
index 4815aaa5..f60dffd6 100644
--- a/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go
+++ b/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go
@@ -302,6 +302,11 @@ func (in *ApisixConsumerAuthParameter) DeepCopyInto(out *ApisixConsumerAuthParam
*out = new(ApisixConsumerJwtAuth)
(*in).DeepCopyInto(*out)
}
+ if in.HMACAuth != nil {
+ in, out := &in.HMACAuth, &out.HMACAuth
+ *out = new(ApisixConsumerHMACAuth)
+ (*in).DeepCopyInto(*out)
+ }
return
}
@@ -357,6 +362,53 @@ func (in *ApisixConsumerBasicAuthValue) DeepCopy() *ApisixConsumerBasicAuthValue
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixConsumerHMACAuth) DeepCopyInto(out *ApisixConsumerHMACAuth) {
+ *out = *in
+ if in.SecretRef != nil {
+ in, out := &in.SecretRef, &out.SecretRef
+ *out = new(v1.LocalObjectReference)
+ **out = **in
+ }
+ if in.Value != nil {
+ in, out := &in.Value, &out.Value
+ *out = new(ApisixConsumerHMACAuthValue)
+ (*in).DeepCopyInto(*out)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixConsumerHMACAuth.
+func (in *ApisixConsumerHMACAuth) DeepCopy() *ApisixConsumerHMACAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(ApisixConsumerHMACAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixConsumerHMACAuthValue) DeepCopyInto(out *ApisixConsumerHMACAuthValue) {
+ *out = *in
+ if in.SignedHeaders != nil {
+ in, out := &in.SignedHeaders, &out.SignedHeaders
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixConsumerHMACAuthValue.
+func (in *ApisixConsumerHMACAuthValue) DeepCopy() *ApisixConsumerHMACAuthValue {
+ if in == nil {
+ return nil
+ }
+ out := new(ApisixConsumerHMACAuthValue)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApisixConsumerJwtAuth) DeepCopyInto(out *ApisixConsumerJwtAuth) {
*out = *in
diff --git a/pkg/kube/apisix/apis/config/v2beta3/types.go b/pkg/kube/apisix/apis/config/v2beta3/types.go
index 07a3a927..36b8dbb7 100644
--- a/pkg/kube/apisix/apis/config/v2beta3/types.go
+++ b/pkg/kube/apisix/apis/config/v2beta3/types.go
@@ -342,6 +342,7 @@ type ApisixConsumerAuthParameter struct {
KeyAuth *ApisixConsumerKeyAuth `json:"keyAuth,omitempty" yaml:"keyAuth"`
WolfRBAC *ApisixConsumerWolfRBAC `json:"wolfRBAC,omitempty" yaml:"wolfRBAC"`
JwtAuth *ApisixConsumerJwtAuth `json:"jwtAuth,omitempty" yaml:"jwtAuth"`
+ HMACAuth *ApisixConsumerHMACAuth `json:"hmacAuth,omitempty" yaml:"hmacAuth"`
}
// ApisixConsumerBasicAuth defines the configuration for basic auth.
@@ -397,6 +398,25 @@ type ApisixConsumerJwtAuthValue struct {
Base64Secret bool `json:"base64_secret,omitempty" yaml:"base64_secret,omitempty"`
}
+// ApisixConsumerHMACAuth defines the configuration for the hmac auth.
+type ApisixConsumerHMACAuth struct {
+ SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty" yaml:"secretRef,omitempty"`
+ Value *ApisixConsumerHMACAuthValue `json:"value,omitempty" yaml:"value,omitempty"`
+}
+
+// ApisixConsumerHMACAuthValue defines the in-place configuration for hmac auth.
+type ApisixConsumerHMACAuthValue struct {
+ AccessKey string `json:"access_key" yaml:"access_key"`
+ SecretKey string `json:"secret_key" yaml:"secret_key"`
+ Algorithm string `json:"algorithm,omitempty" yaml:"algorithm,omitempty"`
+ ClockSkew int64 `json:"clock_skew,omitempty" yaml:"clock_skew,omitempty"`
+ SignedHeaders []string `json:"signed_headers,omitempty" yaml:"signed_headers,omitempty"`
+ KeepHeaders bool `json:"keep_headers,omitempty" yaml:"keep_headers,omitempty"`
+ EncodeURIParams bool `json:"encode_uri_params,omitempty" yaml:"encode_uri_params,omitempty"`
+ ValidateRequestBody bool `json:"validate_request_body,omitempty" yaml:"validate_request_body,omitempty"`
+ MaxReqBody int64 `json:"max_req_body,omitempty" yaml:"max_req_body,omitempty"`
+}
+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ApisixConsumerList contains a list of ApisixConsumer.
diff --git a/pkg/kube/apisix/apis/config/v2beta3/zz_generated.deepcopy.go b/pkg/kube/apisix/apis/config/v2beta3/zz_generated.deepcopy.go
index 9682c936..80391804 100644
--- a/pkg/kube/apisix/apis/config/v2beta3/zz_generated.deepcopy.go
+++ b/pkg/kube/apisix/apis/config/v2beta3/zz_generated.deepcopy.go
@@ -303,6 +303,11 @@ func (in *ApisixConsumerAuthParameter) DeepCopyInto(out *ApisixConsumerAuthParam
*out = new(ApisixConsumerJwtAuth)
(*in).DeepCopyInto(*out)
}
+ if in.HMACAuth != nil {
+ in, out := &in.HMACAuth, &out.HMACAuth
+ *out = new(ApisixConsumerHMACAuth)
+ (*in).DeepCopyInto(*out)
+ }
return
}
@@ -358,6 +363,53 @@ func (in *ApisixConsumerBasicAuthValue) DeepCopy() *ApisixConsumerBasicAuthValue
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixConsumerHMACAuth) DeepCopyInto(out *ApisixConsumerHMACAuth) {
+ *out = *in
+ if in.SecretRef != nil {
+ in, out := &in.SecretRef, &out.SecretRef
+ *out = new(v1.LocalObjectReference)
+ **out = **in
+ }
+ if in.Value != nil {
+ in, out := &in.Value, &out.Value
+ *out = new(ApisixConsumerHMACAuthValue)
+ (*in).DeepCopyInto(*out)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixConsumerHMACAuth.
+func (in *ApisixConsumerHMACAuth) DeepCopy() *ApisixConsumerHMACAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(ApisixConsumerHMACAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixConsumerHMACAuthValue) DeepCopyInto(out *ApisixConsumerHMACAuthValue) {
+ *out = *in
+ if in.SignedHeaders != nil {
+ in, out := &in.SignedHeaders, &out.SignedHeaders
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixConsumerHMACAuthValue.
+func (in *ApisixConsumerHMACAuthValue) DeepCopy() *ApisixConsumerHMACAuthValue {
+ if in == nil {
+ return nil
+ }
+ out := new(ApisixConsumerHMACAuthValue)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApisixConsumerJwtAuth) DeepCopyInto(out *ApisixConsumerJwtAuth) {
*out = *in
diff --git a/pkg/kube/translation/apisix_consumer.go b/pkg/kube/translation/apisix_consumer.go
index 982b741f..275f243b 100644
--- a/pkg/kube/translation/apisix_consumer.go
+++ b/pkg/kube/translation/apisix_consumer.go
@@ -51,6 +51,12 @@ func (t *translator) TranslateApisixConsumerV2beta3(ac *configv2beta3.ApisixCons
return nil, fmt.Errorf("invalid wolf rbac config: %s", err)
}
plugins["wolf-rbac"] = cfg
+ } else if ac.Spec.AuthParameter.HMACAuth != nil {
+ cfg, err := t.translateConsumerHMACAuthPluginV2beta3(ac.Namespace, ac.Spec.AuthParameter.HMACAuth)
+ if err != nil {
+ return nil, fmt.Errorf("invaild hmac auth config: %s", err)
+ }
+ plugins["hmac-auth"] = cfg
}
consumer := apisixv1.NewDefaultConsumer()
@@ -88,6 +94,12 @@ func (t *translator) TranslateApisixConsumerV2(ac *configv2.ApisixConsumer) (*ap
return nil, fmt.Errorf("invalid wolf rbac config: %s", err)
}
plugins["wolf-rbac"] = cfg
+ } else if ac.Spec.AuthParameter.HMACAuth != nil {
+ cfg, err := t.translateConsumerHMACAuthPluginV2(ac.Namespace, ac.Spec.AuthParameter.HMACAuth)
+ if err != nil {
+ return nil, fmt.Errorf("invaild hmac auth config: %s", err)
+ }
+ plugins["hmac-auth"] = cfg
}
consumer := apisixv1.NewDefaultConsumer()
diff --git a/pkg/kube/translation/apisix_consumer_test.go b/pkg/kube/translation/apisix_consumer_test.go
index a875c8a6..1405f2e6 100644
--- a/pkg/kube/translation/apisix_consumer_test.go
+++ b/pkg/kube/translation/apisix_consumer_test.go
@@ -126,6 +126,29 @@ func TestTranslateApisixConsumerV2beta3(t *testing.T) {
assert.Equal(t, "https://httpbin.org", cfg4.Server)
assert.Equal(t, "test01", cfg4.Appid)
+ ac = &configv2beta3.ApisixConsumer{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "jack",
+ Namespace: "qa",
+ },
+ Spec: configv2beta3.ApisixConsumerSpec{
+ AuthParameter: configv2beta3.ApisixConsumerAuthParameter{
+ HMACAuth: &configv2beta3.ApisixConsumerHMACAuth{
+ Value: &configv2beta3.ApisixConsumerHMACAuthValue{
+ AccessKey: "foo",
+ SecretKey: "bar",
+ },
+ },
+ },
+ },
+ }
+ consumer, err = (&translator{}).TranslateApisixConsumerV2beta3(ac)
+ assert.Nil(t, err)
+ assert.Len(t, consumer.Plugins, 1)
+ cfg5 := consumer.Plugins["hmac-auth"].(*apisixv1.HMACAuthConsumerConfig)
+ assert.Equal(t, "foo", cfg5.AccessKey)
+ assert.Equal(t, "bar", cfg5.SecretKey)
+
// No test test cases for secret references as we already test them
// in plugin_test.go.
}
@@ -231,6 +254,29 @@ func TestTranslateApisixConsumerV2(t *testing.T) {
assert.Equal(t, "https://httpbin.org", cfg4.Server)
assert.Equal(t, "test01", cfg4.Appid)
+ ac = &configv2.ApisixConsumer{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "jack",
+ Namespace: "qa",
+ },
+ Spec: configv2.ApisixConsumerSpec{
+ AuthParameter: configv2.ApisixConsumerAuthParameter{
+ HMACAuth: &configv2.ApisixConsumerHMACAuth{
+ Value: &configv2.ApisixConsumerHMACAuthValue{
+ AccessKey: "foo",
+ SecretKey: "bar",
+ },
+ },
+ },
+ },
+ }
+ consumer, err = (&translator{}).TranslateApisixConsumerV2(ac)
+ assert.Nil(t, err)
+ assert.Len(t, consumer.Plugins, 1)
+ cfg5 := consumer.Plugins["hmac-auth"].(*apisixv1.HMACAuthConsumerConfig)
+ assert.Equal(t, "foo", cfg5.AccessKey)
+ assert.Equal(t, "bar", cfg5.SecretKey)
+
// No test test cases for secret references as we already test them
// in plugin_test.go.
}
diff --git a/pkg/kube/translation/apisix_route.go b/pkg/kube/translation/apisix_route.go
index e7dc23c8..5dafd842 100644
--- a/pkg/kube/translation/apisix_route.go
+++ b/pkg/kube/translation/apisix_route.go
@@ -276,6 +276,8 @@ func (t *translator) translateHTTPRouteV2beta3(ctx *TranslateContext, ar *config
pluginMap["wolf-rbac"] = make(map[string]interface{})
case "jwtAuth":
pluginMap["jwt-auth"] = part.Authentication.JwtAuth
+ case "hmacAuth":
+ pluginMap["hmac-auth"] = make(map[string]interface{})
default:
pluginMap["basic-auth"] = make(map[string]interface{})
}
@@ -410,6 +412,8 @@ func (t *translator) translateHTTPRouteV2(ctx *TranslateContext, ar *configv2.Ap
pluginMap["wolf-rbac"] = make(map[string]interface{})
case "jwtAuth":
pluginMap["jwt-auth"] = part.Authentication.JwtAuth
+ case "hmacAuth":
+ pluginMap["hmac-auth"] = make(map[string]interface{})
default:
pluginMap["basic-auth"] = make(map[string]interface{})
}
@@ -635,6 +639,8 @@ func (t *translator) translateHTTPRouteV2beta3NotStrictly(ctx *TranslateContext,
pluginMap["wolf-rbac"] = make(map[string]interface{})
case "jwtAuth":
pluginMap["jwt-auth"] = part.Authentication.JwtAuth
+ case "hmacAuth":
+ pluginMap["hmac-auth"] = make(map[string]interface{})
default:
pluginMap["basic-auth"] = make(map[string]interface{})
}
@@ -692,6 +698,8 @@ func (t *translator) translateHTTPRouteV2NotStrictly(ctx *TranslateContext, ar *
pluginMap["wolf-rbac"] = make(map[string]interface{})
case "jwtAuth":
pluginMap["jwt-auth"] = part.Authentication.JwtAuth
+ case "hmacAuth":
+ pluginMap["hmac-auth"] = make(map[string]interface{})
default:
pluginMap["basic-auth"] = make(map[string]interface{})
}
diff --git a/pkg/kube/translation/plugin.go b/pkg/kube/translation/plugin.go
index 5b532932..0340a2d5 100644
--- a/pkg/kube/translation/plugin.go
+++ b/pkg/kube/translation/plugin.go
@@ -29,6 +29,13 @@ var (
_errPasswordNotFoundOrInvalid = errors.New("key \"password\" not found or invalid in secret")
_jwtAuthExpDefaultValue = int64(868400)
+
+ _hmacAuthAlgorithmDefaultValue = "hmac-sha256"
+ _hmacAuthClockSkewDefaultValue = int64(0)
+ _hmacAuthKeepHeadersDefaultValue = false
+ _hmacAuthEncodeURIParamsDefaultValue = true
+ _hmacAuthValidateRequestBodyDefaultValue = false
+ _hmacAuthMaxReqBodyDefaultValue = int64(524288)
)
func (t *translator) translateTrafficSplitPlugin(ctx *TranslateContext, ns string, defaultBackendWeight int,
@@ -302,3 +309,213 @@ func (t *translator) translateConsumerJwtAuthPluginV2(consumerNamespace string,
Base64Secret: base64Secret,
}, nil
}
+
+func (t *translator) translateConsumerHMACAuthPluginV2beta3(consumerNamespace string, cfg *configv2beta3.ApisixConsumerHMACAuth) (*apisixv1.HMACAuthConsumerConfig, error) {
+ if cfg.Value != nil {
+ return &apisixv1.HMACAuthConsumerConfig{
+ AccessKey: cfg.Value.AccessKey,
+ SecretKey: cfg.Value.SecretKey,
+ Algorithm: cfg.Value.Algorithm,
+ ClockSkew: cfg.Value.ClockSkew,
+ SignedHeaders: cfg.Value.SignedHeaders,
+ KeepHeaders: cfg.Value.KeepHeaders,
+ EncodeURIParams: cfg.Value.EncodeURIParams,
+ ValidateRequestBody: cfg.Value.ValidateRequestBody,
+ MaxReqBody: cfg.Value.MaxReqBody,
+ }, nil
+ }
+
+ sec, err := t.SecretLister.Secrets(consumerNamespace).Get(cfg.SecretRef.Name)
+ if err != nil {
+ return nil, err
+ }
+
+ accessKeyRaw, ok := sec.Data["access_key"]
+ if !ok || len(accessKeyRaw) == 0 {
+ return nil, _errKeyNotFoundOrInvalid
+ }
+
+ secretKeyRaw, ok := sec.Data["secret_key"]
+ if !ok || len(secretKeyRaw) == 0 {
+ return nil, _errKeyNotFoundOrInvalid
+ }
+
+ algorithmRaw, ok := sec.Data["algorithm"]
+ var algorithm string
+ if !ok {
+ algorithm = _hmacAuthAlgorithmDefaultValue
+ } else {
+ algorithm = string(algorithmRaw)
+ }
+
+ clockSkewRaw := sec.Data["clock_skew"]
+ clockSkew, _ := strconv.ParseInt(string(clockSkewRaw), 10, 64)
+ if clockSkew < 0 {
+ clockSkew = _hmacAuthClockSkewDefaultValue
+ }
+
+ var signedHeaders []string
+ signedHeadersRaw := sec.Data["signed_headers"]
+ for _, b := range signedHeadersRaw {
+ signedHeaders = append(signedHeaders, string(b))
+ }
+
+ var keepHeader bool
+ keepHeaderRaw, ok := sec.Data["keep_headers"]
+ if !ok {
+ keepHeader = _hmacAuthKeepHeadersDefaultValue
+ } else {
+ if string(keepHeaderRaw) == "true" {
+ keepHeader = true
+ } else {
+ keepHeader = false
+ }
+ }
+
+ var encodeURIParams bool
+ encodeURIParamsRaw, ok := sec.Data["encode_uri_params"]
+ if !ok {
+ encodeURIParams = _hmacAuthEncodeURIParamsDefaultValue
+ } else {
+ if string(encodeURIParamsRaw) == "true" {
+ encodeURIParams = true
+ } else {
+ encodeURIParams = false
+ }
+ }
+
+ var validateRequestBody bool
+ validateRequestBodyRaw, ok := sec.Data["validate_request_body"]
+ if !ok {
+ validateRequestBody = _hmacAuthValidateRequestBodyDefaultValue
+ } else {
+ if string(validateRequestBodyRaw) == "true" {
+ validateRequestBody = true
+ } else {
+ validateRequestBody = false
+ }
+ }
+
+ maxReqBodyRaw := sec.Data["max_req_body"]
+ maxReqBody, _ := strconv.ParseInt(string(maxReqBodyRaw), 10, 64)
+ if maxReqBody < 0 {
+ maxReqBody = _hmacAuthMaxReqBodyDefaultValue
+ }
+
+ return &apisixv1.HMACAuthConsumerConfig{
+ AccessKey: string(accessKeyRaw),
+ SecretKey: string(secretKeyRaw),
+ Algorithm: algorithm,
+ ClockSkew: clockSkew,
+ SignedHeaders: signedHeaders,
+ KeepHeaders: keepHeader,
+ EncodeURIParams: encodeURIParams,
+ ValidateRequestBody: validateRequestBody,
+ MaxReqBody: maxReqBody,
+ }, nil
+}
+
+func (t *translator) translateConsumerHMACAuthPluginV2(consumerNamespace string, cfg *configv2.ApisixConsumerHMACAuth) (*apisixv1.HMACAuthConsumerConfig, error) {
+ if cfg.Value != nil {
+ return &apisixv1.HMACAuthConsumerConfig{
+ AccessKey: cfg.Value.AccessKey,
+ SecretKey: cfg.Value.SecretKey,
+ Algorithm: cfg.Value.Algorithm,
+ ClockSkew: cfg.Value.ClockSkew,
+ SignedHeaders: cfg.Value.SignedHeaders,
+ KeepHeaders: cfg.Value.KeepHeaders,
+ EncodeURIParams: cfg.Value.EncodeURIParams,
+ ValidateRequestBody: cfg.Value.ValidateRequestBody,
+ MaxReqBody: cfg.Value.MaxReqBody,
+ }, nil
+ }
+
+ sec, err := t.SecretLister.Secrets(consumerNamespace).Get(cfg.SecretRef.Name)
+ if err != nil {
+ return nil, err
+ }
+
+ accessKeyRaw, ok := sec.Data["access_key"]
+ if !ok || len(accessKeyRaw) == 0 {
+ return nil, _errKeyNotFoundOrInvalid
+ }
+
+ secretKeyRaw, ok := sec.Data["secret_key"]
+ if !ok || len(secretKeyRaw) == 0 {
+ return nil, _errKeyNotFoundOrInvalid
+ }
+
+ algorithmRaw, ok := sec.Data["algorithm"]
+ var algorithm string
+ if !ok {
+ algorithm = _hmacAuthAlgorithmDefaultValue
+ } else {
+ algorithm = string(algorithmRaw)
+ }
+
+ clockSkewRaw := sec.Data["clock_skew"]
+ clockSkew, _ := strconv.ParseInt(string(clockSkewRaw), 10, 64)
+ if clockSkew < 0 {
+ clockSkew = _hmacAuthClockSkewDefaultValue
+ }
+
+ var signedHeaders []string
+ signedHeadersRaw := sec.Data["signed_headers"]
+ for _, b := range signedHeadersRaw {
+ signedHeaders = append(signedHeaders, string(b))
+ }
+
+ var keepHeader bool
+ keepHeaderRaw, ok := sec.Data["keep_headers"]
+ if !ok {
+ keepHeader = _hmacAuthKeepHeadersDefaultValue
+ } else {
+ if string(keepHeaderRaw) == "true" {
+ keepHeader = true
+ } else {
+ keepHeader = false
+ }
+ }
+
+ var encodeURIParams bool
+ encodeURIParamsRaw, ok := sec.Data["encode_uri_params"]
+ if !ok {
+ encodeURIParams = _hmacAuthEncodeURIParamsDefaultValue
+ } else {
+ if string(encodeURIParamsRaw) == "true" {
+ encodeURIParams = true
+ } else {
+ encodeURIParams = false
+ }
+ }
+
+ var validateRequestBody bool
+ validateRequestBodyRaw, ok := sec.Data["validate_request_body"]
+ if !ok {
+ validateRequestBody = _hmacAuthValidateRequestBodyDefaultValue
+ } else {
+ if string(validateRequestBodyRaw) == "true" {
+ validateRequestBody = true
+ } else {
+ validateRequestBody = false
+ }
+ }
+
+ maxReqBodyRaw := sec.Data["max_req_body"]
+ maxReqBody, _ := strconv.ParseInt(string(maxReqBodyRaw), 10, 64)
+ if maxReqBody < 0 {
+ maxReqBody = _hmacAuthMaxReqBodyDefaultValue
+ }
+
+ return &apisixv1.HMACAuthConsumerConfig{
+ AccessKey: string(accessKeyRaw),
+ SecretKey: string(secretKeyRaw),
+ Algorithm: algorithm,
+ ClockSkew: clockSkew,
+ SignedHeaders: signedHeaders,
+ KeepHeaders: keepHeader,
+ EncodeURIParams: encodeURIParams,
+ ValidateRequestBody: validateRequestBody,
+ MaxReqBody: maxReqBody,
+ }, nil
+}
diff --git a/pkg/kube/translation/plugin_test.go b/pkg/kube/translation/plugin_test.go
index 94cbc9ee..54f0bab1 100644
--- a/pkg/kube/translation/plugin_test.go
+++ b/pkg/kube/translation/plugin_test.go
@@ -789,7 +789,7 @@ func TestTranslateConsumerJwtAuthWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
+ _, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
assert.Nil(t, err)
delete(sec.Data, "public")
@@ -797,7 +797,7 @@ func TestTranslateConsumerJwtAuthWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
+ _, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
assert.Nil(t, err)
delete(sec.Data, "private")
@@ -805,7 +805,7 @@ func TestTranslateConsumerJwtAuthWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
+ _, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
assert.Nil(t, err)
delete(sec.Data, "algorithm")
@@ -813,7 +813,7 @@ func TestTranslateConsumerJwtAuthWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
+ _, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
assert.Nil(t, err)
delete(sec.Data, "exp")
@@ -821,7 +821,7 @@ func TestTranslateConsumerJwtAuthWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
+ _, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
assert.Nil(t, err)
delete(sec.Data, "base64_secret")
@@ -829,7 +829,7 @@ func TestTranslateConsumerJwtAuthWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
+ _, err = tr.translateConsumerJwtAuthPluginV2beta3("default", jwtAuth)
assert.Nil(t, err)
delete(sec.Data, "key")
@@ -914,7 +914,7 @@ func TestTranslateConsumerWolfRBACWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerWolfRBACPluginV2beta3("default", wolfRBAC)
+ _, err = tr.translateConsumerWolfRBACPluginV2beta3("default", wolfRBAC)
assert.Nil(t, err)
delete(sec.Data, "appid")
@@ -922,7 +922,7 @@ func TestTranslateConsumerWolfRBACWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerWolfRBACPluginV2beta3("default", wolfRBAC)
+ _, err = tr.translateConsumerWolfRBACPluginV2beta3("default", wolfRBAC)
assert.Nil(t, err)
delete(sec.Data, "header_prefix")
@@ -930,9 +930,95 @@ func TestTranslateConsumerWolfRBACWithSecretRef(t *testing.T) {
assert.Nil(t, err)
<-processCh
- cfg, err = tr.translateConsumerWolfRBACPluginV2beta3("default", wolfRBAC)
+ _, err = tr.translateConsumerWolfRBACPluginV2beta3("default", wolfRBAC)
assert.Nil(t, err)
close(processCh)
close(stopCh)
}
+
+func TestTranslateConsumerHMACAuthPluginWithInPlaceValue(t *testing.T) {
+ hmacAuth := &configv2beta3.ApisixConsumerHMACAuth{
+ Value: &configv2beta3.ApisixConsumerHMACAuthValue{
+ AccessKey: "foo",
+ SecretKey: "foo-secret",
+ ClockSkew: 0,
+ SignedHeaders: []string{"User-Agent"},
+ },
+ }
+ cfg, err := (&translator{}).translateConsumerHMACAuthPluginV2beta3("default", hmacAuth)
+ assert.Nil(t, err)
+ assert.Equal(t, "foo", cfg.AccessKey)
+ assert.Equal(t, "foo-secret", cfg.SecretKey)
+ assert.Equal(t, int64(0), cfg.ClockSkew)
+ assert.Equal(t, []string{"User-Agent"}, cfg.SignedHeaders)
+}
+
+func TestTranslateConsumerHMACAuthPluginWithSecretRef(t *testing.T) {
+ sec := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fatpa-hmac-auth",
+ },
+ Data: map[string][]byte{
+ "access_key": []byte("foo"),
+ "secret_key": []byte("foo-secret"),
+ "clock_skew": []byte("0"),
+ },
+ }
+
+ client := fake.NewSimpleClientset()
+ informersFactory := informers.NewSharedInformerFactory(client, 0)
+ secretInformer := informersFactory.Core().V1().Secrets().Informer()
+ secretLister := informersFactory.Core().V1().Secrets().Lister()
+ processCh := make(chan struct{})
+ stopCh := make(chan struct{})
+ secretInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: func(_ interface{}) {
+ processCh <- struct{}{}
+ },
+ UpdateFunc: func(_, _ interface{}) {
+ processCh <- struct{}{}
+ },
+ })
+ go secretInformer.Run(stopCh)
+
+ tr := &translator{
+ &TranslatorOptions{
+ SecretLister: secretLister,
+ },
+ }
+ _, err := client.CoreV1().Secrets("default").Create(context.Background(), sec, metav1.CreateOptions{})
+ assert.Nil(t, err)
+
+ <-processCh
+
+ hmacAuth := &configv2beta3.ApisixConsumerHMACAuth{
+ SecretRef: &corev1.LocalObjectReference{Name: "fatpa-hmac-auth"},
+ }
+ cfg, err := tr.translateConsumerHMACAuthPluginV2beta3("default", hmacAuth)
+ assert.Nil(t, err)
+ assert.Equal(t, "foo", cfg.AccessKey)
+ assert.Equal(t, "foo-secret", cfg.SecretKey)
+ assert.Equal(t, int64(0), cfg.ClockSkew)
+
+ delete(sec.Data, "access_key")
+ _, err = client.CoreV1().Secrets("default").Update(context.Background(), sec, metav1.UpdateOptions{})
+ assert.Nil(t, err)
+ <-processCh
+
+ cfg, err = tr.translateConsumerHMACAuthPluginV2beta3("default", hmacAuth)
+ assert.Nil(t, cfg)
+ assert.Equal(t, _errKeyNotFoundOrInvalid, err)
+
+ delete(sec.Data, "secret_key")
+ _, err = client.CoreV1().Secrets("default").Update(context.Background(), sec, metav1.UpdateOptions{})
+ assert.Nil(t, err)
+ <-processCh
+
+ cfg, err = tr.translateConsumerHMACAuthPluginV2beta3("default", hmacAuth)
+ assert.Nil(t, cfg)
+ assert.Equal(t, _errKeyNotFoundOrInvalid, err)
+
+ close(processCh)
+ close(stopCh)
+}
diff --git a/pkg/types/apisix/v1/plugin_types.go b/pkg/types/apisix/v1/plugin_types.go
index d46b3b50..58591293 100644
--- a/pkg/types/apisix/v1/plugin_types.go
+++ b/pkg/types/apisix/v1/plugin_types.go
@@ -83,6 +83,21 @@ type JwtAuthConsumerConfig struct {
Base64Secret bool `json:"base64_secret,omitempty" yaml:"base64_secret,omitempty"`
}
+// HMACAuthConsumerConfig is the rule config for hmac-auth plugin
+// used in Consumer object.
+// +k8s:deepcopy-gen=true
+type HMACAuthConsumerConfig struct {
+ AccessKey string `json:"access_key" yaml:"access_key"`
+ SecretKey string `json:"secret_key" yaml:"secret_key"`
+ Algorithm string `json:"algorithm,omitempty" yaml:"algorithm,omitempty"`
+ ClockSkew int64 `json:"clock_skew,omitempty" yaml:"clock_skew,omitempty"`
+ SignedHeaders []string `json:"signed_headers,omitempty" yaml:"signed_headers,omitempty"`
+ KeepHeaders bool `json:"keep_headers,omitempty" yaml:"keep_headers,omitempty"`
+ EncodeURIParams bool `json:"encode_uri_params,omitempty" yaml:"encode_uri_params,omitempty"`
+ ValidateRequestBody bool `json:"validate_request_body,omitempty" yaml:"validate_request_body,omitempty"`
+ MaxReqBody int64 `json:"max_req_body,omitempty" yaml:"max_req_body,omitempty"`
+}
+
// BasicAuthRouteConfig is the rule config for basic-auth plugin
// used in Route object.
// +k8s:deepcopy-gen=true
diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go b/pkg/types/apisix/v1/zz_generated.deepcopy.go
index 480cd434..2edb5286 100644
--- a/pkg/types/apisix/v1/zz_generated.deepcopy.go
+++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go
@@ -155,6 +155,27 @@ func (in *GlobalRule) DeepCopy() *GlobalRule {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *HMACAuthConsumerConfig) DeepCopyInto(out *HMACAuthConsumerConfig) {
+ *out = *in
+ if in.SignedHeaders != nil {
+ in, out := &in.SignedHeaders, &out.SignedHeaders
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HMACAuthConsumerConfig.
+func (in *HMACAuthConsumerConfig) DeepCopy() *HMACAuthConsumerConfig {
+ if in == nil {
+ return nil
+ }
+ out := new(HMACAuthConsumerConfig)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IPRestrictConfig) DeepCopyInto(out *IPRestrictConfig) {
*out = *in
diff --git a/samples/deploy/crd/v1/ApisixConsumer.yaml b/samples/deploy/crd/v1/ApisixConsumer.yaml
index 4c967194..32498386 100644
--- a/samples/deploy/crd/v1/ApisixConsumer.yaml
+++ b/samples/deploy/crd/v1/ApisixConsumer.yaml
@@ -51,6 +51,7 @@ spec:
- required: ["keyAuth"]
- required: ["wolfRBAC"]
- required: ["jwtAuth"]
+ - required: ["hmacAuth"]
properties:
basicAuth:
type: object
@@ -122,7 +123,7 @@ spec:
type: string
exp:
type: integer
- base64_secret:
+ base64_secret:
type: boolean
required:
- key
@@ -157,3 +158,43 @@ spec:
minLength: 1
required:
- name
+ hmacAuth:
+ type: object
+ oneOf:
+ - required: ["value"]
+ - required: ["secretRef"]
+ properties:
+ value:
+ type: object
+ properties:
+ access_key:
+ type: string
+ secret_key:
+ type: string
+ algorithm:
+ type: string
+ clock_skew:
+ type: integer
+ signed_headers:
+ type: array
+ items:
+ type: string
+ keep_headers:
+ type: boolean
+ encode_uri_params:
+ type: boolean
+ validate_request_body:
+ type: boolean
+ max_req_body:
+ type: integer
+ required:
+ - access_key
+ - secret_key
+ secretRef:
+ type: object
+ properties:
+ name:
+ type: string
+ minLength: 1
+ required:
+ - name
diff --git a/samples/deploy/crd/v1/ApisixRoute.yaml b/samples/deploy/crd/v1/ApisixRoute.yaml
index 757b8838..eb57ccd8 100644
--- a/samples/deploy/crd/v1/ApisixRoute.yaml
+++ b/samples/deploy/crd/v1/ApisixRoute.yaml
@@ -93,7 +93,7 @@ spec:
match:
type: object
required:
- - paths
+ - paths
properties:
paths:
type: array
@@ -112,7 +112,16 @@ spec:
minItems: 1
items:
type: string
- enum: ["CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]
+ enum:
+ - "CONNECT"
+ - "DELETE"
+ - "GET"
+ - "HEAD"
+ - "OPTIONS"
+ - "PATCH"
+ - "POST"
+ - "PUT"
+ - "TRACE"
remoteAddrs:
type: array
minItems: 1
@@ -129,12 +138,16 @@ spec:
properties:
scope:
type: string
- enum: ["Cookie", "Header", "Path", "Query"]
+ enum:
+ - "Cookie"
+ - "Header"
+ - "Path"
+ - "Query"
name:
type: string
minLength: 1
required:
- - scope
+ - scope
op:
type: string
enum:
@@ -206,7 +219,9 @@ spec:
type: boolean
type:
type: string
- enum: [ "basicAuth", "keyAuth" ]
+ enum:
+ - "basicAuth"
+ - "keyAuth"
keyAuth:
type: object
properties:
@@ -219,11 +234,11 @@ spec:
minItems: 1
items:
type: object
- required: [ "name", "match", "backend", "protocol" ]
+ required: ["name", "match", "backend", "protocol"]
properties:
"protocol":
type: string
- enum: [ "TCP", "UDP" ]
+ enum: ["TCP", "UDP"]
name:
type: string
minLength: 1
@@ -248,7 +263,7 @@ spec:
maximum: 65535
resolveGranularity:
type: string
- enum: [ "endpoint", "service" ]
+ enum: ["endpoint", "service"]
subset:
type: string
required:
@@ -276,7 +291,7 @@ spec:
served: true
storage: false
subresources:
- status: { }
+ status: {}
additionalPrinterColumns:
- jsonPath: .spec.http[].match.hosts
name: Hosts
@@ -309,15 +324,15 @@ spec:
spec:
type: object
anyOf:
- - required: [ "http" ]
- - required: [ "stream" ]
+ - required: ["http"]
+ - required: ["stream"]
properties:
http:
type: array
minItems: 1
items:
type: object
- required: [ "name", "match", "backends" ]
+ required: ["name", "match", "backends"]
properties:
name:
type: string
@@ -355,7 +370,16 @@ spec:
minItems: 1
items:
type: string
- enum: [ "CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE" ]
+ enum:
+ - "CONNECT"
+ - "DELETE"
+ - "GET"
+ - "HEAD"
+ - "OPTIONS"
+ - "PATCH"
+ - "POST"
+ - "PUT"
+ - "TRACE"
remoteAddrs:
type: array
minItems: 1
@@ -372,7 +396,11 @@ spec:
properties:
scope:
type: string
- enum: [ "Cookie", "Header", "Path", "Query" ]
+ enum:
+ - "Cookie"
+ - "Header"
+ - "Path"
+ - "Query"
name:
type: string
minLength: 1
@@ -398,8 +426,8 @@ spec:
items:
type: string
oneOf:
- - required: [ "subject", "op", "value" ]
- - required: [ "subject", "op", "set" ]
+ - required: ["subject", "op", "value"]
+ - required: ["subject", "op", "set"]
websocket:
type: boolean
plugin_config_name:
@@ -420,7 +448,7 @@ spec:
maximum: 65535
resolveGranularity:
type: string
- enum: [ "endpoint", "service" ]
+ enum: ["endpoint", "service"]
weight:
type: integer
minimum: 0
@@ -452,7 +480,12 @@ spec:
type: boolean
type:
type: string
- enum: [ "basicAuth", "keyAuth", "jwtAuth", "wolfRBAC" ]
+ enum:
+ - "basicAuth"
+ - "keyAuth"
+ - "jwtAuth"
+ - "wolfRBAC"
+ - "hmacAuth"
keyAuth:
type: object
properties:
@@ -474,11 +507,11 @@ spec:
minItems: 1
items:
type: object
- required: [ "name", "match", "backend", "protocol" ]
+ required: ["name", "match", "backend", "protocol"]
properties:
"protocol":
type: string
- enum: [ "TCP", "UDP" ]
+ enum: ["TCP", "UDP"]
name:
type: string
minLength: 1
@@ -503,7 +536,7 @@ spec:
maximum: 65535
resolveGranularity:
type: string
- enum: [ "endpoint", "service" ]
+ enum: ["endpoint", "service"]
subset:
type: string
required:
@@ -531,7 +564,7 @@ spec:
served: true
storage: true
subresources:
- status: { }
+ status: {}
additionalPrinterColumns:
- jsonPath: .spec.http[].match.hosts
name: Hosts
@@ -564,15 +597,15 @@ spec:
spec:
type: object
anyOf:
- - required: [ "http" ]
- - required: [ "stream" ]
+ - required: ["http"]
+ - required: ["stream"]
properties:
http:
type: array
minItems: 1
items:
type: object
- required: [ "name", "match", "backends" ]
+ required: ["name", "match", "backends"]
properties:
name:
type: string
@@ -610,7 +643,16 @@ spec:
minItems: 1
items:
type: string
- enum: [ "CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE" ]
+ enum:
+ - "CONNECT"
+ - "DELETE"
+ - "GET"
+ - "HEAD"
+ - "OPTIONS"
+ - "PATCH"
+ - "POST"
+ - "PUT"
+ - "TRACE"
remoteAddrs:
type: array
minItems: 1
@@ -627,7 +669,11 @@ spec:
properties:
scope:
type: string
- enum: [ "Cookie", "Header", "Path", "Query" ]
+ enum:
+ - "Cookie"
+ - "Header"
+ - "Path"
+ - "Query"
name:
type: string
minLength: 1
@@ -653,8 +699,8 @@ spec:
items:
type: string
oneOf:
- - required: [ "subject", "op", "value" ]
- - required: [ "subject", "op", "set" ]
+ - required: ["subject", "op", "value"]
+ - required: ["subject", "op", "set"]
websocket:
type: boolean
plugin_config_name:
@@ -675,7 +721,7 @@ spec:
maximum: 65535
resolveGranularity:
type: string
- enum: [ "endpoint", "service" ]
+ enum: ["endpoint", "service"]
weight:
type: integer
minimum: 0
@@ -707,7 +753,9 @@ spec:
type: boolean
type:
type: string
- enum: [ "basicAuth", "keyAuth" ]
+ enum:
+ - "basicAuth"
+ - "keyAuth"
keyAuth:
type: object
properties:
@@ -720,11 +768,11 @@ spec:
minItems: 1
items:
type: object
- required: [ "name", "match", "backend", "protocol" ]
+ required: ["name", "match", "backend", "protocol"]
properties:
"protocol":
type: string
- enum: [ "TCP", "UDP" ]
+ enum: ["TCP", "UDP"]
name:
type: string
minLength: 1
@@ -749,7 +797,7 @@ spec:
maximum: 65535
resolveGranularity:
type: string
- enum: [ "endpoint", "service" ]
+ enum: ["endpoint", "service"]
subset:
type: string
required:
@@ -772,4 +820,4 @@ spec:
message:
type: string
observedGeneration:
- type: integer
\ No newline at end of file
+ type: integer
diff --git a/test/e2e/suite-features/consumer.go b/test/e2e/suite-features/consumer.go
index 5efef35d..599e0063 100644
--- a/test/e2e/suite-features/consumer.go
+++ b/test/e2e/suite-features/consumer.go
@@ -636,6 +636,208 @@ spec:
assert.Contains(ginkgo.GinkgoT(), msg401, "Missing rbac token in request")
})
+ ginkgo.It("ApisixRoute with hmacAuth consumer", func() {
+ ac := `
+apiVersion: apisix.apache.org/v2beta3
+kind: ApisixConsumer
+metadata:
+ name: hmacvalue
+spec:
+ authParameter:
+ hmacAuth:
+ value:
+ access_key: papa
+ secret_key: fatpa
+ algorithm: "hmac-sha256"
+ clock_skew: 0
+`
+ assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ac), "creating hmacAuth ApisixConsumer")
+
+ // Wait until the ApisixConsumer create event was delivered.
+ time.Sleep(6 * time.Second)
+
+ grs, err := s.ListApisixConsumers()
+ assert.Nil(ginkgo.GinkgoT(), err, "listing consumer")
+ assert.Len(ginkgo.GinkgoT(), grs, 1)
+ assert.Len(ginkgo.GinkgoT(), grs[0].Plugins, 1)
+ hmacAuth, _ := grs[0].Plugins["hmac-auth"].(map[string]interface{})
+ assert.Equal(ginkgo.GinkgoT(), "papa", hmacAuth["access_key"])
+ assert.Equal(ginkgo.GinkgoT(), "fatpa", hmacAuth["secret_key"])
+ assert.Equal(ginkgo.GinkgoT(), "hmac-sha256", hmacAuth["algorithm"])
+ assert.Equal(ginkgo.GinkgoT(), float64(0), hmacAuth["clock_skew"])
+
+ backendSvc, backendPorts := s.DefaultHTTPBackend()
+ ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2beta3
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+ match:
+ hosts:
+ - httpbin.org
+ paths:
+ - /ip
+ exprs:
+ - subject:
+ scope: Header
+ name: X-Foo
+ op: Equal
+ value: bar
+ backends:
+ - serviceName: %s
+ servicePort: %d
+ authentication:
+ enable: true
+ type: hmacAuth
+`, backendSvc, backendPorts[0])
+ assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ar), "creating ApisixRoute with hmacAuth")
+ assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
+ assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixUpstreamsCreated(1), "Checking number of upstreams")
+
+ _ = s.NewAPISIXClient().GET("/ip").
+ WithHeader("Host", "httpbin.org").
+ WithHeader("X-Foo", "bar").
+ WithHeader("X-HMAC-SIGNATURE", "l3Uka7E1kxPA/owQ2+OqJUmflRppjD5q8xPcWbyKKrg=").
+ WithHeader("X-HMAC-ACCESS-KEY", "papa").
+ WithHeader("X-HMAC-ALGORITHM", "hmac-sha256").
+ WithHeader("X-HMAC-SIGNED-HEADERS", "User-Agent;X-Foo").
+ WithHeader("User-Agent", "curl/7.29.0").
+ Expect().
+ Status(http.StatusOK)
+
+ msg := s.NewAPISIXClient().GET("/ip").
+ WithHeader("Host", "httpbin.org").
+ WithHeader("X-Foo", "bar").
+ Expect().
+ Status(http.StatusUnauthorized).
+ Body().
+ Raw()
+ assert.Contains(ginkgo.GinkgoT(), msg, "access key or signature missing")
+
+ msg = s.NewAPISIXClient().GET("/ip").
+ WithHeader("Host", "httpbin.org").
+ WithHeader("X-Foo", "baz").
+ WithHeader("X-HMAC-SIGNATURE", "MhGJMkEYFD+98qtvoDPlvCGIUSmmUaw0In/D0vt2Z4E=").
+ WithHeader("X-HMAC-ACCESS-KEY", "papa").
+ WithHeader("X-HMAC-ALGORITHM", "hmac-sha256").
+ WithHeader("X-HMAC-SIGNED-HEADERS", "User-Agent;X-Foo").
+ WithHeader("User-Agent", "curl/7.29.0").
+ Expect().
+ Status(http.StatusNotFound).
+ Body().
+ Raw()
+ assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
+ })
+
+ ginkgo.It("ApisixRoute with hmacAuth consumer using secret", func() {
+ secret := `
+apiVersion: v1
+kind: Secret
+metadata:
+ name: hmac
+data:
+ access_key: cGFwYQ==
+ secret_key: ZmF0cGE=
+ algorithm: aG1hYy1zaGEyNTY=
+ clock_skew: MA==
+`
+ assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(secret), "creating hmac secret for ApisixConsumer")
+
+ ac := `
+apiVersion: apisix.apache.org/v2beta3
+kind: ApisixConsumer
+metadata:
+ name: hmacvalue
+spec:
+ authParameter:
+ hmacAuth:
+ secretRef:
+ name: hmac
+`
+ assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ac), "creating hmacAuth ApisixConsumer")
+
+ // Wait until the ApisixConsumer create event was delivered.
+ time.Sleep(6 * time.Second)
+
+ grs, err := s.ListApisixConsumers()
+ assert.Nil(ginkgo.GinkgoT(), err, "listing consumer")
+ assert.Len(ginkgo.GinkgoT(), grs, 1)
+ assert.Len(ginkgo.GinkgoT(), grs[0].Plugins, 1)
+ hmacAuth, _ := grs[0].Plugins["hmac-auth"].(map[string]interface{})
+ assert.Equal(ginkgo.GinkgoT(), "papa", hmacAuth["access_key"])
+ assert.Equal(ginkgo.GinkgoT(), "fatpa", hmacAuth["secret_key"])
+ assert.Equal(ginkgo.GinkgoT(), "hmac-sha256", hmacAuth["algorithm"])
+ assert.Equal(ginkgo.GinkgoT(), float64(0), hmacAuth["clock_skew"])
+
+ backendSvc, backendPorts := s.DefaultHTTPBackend()
+ ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2beta3
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+ match:
+ hosts:
+ - httpbin.org
+ paths:
+ - /ip
+ exprs:
+ - subject:
+ scope: Header
+ name: X-Foo
+ op: Equal
+ value: bar
+ backends:
+ - serviceName: %s
+ servicePort: %d
+ authentication:
+ enable: true
+ type: hmacAuth
+`, backendSvc, backendPorts[0])
+ assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ar), "creating ApisixRoute with hmacAuth")
+ assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
+ assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixUpstreamsCreated(1), "Checking number of upstreams")
+
+ _ = s.NewAPISIXClient().GET("/ip").
+ WithHeader("Host", "httpbin.org").
+ WithHeader("X-Foo", "bar").
+ WithHeader("X-HMAC-SIGNATURE", "l3Uka7E1kxPA/owQ2+OqJUmflRppjD5q8xPcWbyKKrg=").
+ WithHeader("X-HMAC-ACCESS-KEY", "papa").
+ WithHeader("X-HMAC-ALGORITHM", "hmac-sha256").
+ WithHeader("X-HMAC-SIGNED-HEADERS", "User-Agent;X-Foo").
+ WithHeader("User-Agent", "curl/7.29.0").
+ Expect().
+ Status(http.StatusOK)
+
+ msg := s.NewAPISIXClient().GET("/ip").
+ WithHeader("Host", "httpbin.org").
+ WithHeader("X-Foo", "bar").
+ Expect().
+ Status(http.StatusUnauthorized).
+ Body().
+ Raw()
+ assert.Contains(ginkgo.GinkgoT(), msg, "access key or signature missing")
+
+ msg = s.NewAPISIXClient().GET("/ip").
+ WithHeader("Host", "httpbin.org").
+ WithHeader("X-Foo", "baz").
+ WithHeader("X-HMAC-SIGNATURE", "MhGJMkEYFD+98qtvoDPlvCGIUSmmUaw0In/D0vt2Z4E=").
+ WithHeader("X-HMAC-ACCESS-KEY", "papa").
+ WithHeader("X-HMAC-ALGORITHM", "hmac-sha256").
+ WithHeader("X-HMAC-SIGNED-HEADERS", "User-Agent;X-Foo").
+ WithHeader("User-Agent", "curl/7.29.0").
+ Expect().
+ Status(http.StatusNotFound).
+ Body().
+ Raw()
+ assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
+ })
+
ginkgo.It("ApisixRoute with jwtAuth consumer", func() {
ac := `
apiVersion: apisix.apache.org/v2beta3
diff --git a/tools.go b/tools.go
index 3fd06649..ade0979c 100644
--- a/tools.go
+++ b/tools.go
@@ -1,3 +1,4 @@
+//go:build tools
// +build tools
// Licensed to the Apache Software Foundation (ASF) under one or more