Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APIGW] NET-5017 JWT Cleanup/Status Conditions #18700

Merged
merged 4 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 47 additions & 26 deletions agent/consul/gateways/controller_gateways.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,40 +236,55 @@ func (r *apiGatewayReconciler) reconcileGateway(_ context.Context, req controlle
return err
}

meta := newGatewayMeta(gateway, bound)
_, jwtProvidersConfigEntries, err := store.ConfigEntriesByKind(nil, structs.JWTProvider, wildcardMeta())
if err != nil {
return err
}

jwtProviders := make(map[string]*structs.JWTProviderConfigEntry, len(jwtProvidersConfigEntries))
for _, provider := range jwtProvidersConfigEntries {
jwtProviders[provider.GetName()] = provider.(*structs.JWTProviderConfigEntry)
}

meta := newGatewayMeta(gateway, bound, jwtProviders)

certificateErrors, err := meta.checkCertificates(store)
if err != nil {
logger.Warn("error checking gateway certificates", "error", err)
return err
}

jwtErrors, err := meta.checkJWTProviders(store)
jwtErrors, err := meta.checkJWTProviders()
if err != nil {
logger.Warn("error checking gateway JWT Providers", "error", err)
return err
}

// set each listener as having valid certs, then overwrite that status condition
// set each listener as having resolved refs, then overwrite that status condition
// if there are any certificate errors
meta.eachListener(func(listener *structs.APIGatewayListener, bound *structs.BoundAPIGatewayListener) error {
meta.eachListener(func(_ *structs.APIGatewayListener, bound *structs.BoundAPIGatewayListener) error {
listenerRef := structs.ResourceReference{
Kind: structs.APIGateway,
Name: meta.BoundGateway.Name,
SectionName: bound.Name,
EnterpriseMeta: meta.BoundGateway.EnterpriseMeta,
}
updater.SetCondition(validCertificate(listenerRef))
updater.SetCondition(resolvedRefs(listenerRef))
return nil
})

for ref, err := range certificateErrors {
updater.SetCondition(invalidCertificate(ref, err))
}

for ref, err := range jwtErrors {
updater.SetCondition(invalidJWTProvider(ref, err))
}

if len(certificateErrors) > 0 {
updater.SetCondition(invalidCertificates())
}

if len(jwtErrors) > 0 {
updater.SetCondition(invalidJWTProviders())
}
Expand Down Expand Up @@ -477,13 +492,6 @@ func (r *apiGatewayReconciler) reconcileRoute(_ context.Context, req controller.
updater.SetCondition(routeNoUpstreams())
}

if httpRoute, ok := route.(*structs.HTTPRouteConfigEntry); ok {
err := validateJWTForRoute(store, updater, httpRoute)
if err != nil {
return err
}
}

// the route is valid, attempt to bind it to all gateways
r.logger.Trace("binding routes to gateway")
modifiedGateways, boundRefs, bindErrors := bindRoutesToGateways(route, meta...)
Expand Down Expand Up @@ -584,6 +592,10 @@ type gatewayMeta struct {
// the map values are pointers so that we can update them directly
// and have the changes propagate back to the container gateways.
boundListeners map[string]*structs.BoundAPIGatewayListener
// jwtProviders holds the list of all the JWT Providers in a given partition
// we expect this list to be relatively small so we're okay with holding them all
// in memory
jwtProviders map[string]*structs.JWTProviderConfigEntry
}

// getAllGatewayMeta returns a pre-constructed list of all valid gateway and state
Expand All @@ -599,6 +611,16 @@ func getAllGatewayMeta(store *state.Store) ([]*gatewayMeta, error) {
return nil, err
}

_, jwtProvidersConfigEntries, err := store.ConfigEntriesByKind(nil, structs.JWTProvider, wildcardMeta())
if err != nil {
return nil, err
}

jwtProviders := make(map[string]*structs.JWTProviderConfigEntry, len(jwtProvidersConfigEntries))
for _, provider := range jwtProvidersConfigEntries {
jwtProviders[provider.GetName()] = provider.(*structs.JWTProviderConfigEntry)
}

meta := make([]*gatewayMeta, 0, len(boundGateways))
for _, b := range boundGateways {
bound := b.(*structs.BoundAPIGatewayConfigEntry)
Expand All @@ -608,6 +630,7 @@ func getAllGatewayMeta(store *state.Store) ([]*gatewayMeta, error) {
meta = append(meta, (&gatewayMeta{
BoundGateway: bound,
Gateway: gateway,
jwtProviders: jwtProviders,
}).initialize())
break
}
Expand Down Expand Up @@ -662,6 +685,14 @@ func (g *gatewayMeta) updateRouteBinding(route structs.BoundRoute) (bool, []stru
if err != nil {
errors[ref] = err
}

if httpRoute, ok := route.(*structs.HTTPRouteConfigEntry); ok {
var jwtErrors map[structs.ResourceReference]error
didBind, jwtErrors = g.validateJWTForRoute(httpRoute)
for ref, err := range jwtErrors {
errors[ref] = err
}
}
if didBind {
refDidBind = true
listenerBound[listener.Name] = true
Expand All @@ -673,6 +704,7 @@ func (g *gatewayMeta) updateRouteBinding(route structs.BoundRoute) (bool, []stru
if !refDidBind && errors[ref] == nil {
errors[ref] = fmt.Errorf("failed to bind route %s to gateway %s with listener '%s'", route.GetName(), g.Gateway.Name, ref.SectionName)
}

if refDidBind {
boundRefs = append(boundRefs, ref)
}
Expand Down Expand Up @@ -845,7 +877,7 @@ func (g *gatewayMeta) initialize() *gatewayMeta {
}

// newGatewayMeta returns an object that wraps the given APIGateway and BoundAPIGateway
func newGatewayMeta(gateway *structs.APIGatewayConfigEntry, bound structs.ConfigEntry) *gatewayMeta {
func newGatewayMeta(gateway *structs.APIGatewayConfigEntry, bound structs.ConfigEntry, jwtProviders map[string]*structs.JWTProviderConfigEntry) *gatewayMeta {
var b *structs.BoundAPIGatewayConfigEntry
if bound == nil {
b = &structs.BoundAPIGatewayConfigEntry{
Expand All @@ -871,6 +903,7 @@ func newGatewayMeta(gateway *structs.APIGatewayConfigEntry, bound structs.Config
return (&gatewayMeta{
BoundGateway: b,
Gateway: gateway,
jwtProviders: jwtProviders,
}).initialize()
}

Expand All @@ -888,7 +921,7 @@ func gatewayAccepted() structs.Condition {
// invalidCertificate returns a condition used when a gateway references a
// certificate that does not exist. It takes a ref used to scope the condition
// to a given APIGateway listener.
func validCertificate(ref structs.ResourceReference) structs.Condition {
func resolvedRefs(ref structs.ResourceReference) structs.Condition {
return structs.NewGatewayCondition(
api.GatewayConditionResolvedRefs,
api.ConditionStatusTrue,
Expand Down Expand Up @@ -995,18 +1028,6 @@ func gatewayNotFound(ref structs.ResourceReference) structs.Condition {
)
}

// jwtProviderNotFound marks a Route as having failed to bind to a referenced APIGateway due to
// one or more of the referenced JWT providers not existing (or having not been reconciled yet)
func jwtProviderNotFound(ref structs.ResourceReference, err error) structs.Condition {
return structs.NewRouteCondition(
api.RouteConditionBound,
api.ConditionStatusFalse,
api.RouteReasonGatewayNotFound,
err.Error(),
ref,
)
}

// routeUnbound marks the route as having failed to bind to the referenced APIGateway
func routeUnbound(ref structs.ResourceReference, err error) structs.Condition {
return structs.NewRouteCondition(
Expand Down
6 changes: 3 additions & 3 deletions agent/consul/gateways/controller_gateways_ce.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ func (r *apiGatewayReconciler) enqueueJWTProviderReferencedGatewaysAndHTTPRoutes
return nil
}

func (m *gatewayMeta) checkJWTProviders(_ *state.Store) (map[structs.ResourceReference]error, error) {
func (m *gatewayMeta) checkJWTProviders() (map[structs.ResourceReference]error, error) {
return nil, nil
}

func validateJWTForRoute(_ *state.Store, _ *structs.StatusUpdater, _ *structs.HTTPRouteConfigEntry) error {
return nil
func (m *gatewayMeta) validateJWTForRoute(_ *structs.HTTPRouteConfigEntry) (bool, map[structs.ResourceReference]error) {
return true, nil
}
26 changes: 13 additions & 13 deletions agent/consul/gateways/controller_gateways_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2013,7 +2013,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Expand Down Expand Up @@ -2111,7 +2111,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Expand Down Expand Up @@ -2240,7 +2240,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Expand Down Expand Up @@ -2389,7 +2389,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Expand Down Expand Up @@ -2536,7 +2536,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Expand Down Expand Up @@ -2700,12 +2700,12 @@ func TestAPIGatewayController(t *testing.T) {
Name: "gateway",
SectionName: "tcp-listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "tcp-listener",
Expand Down Expand Up @@ -3054,7 +3054,7 @@ func TestAPIGatewayController(t *testing.T) {
Name: "gateway",
SectionName: "http-listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
Expand Down Expand Up @@ -3407,7 +3407,7 @@ func TestAPIGatewayController(t *testing.T) {
Name: "gateway",
SectionName: "http-listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
Expand Down Expand Up @@ -3504,12 +3504,12 @@ func TestAPIGatewayController(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "listener-1",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "listener-2",
Expand Down Expand Up @@ -3728,7 +3728,7 @@ func TestAPIGatewayController(t *testing.T) {
Name: "gateway",
SectionName: "invalid-listener",
}, errors.New("certificate \"missing certificate\" not found")),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "valid-listener",
Expand Down Expand Up @@ -3834,7 +3834,7 @@ func TestAPIGatewayController(t *testing.T) {
Name: "gateway",
SectionName: "http-listener",
}),
validCertificate(structs.ResourceReference{
resolvedRefs(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
Expand Down
39 changes: 31 additions & 8 deletions agent/xds/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -1248,21 +1248,44 @@ func convertPermission(perm *structs.IntentionPermission) *envoy_rbac_v3.Permiss

switch {
case hdr.Exact != "":
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_ExactMatch{
ExactMatch: hdr.Exact,
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_matcher_v3.StringMatcher{
MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{
Exact: hdr.Exact,
},
IgnoreCase: false,
},
}
case hdr.Regex != "":
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_SafeRegexMatch{
SafeRegexMatch: response.MakeEnvoyRegexMatch(hdr.Regex),
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_matcher_v3.StringMatcher{
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
SafeRegex: response.MakeEnvoyRegexMatch(hdr.Regex),
},
IgnoreCase: false,
},
}

case hdr.Prefix != "":
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_PrefixMatch{
PrefixMatch: hdr.Prefix,
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_matcher_v3.StringMatcher{
MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{
Prefix: hdr.Prefix,
},
IgnoreCase: false,
},
}

case hdr.Suffix != "":
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_SuffixMatch{
SuffixMatch: hdr.Suffix,
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_matcher_v3.StringMatcher{
MatchPattern: &envoy_matcher_v3.StringMatcher_Suffix{
Suffix: hdr.Suffix,
},
IgnoreCase: false,
},
}

case hdr.Present:
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_PresentMatch{
PresentMatch: true,
Expand Down
Loading