Skip to content

Commit

Permalink
Add: MTLS Support in API level
Browse files Browse the repository at this point in the history
  • Loading branch information
BLasan committed Jul 5, 2023
1 parent 1154ef6 commit d6c2a18
Show file tree
Hide file tree
Showing 34 changed files with 2,370 additions and 107 deletions.
2 changes: 2 additions & 0 deletions adapter/api/proto/wso2/discovery/api/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package wso2.discovery.api;
import "wso2/discovery/api/Resource.proto";
import "wso2/discovery/api/Certificate.proto";
import "wso2/discovery/api/BackendJWTTokenInfo.proto";
import "wso2/discovery/api/mutual_ssl.proto";
// import "wso2/discovery/api/graphql.proto";

option go_package = "github.com/envoyproxy/go-control-plane/wso2/discovery/api;api";
Expand Down Expand Up @@ -54,4 +55,5 @@ message Api {
bool systemAPI = 24;
BackendJWTTokenInfo backendJWTTokenInfo = 25;
bytes apiDefinitionFile = 26;
MutualSSL mutualSSLAuth = 27;
}
36 changes: 36 additions & 0 deletions adapter/api/proto/wso2/discovery/api/mutual_ssl.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2022, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
//
// WSO2 LLC. 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.

syntax = "proto3";

package wso2.discovery.api;

option go_package = "github.com/envoyproxy/go-control-plane/wso2/discovery/api;api";
option java_package = "org.wso2.apk.enforcer.discovery.api";
option java_outer_classname = "MutualSSLAuth";
option java_multiple_files = true;


// [#protodoc-title: MTLS]

