Skip to content

Commit

Permalink
Updates Translator to Build Authentication IR (#901)
Browse files Browse the repository at this point in the history
Signed-off-by: danehans <daneyonhansen@gmail.com>

Signed-off-by: danehans <daneyonhansen@gmail.com>
  • Loading branch information
danehans authored Jan 19, 2023
1 parent 12eb305 commit 77c98eb
Show file tree
Hide file tree
Showing 19 changed files with 886 additions and 284 deletions.
51 changes: 38 additions & 13 deletions internal/gatewayapi/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type HTTPFilterChains struct {
RemoveResponseHeaders []string

Mirrors []*ir.RouteDestination

RequestAuthentication *ir.RequestAuthentication
}

// ProcessHTTPFilters translate gateway api http filters to IRs.
Expand Down Expand Up @@ -614,21 +616,15 @@ func (t *Translator) processResponseHeaderModifierFilter(
}

func (t *Translator) processExtensionRefHTTPFilter(filter v1beta1.HTTPRouteFilter, filterContext *HTTPFiltersContext, resources *Resources) {
// If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped.
// Instead, requests that would have been processed by that filter MUST receive an HTTP error
// response.
found := false

for _, f := range resources.AuthenFilters {
if f.Namespace == filterContext.HTTPRoute.Namespace && f.Name == string(filter.ExtensionRef.Name) {
found = true
break
}
// Make sure the config actually exists.
extFilter := filter.ExtensionRef
if extFilter == nil {
return
}

if !found {
errMsg := fmt.Sprintf("Reference not found for filter type: %s", filter.Type)
// Reset the conditions in case the Accepted=True condition exists.
// Set negative status condition and return early if the no AuthenticationFilters exist.
if len(resources.AuthenticationFilters) == 0 {
errMsg := fmt.Sprintf("Reference not found for filter type: %v", filter.Type)
filterContext.ParentRef.ResetConditions(filterContext.HTTPRoute)
filterContext.ParentRef.SetCondition(filterContext.HTTPRoute,
v1beta1.RouteConditionResolvedRefs,
Expand All @@ -640,6 +636,35 @@ func (t *Translator) processExtensionRefHTTPFilter(filter v1beta1.HTTPRouteFilte
Body: &errMsg,
StatusCode: 500,
}
return
}

// Set the filter context and return early if a matching AuthenticationFilter is found.
for _, authenFilter := range resources.AuthenticationFilters {
if authenFilter.Namespace == filterContext.HTTPRoute.Namespace &&
authenFilter.Name == string(extFilter.Name) {
filterContext.HTTPFilterChains.RequestAuthentication = &ir.RequestAuthentication{
JWT: &ir.JwtRequestAuthentication{
Providers: authenFilter.Spec.JwtProviders,
},
}
return
}
}

// Matching AuthenticationFilter not found, so set negative status condition.
errMsg := fmt.Sprintf("Reference %s/%s not found for filter type: %v", filterContext.HTTPRoute.Namespace,
filter.ExtensionRef.Name, filter.Type)
filterContext.ParentRef.ResetConditions(filterContext.HTTPRoute)
filterContext.ParentRef.SetCondition(filterContext.HTTPRoute,
v1beta1.RouteConditionResolvedRefs,
metav1.ConditionFalse,
v1beta1.RouteReasonBackendNotFound,
errMsg,
)
filterContext.DirectResponse = &ir.DirectResponse{
Body: &errMsg,
StatusCode: 500,
}
}

Expand Down
48 changes: 24 additions & 24 deletions internal/gatewayapi/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,34 @@ type InfraIRMap map[string]*ir.Infra
// resources that the translators needs as inputs.
// +k8s:deepcopy-gen=true
type Resources struct {
Gateways []*v1beta1.Gateway
HTTPRoutes []*v1beta1.HTTPRoute
GRPCRoutes []*v1alpha2.GRPCRoute
TLSRoutes []*v1alpha2.TLSRoute
TCPRoutes []*v1alpha2.TCPRoute
UDPRoutes []*v1alpha2.UDPRoute
ReferenceGrants []*v1alpha2.ReferenceGrant
Namespaces []*v1.Namespace
Services []*v1.Service
Secrets []*v1.Secret
AuthenFilters []*egv1a1.AuthenticationFilter
RateLimitFilters []*egv1a1.RateLimitFilter
EnvoyProxy *egcfgv1a1.EnvoyProxy
Gateways []*v1beta1.Gateway
HTTPRoutes []*v1beta1.HTTPRoute
GRPCRoutes []*v1alpha2.GRPCRoute
TLSRoutes []*v1alpha2.TLSRoute
TCPRoutes []*v1alpha2.TCPRoute
UDPRoutes []*v1alpha2.UDPRoute
ReferenceGrants []*v1alpha2.ReferenceGrant
Namespaces []*v1.Namespace
Services []*v1.Service
Secrets []*v1.Secret
AuthenticationFilters []*egv1a1.AuthenticationFilter
RateLimitFilters []*egv1a1.RateLimitFilter
EnvoyProxy *egcfgv1a1.EnvoyProxy
}

func NewResources() *Resources {
return &Resources{
Gateways: []*v1beta1.Gateway{},
HTTPRoutes: []*v1beta1.HTTPRoute{},
GRPCRoutes: []*v1alpha2.GRPCRoute{},
TLSRoutes: []*v1alpha2.TLSRoute{},
Services: []*v1.Service{},
Secrets: []*v1.Secret{},
ReferenceGrants: []*v1alpha2.ReferenceGrant{},
Namespaces: []*v1.Namespace{},
AuthenFilters: []*egv1a1.AuthenticationFilter{},
RateLimitFilters: []*egv1a1.RateLimitFilter{},
EnvoyProxy: new(egcfgv1a1.EnvoyProxy),
Gateways: []*v1beta1.Gateway{},
HTTPRoutes: []*v1beta1.HTTPRoute{},
GRPCRoutes: []*v1alpha2.GRPCRoute{},
TLSRoutes: []*v1alpha2.TLSRoute{},
Services: []*v1.Service{},
Secrets: []*v1.Secret{},
ReferenceGrants: []*v1alpha2.ReferenceGrant{},
Namespaces: []*v1.Namespace{},
RateLimitFilters: []*egv1a1.RateLimitFilter{},
EnvoyProxy: new(egcfgv1a1.EnvoyProxy),
AuthenticationFilters: []*egv1a1.AuthenticationFilter{},
}
}

Expand Down
10 changes: 8 additions & 2 deletions internal/gatewayapi/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res
}
}

func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRef *RouteParentContext, resources *Resources) []*ir.HTTPRoute {
func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext,
parentRef *RouteParentContext,
resources *Resources) []*ir.HTTPRoute {
var routeRoutes []*ir.HTTPRoute

// compute matches, filters, backends
Expand All @@ -95,7 +97,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe
// A rule is matched if any one of its matches
// is satisfied (i.e. a logical "OR"), so generate
// a unique Xds IR HTTPRoute per match.
var ruleRoutes []*ir.HTTPRoute = t.processHTTPRouteRule(httpRoute, ruleIdx, httpFiltersContext, rule)
var ruleRoutes = t.processHTTPRouteRule(httpRoute, ruleIdx, httpFiltersContext, rule)

for _, backendRef := range rule.BackendRefs {
destination, backendWeight := t.processRouteDestination(backendRef.BackendRef, parentRef, httpRoute, resources)
Expand Down Expand Up @@ -221,6 +223,9 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i
if len(httpFiltersContext.Mirrors) > 0 {
irRoute.Mirrors = httpFiltersContext.Mirrors
}
if httpFiltersContext.RequestAuthentication != nil {
irRoute.RequestAuthentication = httpFiltersContext.RequestAuthentication
}
ruleRoutes = append(ruleRoutes, irRoute)
}

Expand Down Expand Up @@ -274,6 +279,7 @@ func (t *Translator) processHTTPRouteParentRefListener(httpRoute *HTTPRouteConte
DirectResponse: routeRoute.DirectResponse,
URLRewrite: routeRoute.URLRewrite,
Mirrors: routeRoute.Mirrors,
RequestAuthentication: routeRoute.RequestAuthentication,
}
// Don't bother copying over the weights unless the route has invalid backends.
if routeRoute.BackendWeights.Invalid > 0 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
gateways:
- apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
namespace: envoy-gateway
name: gateway-1
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "*.envoyproxy.io"
allowedRoutes:
namespaces:
from: All
httpRoutes:
- apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
namespace: default
name: httproute-1
spec:
hostnames:
- gateway.envoyproxy.io
parentRefs:
- namespace: envoy-gateway
name: gateway-1
sectionName: http
rules:
- matches:
- path:
value: "/"
backendRefs:
- name: service-1
port: 8080
filters:
- type: ExtensionRef
extensionRef:
group: gateway.envoyproxy.io
kind: AuthenticationFilter
name: not-exist
authenticationFilters:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: AuthenticationFilter
metadata:
name: example1
namespace: default
spec:
type: JWT
jwtProviders:
- name: example1
issuer: https://www.example1.com
audiences:
- foo.com
remoteJwks:
uri: https://foo.com/jwt/public-key/jwks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
gateways:
- apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
namespace: envoy-gateway
name: gateway-1
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "*.envoyproxy.io"
allowedRoutes:
namespaces:
from: All
status:
listeners:
- name: http
supportedKinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
attachedRoutes: 1
conditions:
- type: Programmed
status: "True"
reason: Programmed
message: Listener is ready
httpRoutes:
- apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
namespace: default
name: httproute-1
spec:
hostnames:
- gateway.envoyproxy.io
parentRefs:
- namespace: envoy-gateway
name: gateway-1
sectionName: http
rules:
- matches:
- path:
value: "/"
backendRefs:
- name: service-1
port: 8080
filters:
- type: ExtensionRef
extensionRef:
group: gateway.envoyproxy.io
kind: AuthenticationFilter
name: not-exist
status:
parents:
- parentRef:
namespace: envoy-gateway
name: gateway-1
sectionName: http
controllerName: gateway.envoyproxy.io/gatewayclass-controller
conditions:
- type: ResolvedRefs
status: "False"
reason: BackendNotFound
message: "Reference default/not-exist not found for filter type: ExtensionRef"
xdsIR:
envoy-gateway-gateway-1:
http:
- name: envoy-gateway-gateway-1-http
address: 0.0.0.0
port: 10080
hostnames:
- "*.envoyproxy.io"
routes:
- name: default-httproute-1-rule-0-match-0-gateway.envoyproxy.io
pathMatch:
prefix: "/"
headerMatches:
- name: ":authority"
exact: gateway.envoyproxy.io
# I believe the correct way to handle an invalid filter should be to allow the HTTPRoute to function
# normally but leave out the filter config and set the status, but this behaviour can be changed.
directResponse:
body: "Reference default/not-exist not found for filter type: ExtensionRef"
statusCode: 500
infraIR:
envoy-gateway-gateway-1:
proxy:
metadata:
labels:
gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
gateway.envoyproxy.io/owning-gateway-name: gateway-1
name: envoy-gateway-gateway-1
image: envoyproxy/envoy:translator-tests
listeners:
- address: ""
ports:
- name: http
protocol: "HTTP"
containerPort: 10080
servicePort: 80
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ httpRoutes:
group: gateway.envoyproxy.io
kind: AuthenticationFilter
name: test
authenFilters:
authenticationFilters:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: AuthenticationFilter
metadata:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ xdsIR:
headerMatches:
- name: ":authority"
exact: gateway.envoyproxy.io
requestAuthentication:
jwt:
providers:
- name: test
issuer: https://www.test.local
remoteJWKS:
uri: https://test.local/jwt/public-key/jwks.json
destinations:
- host: 7.7.7.7
port: 8080
Expand Down
Loading

0 comments on commit 77c98eb

Please sign in to comment.