Skip to content

Commit

Permalink
Add ext-auth filters to routes that referred from APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
Tharsanan1 committed Aug 13, 2024
1 parent 4b0785b commit 571b24b
Show file tree
Hide file tree
Showing 17 changed files with 365 additions and 34 deletions.
8 changes: 4 additions & 4 deletions adapter/internal/oasparser/envoyconf/http_filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import (

// getHTTPFilters generates httpFilter configuration
func getHTTPFilters(globalLuaScript string) []*hcmv3.HttpFilter {
extAuth := getExtAuthzHTTPFilter()
extAuth := GetExtAuthzHTTPFilter()
router := getRouterHTTPFilter()
luaLocal := getLuaFilter(LuaLocal, `
function envoy_on_request(request_handle)
Expand Down Expand Up @@ -127,7 +127,7 @@ func getCorsHTTPFilter() *hcmv3.HttpFilter {
func getUpgradeFilters() []*hcmv3.HttpFilter {

cors := getCorsHTTPFilter()
extAauth := getExtAuthzHTTPFilter()
extAauth := GetExtAuthzHTTPFilter()
apkWebSocketWASM := getAPKWebSocketWASMFilter()
router := getRouterHTTPFilter()
upgradeFilters := []*hcmv3.HttpFilter{
Expand Down Expand Up @@ -190,8 +190,8 @@ func getRateLimitFilter() *hcmv3.HttpFilter {
return &rlFilter
}

// getExtAuthzHTTPFilter gets ExtAauthz http filter.
func getExtAuthzHTTPFilter() *hcmv3.HttpFilter {
// GetExtAuthzHTTPFilter gets ExtAauthz http filter.
func GetExtAuthzHTTPFilter() *hcmv3.HttpFilter {
conf := config.ReadConfigs()
extAuthzConfig := &ext_authv3.ExtAuthz{
// This would clear the route cache only if there is a header added/removed or changed
Expand Down
97 changes: 97 additions & 0 deletions adapter/internal/operator/gateway-api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024, 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.
*
* This file contains code derived from Envoy Gateway,
* https://github.com/envoyproxy/gateway
* and is provided here subject to the following:
* Copyright Project Envoy Gateway Authors
*
*/

package gatewayapi

import (
"strings"

"github.com/wso2/apk/adapter/internal/operator/gateway-api/ir"
dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
"k8s.io/apimachinery/pkg/types"
)

func (t *Translator) ProcessAPIs(apis []*dpv1alpha2.API, httpRoutes []*gwapiv1.HTTPRoute, gateways []*GatewayContext, httpRouteContexts []*HTTPRouteContext, resources *Resources, xdsIR XdsIRMap) {
for _, api := range apis {
// For each API find all the related httproutes. If not found any continue.
routeNamespacedNames := make([]string,0)
for _, httpRouteProds := range api.Spec.Production {
for _, httpRouteRef := range httpRouteProds.RouteRefs {
routeNamespacedNames = append(routeNamespacedNames, types.NamespacedName{
Namespace: api.GetNamespace(),
Name: httpRouteRef,
}.String())
}
}
for _, httpRouteSands := range api.Spec.Sandbox {
for _, httpRouteRef := range httpRouteSands.RouteRefs {
routeNamespacedNames = append(routeNamespacedNames, types.NamespacedName{
Namespace: api.GetNamespace(),
Name: httpRouteRef,
}.String())
}
}
for _, httpRouteContext := range httpRouteContexts {
if !Contains(routeNamespacedNames, GetNamespacedName(httpRouteContext)) {
continue
}
prefix := irRoutePrefix(httpRouteContext)
parentRefs := GetParentReferences(httpRouteContext)
for _, p := range parentRefs {
parentRefCtx := GetRouteParentContext(httpRouteContext, p)
gtwCtx := parentRefCtx.GetGateway()
if gtwCtx == nil {
continue
}
irKey := t.getIRKey(gtwCtx.Gateway)
for _, listener := range parentRefCtx.listeners {
irListener := xdsIR[irKey].GetHTTPListener(irListenerName(listener))
if irListener != nil {
for _, r := range irListener.Routes {
if strings.HasPrefix(r.Name, prefix) {
extAuth := buildExtAuth()
r.ExtAuth = extAuth
}
}
}
}
}
}

}
}

func buildExtAuth() *ir.ExtAuth {
grpcExtAuthService := ir.GRPCExtAuthService{
Destination: ir.RouteDestination{
Name: EXT_AUTH_CLUSTER_NAME,
},
}
flag := true
extAuth := &ir.ExtAuth{
Name: EXT_AUTH_NAME,
GRPC: &grpcExtAuthService,
UseBootstrapCluster: &flag,
}
return extAuth
}
4 changes: 4 additions & 0 deletions adapter/internal/operator/gateway-api/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package gatewayapi

const EXT_AUTH_CLUSTER_NAME = "ext-authz"
const EXT_AUTH_NAME = "ext-authz"
32 changes: 31 additions & 1 deletion adapter/internal/operator/gateway-api/contexts.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,25 @@ func (g *GatewayContext) ResetListeners() {
}
}



func (g *GatewayContext) attachEnvoyProxy(resources *Resources) {
// if g.Spec.Infrastructure != nil && g.Spec.Infrastructure.ParametersRef != nil && !IsMergeGatewaysEnabled(resources) {
// ref := g.Spec.Infrastructure.ParametersRef
// if string(ref.Group) == egv1a1.GroupVersion.Group && ref.Kind == egv1a1.KindEnvoyProxy {
// ep := resources.GetEnvoyProxy(g.Namespace, ref.Name)
// if ep != nil {
// g.envoyProxy = ep
// return
// }
// }
// // not found, fallthrough to use envoyProxy attached to gatewayclass
// }

// g.envoyProxy = resources.EnvoyProxyForGatewayClass
}


// ListenerContext wraps a Listener and provides helper methods for
// setting conditions and other status information on the associated
// Gateway, etc.
Expand Down Expand Up @@ -379,6 +398,17 @@ type RouteParentContext struct {
listeners []*ListenerContext
}

// GetGateway returns the GatewayContext if parent resource is a gateway.
func (r *RouteParentContext) GetGateway() *GatewayContext {
if r == nil || len(r.listeners) == 0 {
return nil
}
gwc := &GatewayContext{
Gateway: r.listeners[0].gateway,
}
return gwc
}

func (r *RouteParentContext) SetListeners(listeners ...*ListenerContext) {
r.listeners = append(r.listeners, listeners...)
}
Expand Down Expand Up @@ -450,4 +480,4 @@ func GetBackendRef(b BackendRefContext) *gwapiv1.BackendRef {

func GetFilters(b BackendRefContext) any {
return reflect.ValueOf(b).FieldByName("Filters").Interface()
}
}
22 changes: 22 additions & 0 deletions adapter/internal/operator/gateway-api/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ func KindDerefOr(kind *gwapiv1.Kind, defaultKind string) string {
return defaultKind
}

func irListenerName(listener *ListenerContext) string {
return fmt.Sprintf("%s/%s/%s", listener.gateway.Namespace, listener.gateway.Name, listener.Name)
}

// IsRefToGateway returns whether the provided parent ref is a reference
// to a Gateway with the given namespace/name, irrespective of whether a
// section/listener name has been specified (i.e. a parent ref to a listener
Expand Down Expand Up @@ -374,6 +378,13 @@ func irRoutePrefix(route RouteContext) string {
return fmt.Sprintf("%s/%s/%s/", strings.ToLower(string(GetRouteType(route))), route.GetNamespace(), route.GetName())
}

func GetNamespacedName(route RouteContext) string {
return types.NamespacedName{
Namespace: route.GetNamespace(),
Name: route.GetName(),
}.String()
}

func irRouteName(route RouteContext, ruleIdx, matchIdx int) string {
return fmt.Sprintf("%srule/%d/match/%d", irRoutePrefix(route), ruleIdx, matchIdx)
}
Expand Down Expand Up @@ -459,3 +470,14 @@ func listenersWithSameHTTPPort(xdsIR *ir.Xds, listener *ir.HTTPListener) []strin
}
return res
}

// contains checks if a specific string exists in the provided slice of strings.
// It returns true if the string is found, and false otherwise.
func Contains(slice []string, item string) bool {
for _, v := range slice {
if v == item {
return true
}
}
return false
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,13 @@ func expectedProxyContainers(infra *ir.ProxyInfra,
ports = append(ports, port)
}
}

port := corev1.ContainerPort{
// hashed container port name including up to the 6 characters of the port name and the maximum of 15 characters.
Name: "admin",
ContainerPort: 9000,
Protocol: "TCP",
}
ports = append(ports, port)
if enablePrometheus(infra) {
ports = append(ports, corev1.ContainerPort{
Name: "metrics",
Expand Down
5 changes: 5 additions & 0 deletions adapter/internal/operator/gateway-api/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,10 @@ type BasicAuth struct {
//
// +k8s:deepcopy-gen=true
type ExtAuth struct {
// Name is a unique name for an ExtAuth configuration.
// The xds translator only generates one external authorization filter for each unique name.
Name string `json:"name" yaml:"name"`

// GRPC defines the gRPC External Authorization service.
// Only one of GRPCService or HTTPService may be specified.
GRPC *GRPCExtAuthService `json:"grpc,omitempty"`
Expand All @@ -602,6 +606,7 @@ type ExtAuth struct {
// in HeadersToExtAuth or not.
// +optional
HeadersToExtAuth []string `json:"headersToExtAuth,omitempty"`
UseBootstrapCluster *bool `json:"useBootstrapCluster,omitempty"`
}

// HTTPExtAuthService defines the HTTP External Authorization service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2"
)

type gatewayReconcilerNew struct {
Expand All @@ -69,6 +70,8 @@ type resourceMappings struct {
allAssociatedNamespaces map[string]struct{}
// Map for storing backendRefs' NamespaceNames referred by various Route objects.
allAssociatedBackendRefs map[gwapiv1.BackendObjectReference]struct{}
// Map for storing APIs NamespaceNames
allAssociatedAPIs map[*dpv1alpha1.API]struct{}
// extensionRefFilters is a map of filters managed by an extension.
// The key is the namespaced name of the filter and the value is the
// unstructured form of the resource.
Expand Down Expand Up @@ -405,6 +408,19 @@ func (r *gatewayReconcilerNew) watchResources(ctx context.Context, mgr manager.M
return err
}

// Watch API CRUDs and process affected Gateways.
apiPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}}
if err := c.Watch(
source.Kind(mgr.GetCache(), &dpv1alpha2.API{}),
handler.EnqueueRequestsFromMapFunc(r.enqueueClass),
apiPredicates...,
); err != nil {
return err
}
if err := addAPIIndexers(ctx, mgr); err != nil {
return err
}

// // Watch GRPCRoute CRUDs and process affected Gateways.
// grpcrPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}}
// if err := c.Watch(
Expand Down Expand Up @@ -612,6 +628,8 @@ func (r *gatewayReconcilerNew) watchResources(ctx context.Context, mgr manager.M
// }
// loggers.LoggerAPKOperator.Info("Watching additional resource", "resource", gvk.String())
// }


return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/wso2/apk/adapter/internal/loggers"
gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api"
dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
Expand All @@ -39,6 +40,7 @@ const (
classGatewayIndex = "classGatewayIndex"
gatewayTLSRouteIndex = "gatewayTLSRouteIndex"
gatewayHTTPRouteIndex = "gatewayHTTPRouteIndex"
httpRouteAPIIndex = "httpRouteAPIIndex"
gatewayGRPCRouteIndex = "gatewayGRPCRouteIndex"
gatewayTCPRouteIndex = "gatewayTCPRouteIndex"
gatewayUDPRouteIndex = "gatewayUDPRouteIndex"
Expand Down Expand Up @@ -83,6 +85,16 @@ func addHTTPRouteIndexers(ctx context.Context, mgr manager.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.HTTPRoute{}, serviceHTTPRouteIndex, serviceHTTPRouteIndexFunc)
}

// addAPIIndexers adds indexing on API.
// - For Service, ServiceImports objects that are referenced in HTTPRoute objects via `.spec.rules.backendRefs`.
// This helps in querying for HTTPRoutes that are affected by a particular Service CRUD.
func addAPIIndexers(ctx context.Context, mgr manager.Manager) error {
if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha2.API{}, httpRouteAPIIndex, httpRouteAPIIndexFunc); err != nil {
return err
}
return nil
}

func gatewayHTTPRouteIndexFunc(rawObj client.Object) []string {
httproute := rawObj.(*gwapiv1.HTTPRoute)
var gateways []string
Expand All @@ -101,6 +113,34 @@ func gatewayHTTPRouteIndexFunc(rawObj client.Object) []string {
return gateways
}

func httpRouteAPIIndexFunc(rawObj client.Object) []string {
api := rawObj.(*dpv1alpha2.API)
var httpRoutes []string
if len(api.Spec.Production) > 0 {
for _, ref := range api.Spec.Production[0].RouteRefs {
if ref != "" {
httpRoutes = append(httpRoutes,
types.NamespacedName{
Namespace: api.Namespace,
Name: ref,
}.String())
}
}
}
if len(api.Spec.Sandbox) > 0 {
for _, ref := range api.Spec.Sandbox[0].RouteRefs {
if ref != "" {
httpRoutes = append(httpRoutes,
types.NamespacedName{
Namespace: api.Namespace,
Name: ref,
}.String())
}
}
}
return httpRoutes
}

func serviceHTTPRouteIndexFunc(rawObj client.Object) []string {
httproute := rawObj.(*gwapiv1.HTTPRoute)
var backendRefs []string
Expand Down
Loading

0 comments on commit 571b24b

Please sign in to comment.