Skip to content

Commit

Permalink
add authorisation api spec
Browse files Browse the repository at this point in the history
Signed-off-by: Jesse Haka <haka.jesse@gmail.com>
  • Loading branch information
zetaab committed Feb 20, 2024
1 parent a5125bf commit c2b9033
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 5 deletions.
57 changes: 57 additions & 0 deletions api/v1alpha1/authorization_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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

// +kubebuilder:validation:XValidation:rule="(has(self.allow) || has(self.deny))",message="one of allow or deny must be specified"
//
// 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 {
// ClientSelector contains the client selector configuration.
ClientSelector ClientSelector `json:"clientSelector,omitempty"`

// Permission contains the permissions for the selector.
Permission Permission `json:"permissions,omitempty"`

// Action defines the action to be taken if the rule matches.
Action RuleActionType `json:"action,omitempty"`
}

// ClientSelector contains the client selector configuration.
type ClientSelector struct {
// ClientCIDRs is a list of CIDRs.
// Valid examples are "192.168.1.0/24" or "2001:db8::/64"
//
// +optional
ClientCIDRs []string `json:"clientCIDR,omitempty"`
}

// Permission contains the permissions for the selector.
type Permission struct {
// Any indicates that the rule matches any permission.
//
// +optional
Any bool `json:"any,omitempty"`
}

// RuleActionType specifies the types of authorization rule action.
// +kubebuilder:validation:Enum=AllowRuleType;DenyRuleType;LogRuleType
type RuleActionType string

const (
// AllowRuleType is the action to allow the request.
AllowRuleType RuleActionType = "ALLOW"
// DenyRuleType is the action to deny the request.
DenyRuleType RuleActionType = "DENY"
// LogRuleType is the action to log the request.
LogRuleType RuleActionType = "LOG"
)
5 changes: 5 additions & 0 deletions api/v1alpha1/securitypolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ type SecurityPolicySpec struct {
//
// +optional
ExtAuth *ExtAuth `json:"extAuth,omitempty"`

// Authorization defines the authorization configuration.
//
// +optional
Authorization *Authorization `json:"authorization,omitempty"`
}

// SecurityPolicyStatus defines the state of SecurityPolicy
Expand Down
32 changes: 30 additions & 2 deletions api/v1alpha1/validation/securitypolicy_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package validation
import (
"errors"
"fmt"
"net"
"net/mail"
"net/url"

Expand All @@ -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)
Expand All @@ -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"))
Expand All @@ -52,13 +55,38 @@ 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 := ValidateAuthorisation(spec.Authorization); err != nil {
errs = append(errs, err)
}

return utilerrors.NewAggregate(errs)
}

// ValidateAuthorisation validates the provided Authorisation configuration.
func ValidateAuthorisation(as *egv1a1.Authorization) error {
var errs []error
if as == nil {
return nil
}

for _, rule := range as.Rules {
for _, cidr := range rule.ClientSelector.ClientCIDRs {
_, _, err := net.ParseCIDR(cidr)
if err != nil {
errs = append(errs, fmt.Errorf("invalid CIDR: %s", cidr))
}
}
}

return utilerrors.NewAggregate(errs)
}

// ValidateJWTProvider validates the provided JWT authentication configuration.
func ValidateJWTProvider(providers []egv1a1.JWTProvider) error {
var errs []error
Expand Down
114 changes: 111 additions & 3 deletions api/v1alpha1/validation/securitypolicy_validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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"
Expand Down Expand Up @@ -463,16 +463,124 @@ 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{
{
ClientSelector: egv1a1.ClientSelector{
ClientCIDRs: []string{"192.168.1.0/24"},
},
Permission: egv1a1.Permission{Any: true},
Action: egv1a1.AllowRuleType,
},
},
},
},
},
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{
{
ClientSelector: egv1a1.ClientSelector{
ClientCIDRs: []string{"2001:db8::/64"},
},
Permission: egv1a1.Permission{Any: true},
Action: egv1a1.AllowRuleType,
},
},
},
},
},
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{
{
ClientSelector: egv1a1.ClientSelector{
ClientCIDRs: []string{"192.168.1.001/24"},
},
Permission: egv1a1.Permission{Any: true},
Action: egv1a1.AllowRuleType,
},
},
},
},
},
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{
{
ClientSelector: egv1a1.ClientSelector{
ClientCIDRs: []string{"2001:dffoob8::/64"},
},
Permission: egv1a1.Permission{Any: true},
Action: egv1a1.AllowRuleType,
},
},
},
},
},
expected: false,
},
}

for i := range testCases {
tc := testCases[i]
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)
}
})
}
Expand Down
79 changes: 79 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c2b9033

Please sign in to comment.