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 May 2, 2024
1 parent 9a626f2 commit 50c998e
Show file tree
Hide file tree
Showing 7 changed files with 388 additions and 5 deletions.
54 changes: 54 additions & 0 deletions api/v1alpha1/authorization_types.go
Original file line number Diff line number Diff line change
@@ -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"
)
5 changes: 5 additions & 0 deletions api/v1alpha1/securitypolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 32 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,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
Expand Down
122 changes: 119 additions & 3 deletions api/v1alpha1/validation/securitypolicy_validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -463,16 +467,128 @@ 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 {
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
74 changes: 74 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 50c998e

Please sign in to comment.