diff --git a/api/v1alpha1/authorization_types.go b/api/v1alpha1/authorization_types.go
new file mode 100644
index 000000000000..20b9104bf665
--- /dev/null
+++ b/api/v1alpha1/authorization_types.go
@@ -0,0 +1,54 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package v1alpha1
+
+// Authorization defines the authorization configuration.
+type Authorization struct {
+ // Rules contains all the authorization rules.
+ //
+ // +kubebuilder:validation:MinItems=1
+ Rules []Rule `json:"rules,omitempty"`
+}
+
+// Rule defines the single authorization rule.
+type Rule struct {
+ // Subjects contains the subject configuration.
+ // If empty, all subjects are included.
+ //
+ // +optional
+ Subjects []Subject `json:"subjects,omitempty"`
+
+ // Permissions contains allowed HTTP methods.
+ // If empty, all methods are matching.
+ //
+ // +optional
+ Permissions []string `json:"permissions,omitempty"`
+
+ // Action defines the action to be taken if the rule matches.
+ Action RuleActionType `json:"action"`
+}
+
+// Subject contains the subject configuration.
+type Subject struct {
+ // ClientCIDR contains client cidr configuration.
+ // Valid examples are "192.168.1.0/24" or "2001:db8::/64"
+ //
+ // +optional
+ ClientCIDR *string `json:"clientCIDR,omitempty"`
+}
+
+// RuleActionType specifies the types of authorization rule action.
+// +kubebuilder:validation:Enum=Allow;Deny;Log
+type RuleActionType string
+
+const (
+ // Allow is the action to allow the request.
+ Allow RuleActionType = "Allow"
+ // Deny is the action to deny the request.
+ Deny RuleActionType = "Deny"
+ // Log is the action to log the request.
+ Log RuleActionType = "Log"
+)
diff --git a/api/v1alpha1/securitypolicy_types.go b/api/v1alpha1/securitypolicy_types.go
index 85c0b21892dc..74e3b2ca5aac 100644
--- a/api/v1alpha1/securitypolicy_types.go
+++ b/api/v1alpha1/securitypolicy_types.go
@@ -69,6 +69,11 @@ type SecurityPolicySpec struct {
//
// +optional
ExtAuth *ExtAuth `json:"extAuth,omitempty"`
+
+ // Authorization defines the authorization configuration.
+ //
+ // +optional
+ Authorization *Authorization `json:"authorization,omitempty"`
}
//+kubebuilder:object:root=true
diff --git a/api/v1alpha1/validation/securitypolicy_validate.go b/api/v1alpha1/validation/securitypolicy_validate.go
index 628d3f801730..5f40b252e572 100644
--- a/api/v1alpha1/validation/securitypolicy_validate.go
+++ b/api/v1alpha1/validation/securitypolicy_validate.go
@@ -8,6 +8,7 @@ package validation
import (
"errors"
"fmt"
+ "net"
"net/mail"
"net/url"
@@ -24,7 +25,7 @@ func ValidateSecurityPolicy(policy *egv1a1.SecurityPolicy) error {
return errors.New("policy is nil")
}
if err := validateSecurityPolicySpec(&policy.Spec); err != nil {
- errs = append(errs, errors.New("policy is nil"))
+ errs = append(errs, err)
}
return utilerrors.NewAggregate(errs)
@@ -42,6 +43,8 @@ func validateSecurityPolicySpec(spec *egv1a1.SecurityPolicySpec) error {
sum++
case spec.JWT != nil:
sum++
+ case spec.Authorization != nil:
+ sum++
}
if sum == 0 {
errs = append(errs, errors.New("no security policy is specified"))
@@ -52,13 +55,40 @@ func validateSecurityPolicySpec(spec *egv1a1.SecurityPolicySpec) error {
return utilerrors.NewAggregate(errs)
}
- if err := ValidateJWTProvider(spec.JWT.Providers); err != nil {
+ if spec.JWT != nil {
+ if err := ValidateJWTProvider(spec.JWT.Providers); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if err := ValidateAuthorization(spec.Authorization); err != nil {
errs = append(errs, err)
}
return utilerrors.NewAggregate(errs)
}
+// ValidateAuthorization validates the provided Authorisation configuration.
+func ValidateAuthorization(as *egv1a1.Authorization) error {
+ var errs []error
+ if as == nil {
+ return nil
+ }
+
+ for _, rule := range as.Rules {
+ for _, subject := range rule.Subjects {
+ if subject.ClientCIDR != nil {
+ _, _, err := net.ParseCIDR(*subject.ClientCIDR)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("invalid CIDR: %s", *subject.ClientCIDR))
+ }
+ }
+ }
+ }
+
+ return utilerrors.NewAggregate(errs)
+}
+
// ValidateJWTProvider validates the provided JWT authentication configuration.
func ValidateJWTProvider(providers []egv1a1.JWTProvider) error {
var errs []error
diff --git a/api/v1alpha1/validation/securitypolicy_validate_test.go b/api/v1alpha1/validation/securitypolicy_validate_test.go
index 489c7644f8b1..8bf286ac8bde 100644
--- a/api/v1alpha1/validation/securitypolicy_validate_test.go
+++ b/api/v1alpha1/validation/securitypolicy_validate_test.go
@@ -8,12 +8,16 @@ package validation
import (
"testing"
- "github.com/stretchr/testify/require"
+ "github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
)
+func asPtr(s string) *string {
+ return &s
+}
+
func TestValidateSecurityPolicy(t *testing.T) {
testCases := []struct {
name string
@@ -463,6 +467,118 @@ func TestValidateSecurityPolicy(t *testing.T) {
},
expected: true,
},
+ {
+ name: "authorisation with valid ipv4 cidr",
+ policy: &egv1a1.SecurityPolicy{
+ TypeMeta: metav1.TypeMeta{
+ Kind: egv1a1.KindSecurityPolicy,
+ APIVersion: egv1a1.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ Name: "test",
+ },
+ Spec: egv1a1.SecurityPolicySpec{
+ Authorization: &egv1a1.Authorization{
+ Rules: []egv1a1.Rule{
+ {
+ Subjects: []egv1a1.Subject{
+ {
+ ClientCIDR: asPtr("192.168.1.0/24"),
+ },
+ },
+ Action: egv1a1.Allow,
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ {
+ name: "authorisation with valid ipv6 cidr",
+ policy: &egv1a1.SecurityPolicy{
+ TypeMeta: metav1.TypeMeta{
+ Kind: egv1a1.KindSecurityPolicy,
+ APIVersion: egv1a1.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ Name: "test",
+ },
+ Spec: egv1a1.SecurityPolicySpec{
+ Authorization: &egv1a1.Authorization{
+ Rules: []egv1a1.Rule{
+ {
+ Subjects: []egv1a1.Subject{
+ {
+ ClientCIDR: asPtr("2001:db8::/64"),
+ },
+ },
+ Action: egv1a1.Allow,
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ {
+ name: "authorisation with invalid ipv4 cidr",
+ policy: &egv1a1.SecurityPolicy{
+ TypeMeta: metav1.TypeMeta{
+ Kind: egv1a1.KindSecurityPolicy,
+ APIVersion: egv1a1.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ Name: "test",
+ },
+ Spec: egv1a1.SecurityPolicySpec{
+ Authorization: &egv1a1.Authorization{
+ Rules: []egv1a1.Rule{
+ {
+ Subjects: []egv1a1.Subject{
+ {
+ ClientCIDR: asPtr("192.168.1.001/24"),
+ },
+ },
+ Action: egv1a1.Allow,
+ },
+ },
+ },
+ },
+ },
+ expected: false,
+ },
+ {
+ name: "authorisation with invalid ipv6 cidr",
+ policy: &egv1a1.SecurityPolicy{
+ TypeMeta: metav1.TypeMeta{
+ Kind: egv1a1.KindSecurityPolicy,
+ APIVersion: egv1a1.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ Name: "test",
+ },
+ Spec: egv1a1.SecurityPolicySpec{
+ Authorization: &egv1a1.Authorization{
+ Rules: []egv1a1.Rule{
+ {
+ Subjects: []egv1a1.Subject{
+ {
+ ClientCIDR: asPtr("2001:dffoob8::/64"),
+ },
+ },
+ Action: egv1a1.Allow,
+ },
+ },
+ },
+ },
+ },
+ expected: false,
+ },
}
for i := range testCases {
@@ -470,9 +586,9 @@ func TestValidateSecurityPolicy(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
err := ValidateSecurityPolicy(tc.policy)
if tc.expected {
- require.NoError(t, err)
+ assert.NoError(t, err)
} else {
- require.Error(t, err)
+ assert.Error(t, err)
}
})
}
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index c3d53f443bae..05c2d611acc4 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -151,6 +151,28 @@ func (in *ActiveHealthCheckPayload) DeepCopy() *ActiveHealthCheckPayload {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Authorization) DeepCopyInto(out *Authorization) {
+ *out = *in
+ if in.Rules != nil {
+ in, out := &in.Rules, &out.Rules
+ *out = make([]Rule, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authorization.
+func (in *Authorization) DeepCopy() *Authorization {
+ if in == nil {
+ return nil
+ }
+ out := new(Authorization)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackOffPolicy) DeepCopyInto(out *BackOffPolicy) {
*out = *in
@@ -3806,6 +3828,33 @@ func (in *RetryOn) DeepCopy() *RetryOn {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Rule) DeepCopyInto(out *Rule) {
+ *out = *in
+ if in.Subjects != nil {
+ in, out := &in.Subjects, &out.Subjects
+ *out = make([]Subject, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ if in.Permissions != nil {
+ in, out := &in.Permissions, &out.Permissions
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
+func (in *Rule) DeepCopy() *Rule {
+ if in == nil {
+ return nil
+ }
+ out := new(Rule)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecurityPolicy) DeepCopyInto(out *SecurityPolicy) {
*out = *in
@@ -3894,6 +3943,11 @@ func (in *SecurityPolicySpec) DeepCopyInto(out *SecurityPolicySpec) {
*out = new(ExtAuth)
(*in).DeepCopyInto(*out)
}
+ if in.Authorization != nil {
+ in, out := &in.Authorization, &out.Authorization
+ *out = new(Authorization)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityPolicySpec.
@@ -3991,6 +4045,26 @@ func (in *StringMatch) DeepCopy() *StringMatch {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Subject) DeepCopyInto(out *Subject) {
+ *out = *in
+ if in.ClientCIDR != nil {
+ in, out := &in.ClientCIDR, &out.ClientCIDR
+ *out = new(string)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subject.
+func (in *Subject) DeepCopy() *Subject {
+ if in == nil {
+ return nil
+ }
+ out := new(Subject)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TCPActiveHealthChecker) DeepCopyInto(out *TCPActiveHealthChecker) {
*out = *in
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
index 2758a9c2524d..cd0ed80014d8 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -49,6 +49,45 @@ spec:
spec:
description: Spec defines the desired state of SecurityPolicy.
properties:
+ authorization:
+ description: Authorization defines the authorization configuration.
+ properties:
+ rules:
+ description: Rules contains all the authorization rules. If rules
+ contains at least one Allow rule and none of them matches the
+ action for the request is deny. If rules contains at least one
+ Deny rule and none of them matches the action for the request
+ is allow. If rules contains Allow and Deny, and the request
+ matches both, `Deny` takes precedence.
+ items:
+ description: Rule defines the single authorization rule.
+ properties:
+ action:
+ description: Action defines the action to be taken if the
+ rule matches.
+ enum:
+ - Allow
+ - Deny
+ - Log
+ type: string
+ clientSelector:
+ description: ClientSelector contains the client selector
+ configuration. All selectors are AND'd together and only
+ if all selector are valid the Action is performed.
+ properties:
+ clientCIDR:
+ description: ClientCIDRs is a list of CIDRs. Valid examples
+ are "192.168.1.0/24" or "2001:db8::/64"
+ items:
+ type: string
+ type: array
+ type: object
+ required:
+ - action
+ type: object
+ minItems: 1
+ type: array
+ type: object
basicAuth:
description: BasicAuth defines the configuration for the HTTP Basic
Authentication.
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index d485100eab3c..45dd4fa0713d 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -171,6 +171,20 @@ _Appears in:_
| `TCP` | ActiveHealthCheckerTypeTCP defines the TCP type of health checking.
|
+#### Authorization
+
+
+
+Authorization defines the authorization configuration.
+
+_Appears in:_
+- [SecurityPolicySpec](#securitypolicyspec)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `rules` | _[Rule](#rule) array_ | true | Rules contains all the authorization rules. |
+
+
#### BackOffPolicy
@@ -2865,6 +2879,38 @@ _Appears in:_
| `httpStatusCodes` | _[HTTPStatus](#httpstatus) array_ | false | HttpStatusCodes specifies the http status codes to be retried.
The retriable-status-codes trigger must also be configured for these status codes to trigger a retry. |
+#### Rule
+
+
+
+Rule defines the single authorization rule.
+
+_Appears in:_
+- [Authorization](#authorization)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `subjects` | _[Subject](#subject) array_ | false | Subjects contains the subject configuration.
If empty, all subjects are included. |
+| `permissions` | _string array_ | false | Permissions contains allowed HTTP methods.
If empty, all methods are matching. |
+| `action` | _[RuleActionType](#ruleactiontype)_ | true | Action defines the action to be taken if the rule matches. |
+
+
+#### RuleActionType
+
+_Underlying type:_ _string_
+
+RuleActionType specifies the types of authorization rule action.
+
+_Appears in:_
+- [Rule](#rule)
+
+| Value | Description |
+| ----- | ----------- |
+| `Allow` | Allow is the action to allow the request.
|
+| `Deny` | Deny is the action to deny the request.
|
+| `Log` | Log is the action to log the request.
|
+
+
#### SecurityPolicy
@@ -2916,6 +2962,7 @@ _Appears in:_
| `jwt` | _[JWT](#jwt)_ | false | JWT defines the configuration for JSON Web Token (JWT) authentication. |
| `oidc` | _[OIDC](#oidc)_ | false | OIDC defines the configuration for the OpenID Connect (OIDC) authentication. |
| `extAuth` | _[ExtAuth](#extauth)_ | false | ExtAuth defines the configuration for External Authorization. |
+| `authorization` | _[Authorization](#authorization)_ | false | Authorization defines the authorization configuration. |
#### ServiceExternalTrafficPolicy
@@ -3032,6 +3079,20 @@ _Appears in:_
| `RegularExpression` | StringMatchRegularExpression :The input string must match the regular expression
specified in the match value.
The regex string must adhere to the syntax documented in
https://github.com/google/re2/wiki/Syntax.
|
+#### Subject
+
+
+
+Subject contains the subject configuration.
+
+_Appears in:_
+- [Rule](#rule)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `clientCIDR` | _string_ | false | ClientCIDR contains client cidr configuration.
Valid examples are "192.168.1.0/24" or "2001:db8::/64" |
+
+
#### TCPActiveHealthChecker