message MutualSSL {
string certificateHeader = 1;
bool enableClientValidation = 2;
bool clientCertificateEncode = 3;
bool enableOutboundCertificateHeader = 4;
bool isEnabled = 5;
string certificatePath = 6;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ message MutualSSL {
bool enableClientValidation = 2;
bool clientCertificateEncode = 3;
bool enableOutboundCertificateHeader = 4;
bool isEnabled = 5;
}
26 changes: 26 additions & 0 deletions adapter/internal/oasparser/model/adapter_internal_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type AdapterInternalAPI struct {
backendJWTTokenInfo *BackendJWTTokenInfo
apiDefinitionFile []byte
APIProperties []dpv1alpha1.Property
mutualSSLAuth *MutualSSLAuth
// GraphQLSchema string
// GraphQLComplexities GraphQLComplexityYaml
IsSystemAPI bool
Expand All @@ -82,6 +83,21 @@ type BackendJWTTokenInfo struct {
CustomClaims []ClaimMapping
}

// MutualSSLAuth mutual ssl authentication related information
type MutualSSLAuth struct {
TrustedCertPath string `json:"trustedCertPath,omitempty"`
Enabled bool `json:"enabled"`
MTLSSecurity *MTLSSecurity `json:"mtlsSecurity,omitempty"`
}

// MTLSSecurity mutual ssl security related information
type MTLSSecurity struct {
CertificateHeader string `json:"certificateHeader"`
EnableClientValidation bool `json:"enableClientValidation"`
ClientCertificateEncode bool `json:"clientCertificateEncode"`
EnableOutboundCertificateHeader bool `json:"enableOutboundCertificateHeader"`
}

// ClaimMapping represents the object structure holding the information related to the JWT Generator Claims
type ClaimMapping struct {
Claim string
Expand Down Expand Up @@ -256,6 +272,11 @@ func (swagger *AdapterInternalAPI) GetXWso2Endpoints() map[string]*EndpointClust
return swagger.xWso2Endpoints
}

// GetMutualSSLConfigs returns the mutual ssl configs.
func (swagger *AdapterInternalAPI) GetMutualSSLConfigs() *MutualSSLAuth {
return swagger.mutualSSLAuth
}

// GetResources returns the array of resources (openAPI path level info)
func (swagger *AdapterInternalAPI) GetResources() []*Resource {
return swagger.resources
Expand Down Expand Up @@ -302,6 +323,11 @@ func (swagger *AdapterInternalAPI) SetClientCerts(certs []Certificate) {
swagger.clientCertificates = certs
}

// SetMutualSSLAuth set the mutual ssl auth of the API
func (swagger *AdapterInternalAPI) SetMutualSSLAuth(mutualSSLAuth MutualSSLAuth) {
swagger.mutualSSLAuth = &mutualSSLAuth
}

// SetID set the Id of the API
func (swagger *AdapterInternalAPI) SetID(id string) {
swagger.id = id
Expand Down
62 changes: 62 additions & 0 deletions adapter/internal/oasparser/model/http_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func (swagger *AdapterInternalAPI) SetInfoHTTPRouteCR(httpRoute *gwapiv1b1.HTTPR
if authScheme.Spec.Override != nil && authScheme.Spec.Override.ExternalService.Disabled != nil {
disableAuthentications = *authScheme.Spec.Override.ExternalService.Disabled
}
swagger.mutualSSLAuth = parseMTLSAuthToInternal(authScheme)
}
var apiPolicy *dpv1alpha1.APIPolicy
if outputAPIPolicy != nil {
Expand Down Expand Up @@ -342,6 +343,67 @@ func (swagger *AdapterInternalAPI) SetInfoHTTPRouteCR(httpRoute *gwapiv1b1.HTTPR
return nil
}

func parseMTLSAuthToInternal(authScheme *dpv1alpha1.Authentication) *MutualSSLAuth {
mutualSSLAuth := &MutualSSLAuth{}
if authScheme.Spec.Override != nil && authScheme.Spec.Override.MutualSSL != nil {
mtlsOverride := authScheme.Spec.Override.MutualSSL
mtlsDefault := authScheme.Spec.Default.MutualSSL
if mtlsOverride != nil && mtlsOverride.Enabled != nil && *mtlsOverride.Enabled {
if mtlsOverride.Enabled != nil {
mutualSSLAuth.Enabled = *mtlsOverride.Enabled
} else {
mutualSSLAuth.Enabled = *mtlsDefault.Enabled
}
if mtlsOverride.TrustedCertPath != nil {
mutualSSLAuth.TrustedCertPath = *mtlsOverride.TrustedCertPath
} else {
mutualSSLAuth.TrustedCertPath = *mtlsDefault.TrustedCertPath
}
if mtlsOverride.MTLSSecurity != nil {
mutualSSLAuth.MTLSSecurity = &MTLSSecurity{}
if mtlsOverride.MTLSSecurity.CertificateHeader != nil {
mutualSSLAuth.MTLSSecurity.CertificateHeader = *mtlsOverride.MTLSSecurity.CertificateHeader
} else {
mutualSSLAuth.MTLSSecurity.CertificateHeader = *mtlsDefault.MTLSSecurity.CertificateHeader
}
if mtlsOverride.MTLSSecurity.EnableClientValidation != nil {
mutualSSLAuth.MTLSSecurity.EnableClientValidation = *mtlsOverride.MTLSSecurity.EnableClientValidation
} else {
mutualSSLAuth.MTLSSecurity.EnableClientValidation = *mtlsDefault.MTLSSecurity.EnableClientValidation
}
if mtlsOverride.MTLSSecurity.ClientCertificateEncode != nil {
mutualSSLAuth.MTLSSecurity.ClientCertificateEncode = *mtlsOverride.MTLSSecurity.ClientCertificateEncode
} else {
mutualSSLAuth.MTLSSecurity.ClientCertificateEncode = *mtlsDefault.MTLSSecurity.ClientCertificateEncode
}
if mtlsOverride.MTLSSecurity.EnableOutboundCertificateHeader != nil {
mutualSSLAuth.MTLSSecurity.EnableOutboundCertificateHeader = *mtlsOverride.MTLSSecurity.EnableOutboundCertificateHeader
} else {
mutualSSLAuth.MTLSSecurity.EnableOutboundCertificateHeader = *mtlsDefault.MTLSSecurity.EnableOutboundCertificateHeader
}
} else {
mutualSSLAuth.MTLSSecurity = &MTLSSecurity{
CertificateHeader: *mtlsDefault.MTLSSecurity.CertificateHeader,
EnableClientValidation: *mtlsDefault.MTLSSecurity.EnableClientValidation,
ClientCertificateEncode: *mtlsDefault.MTLSSecurity.ClientCertificateEncode,
EnableOutboundCertificateHeader: *mtlsDefault.MTLSSecurity.EnableOutboundCertificateHeader,
}
}
}
} else if authScheme.Spec.Default != nil && authScheme.Spec.Default.MutualSSL != nil {
mtlsDefault := authScheme.Spec.Default.MutualSSL
mutualSSLAuth.Enabled = *mtlsDefault.Enabled
mutualSSLAuth.TrustedCertPath = *mtlsDefault.TrustedCertPath
mutualSSLAuth.MTLSSecurity = &MTLSSecurity{
CertificateHeader: *mtlsDefault.MTLSSecurity.CertificateHeader,
EnableClientValidation: *mtlsDefault.MTLSSecurity.EnableClientValidation,
ClientCertificateEncode: *mtlsDefault.MTLSSecurity.ClientCertificateEncode,
EnableOutboundCertificateHeader: *mtlsDefault.MTLSSecurity.EnableOutboundCertificateHeader,
}
}
return mutualSSLAuth
}

func parseBackendJWTTokenToInternal(backendJWTToken *dpv1alpha1.BackendJWTToken) *BackendJWTTokenInfo {
var customClaims []ClaimMapping
for _, value := range backendJWTToken.CustomClaims {
Expand Down
4 changes: 4 additions & 0 deletions adapter/internal/operator/PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ resources:
kind: Authentication
path: github.com/wso2/apk/adapter/internal/operator/apis/dp/v1alpha1
version: v1alpha1
webhooks:
defaulting: true
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
Expand Down
18 changes: 18 additions & 0 deletions adapter/internal/operator/apis/dp/v1alpha1/authentication_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@ type AuthenticationSpec struct {
type AuthSpec struct {
AuthServerType string `json:"type,omitempty"`
ExternalService ExtAuthService `json:"ext,omitempty"`
// +optional
MutualSSL *MutualSSLAuth `json:"mutualSSL,omitempty"`
}

// MutualSSLAuth mutual ssl authentication related information
type MutualSSLAuth struct {
TrustedCertPath *string `json:"trustedCertPath,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
// +optional
MTLSSecurity *MTLSSecurity `json:"mtlsSecurity,omitempty"`
}

// MTLSSecurity mutual ssl security related information
type MTLSSecurity struct {
CertificateHeader *string `json:"certificateHeader,omitempty"`
EnableClientValidation *bool `json:"enableClientValidation,omitempty"`
ClientCertificateEncode *bool `json:"clientCertificateEncode,omitempty"`
EnableOutboundCertificateHeader *bool `json:"enableOutboundCertificateHeader,omitempty"`
}

// ExtAuthService external authentication related information
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2022, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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 v1alpha1

import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

// log is for logging in this package.
var authenticationlog = logf.Log.WithName("authentication-resource")

// SetupWebhookWithManager sets up and registers the webhook with the manager.
func (r *Authentication) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!

//+kubebuilder:webhook:path=/mutate-dp-wso2-com-v1alpha1-authentication,mutating=true,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=authentications,verbs=create;update,versions=v1alpha1,name=mauthentication.kb.io,admissionReviewVersions=v1

var _ webhook.Defaulter = &Authentication{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *Authentication) Default() {
authenticationlog.Info("default", "name", r.Name)

// TODO(user): fill in your defaulting logic.
}

// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
//+kubebuilder:webhook:path=/validate-dp-wso2-com-v1alpha1-authentication,mutating=false,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=authentications,verbs=create;update,versions=v1alpha1,name=vauthentication.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &Authentication{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *Authentication) ValidateCreate() error {
return validateAuthentication(r)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *Authentication) ValidateUpdate(old runtime.Object) error {
return validateAuthentication(r)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *Authentication) ValidateDelete() error {
authenticationlog.Info("validate delete", "name", r.Name)

// TODO(user): fill in your validation logic upon object deletion.
return nil
}

func validateAuthentication(r *Authentication) error {
var allErrs field.ErrorList
mtlsAuthOverride := r.Spec.Override.MutualSSL
mtlsAuthDefault := r.Spec.Default.MutualSSL
if mtlsAuthOverride != nil && mtlsAuthDefault != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("default").Child("mutualSSL"),
mtlsAuthDefault.MTLSSecurity, "mutualSSL should be atleast specified in either default or override sections"))
}
if len(allErrs) > 0 {
return apierrors.NewInvalid(
schema.GroupKind{Group: "dp.wso2.com", Kind: "Authentication"},
r.Name, allErrs)
}
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ var _ = BeforeSuite(func() {
err = (&Backend{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

err = (&Authentication{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

//+kubebuilder:scaffold:webhook

go func() {
Expand Down
Loading

0 comments on commit d6c2a18

Please sign in to comment.