Browse Source

APIGW: Update HTTPRouteConfigEntry for JWT Auth (#18422)

* Updated httproute config entry for JWT Filters

* Added manual deepcopy method for httproute jwt filter

* Fix test

* Update JWTFilter to be in oss file

* Add changelog

* Add build tags for deepcopy oss file
pull/18445/head
John Maguire 1 year ago committed by GitHub
parent
commit
df11e4e7b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .changelog/_18422.txt
  2. 3
      agent/structs/config_entry_apigw_jwt_oss.go
  3. 1
      agent/structs/config_entry_routes.go
  4. 6
      agent/structs/structs.deepcopy.go
  5. 11
      agent/structs/structs.deepcopy_oss.go
  6. 6
      api/config_entry_routes.go
  7. 134
      api/config_entry_routes_test.go
  8. 2
      proto/private/pbconfigentry/config_entry.gen.go
  9. 10
      proto/private/pbconfigentry/config_entry.pb.binary.go
  10. 1300
      proto/private/pbconfigentry/config_entry.pb.go
  11. 6
      proto/private/pbconfigentry/config_entry.proto
  12. 8
      proto/private/pbconfigentry/config_entry_oss.go

3
.changelog/_18422.txt

@ -0,0 +1,3 @@
```release-note:feature
config-entry(api-gateway): (Enterprise only) Add JWTFilter to HTTPRoute Filters
```

3
agent/structs/config_entry_apigw_jwt_oss.go

@ -8,3 +8,6 @@ package structs
// APIGatewayJWTRequirement holds the list of JWT providers to be verified against // APIGatewayJWTRequirement holds the list of JWT providers to be verified against
type APIGatewayJWTRequirement struct{} type APIGatewayJWTRequirement struct{}
// JWTFilter holds the JWT Filter configuration for an HTTPRoute
type JWTFilter struct{}

1
agent/structs/config_entry_routes.go

@ -422,6 +422,7 @@ type HTTPFilters struct {
URLRewrite *URLRewrite URLRewrite *URLRewrite
RetryFilter *RetryFilter RetryFilter *RetryFilter
TimeoutFilter *TimeoutFilter TimeoutFilter *TimeoutFilter
JWT *JWTFilter
} }
// HTTPHeaderFilter specifies how HTTP headers should be modified. // HTTPHeaderFilter specifies how HTTP headers should be modified.

6
agent/structs/structs.deepcopy.go

@ -421,6 +421,9 @@ func (o *HTTPRouteConfigEntry) DeepCopy() *HTTPRouteConfigEntry {
cp.Rules[i2].Filters.TimeoutFilter = new(TimeoutFilter) cp.Rules[i2].Filters.TimeoutFilter = new(TimeoutFilter)
*cp.Rules[i2].Filters.TimeoutFilter = *o.Rules[i2].Filters.TimeoutFilter *cp.Rules[i2].Filters.TimeoutFilter = *o.Rules[i2].Filters.TimeoutFilter
} }
if o.Rules[i2].Filters.JWT != nil {
cp.Rules[i2].Filters.JWT = o.Rules[i2].Filters.JWT.DeepCopy()
}
if o.Rules[i2].Matches != nil { if o.Rules[i2].Matches != nil {
cp.Rules[i2].Matches = make([]HTTPMatch, len(o.Rules[i2].Matches)) cp.Rules[i2].Matches = make([]HTTPMatch, len(o.Rules[i2].Matches))
copy(cp.Rules[i2].Matches, o.Rules[i2].Matches) copy(cp.Rules[i2].Matches, o.Rules[i2].Matches)
@ -489,6 +492,9 @@ func (o *HTTPRouteConfigEntry) DeepCopy() *HTTPRouteConfigEntry {
cp.Rules[i2].Services[i4].Filters.TimeoutFilter = new(TimeoutFilter) cp.Rules[i2].Services[i4].Filters.TimeoutFilter = new(TimeoutFilter)
*cp.Rules[i2].Services[i4].Filters.TimeoutFilter = *o.Rules[i2].Services[i4].Filters.TimeoutFilter *cp.Rules[i2].Services[i4].Filters.TimeoutFilter = *o.Rules[i2].Services[i4].Filters.TimeoutFilter
} }
if o.Rules[i2].Services[i4].Filters.JWT != nil {
cp.Rules[i2].Services[i4].Filters.JWT = o.Rules[i2].Services[i4].Filters.JWT.DeepCopy()
}
} }
} }
} }

11
agent/structs/structs.deepcopy_oss.go

@ -1,6 +1,17 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
//go:build !consulent
// +build !consulent
package structs package structs
// DeepCopy generates a deep copy of *APIGatewayJWTRequirement // DeepCopy generates a deep copy of *APIGatewayJWTRequirement
func (o *APIGatewayJWTRequirement) DeepCopy() *APIGatewayJWTRequirement { func (o *APIGatewayJWTRequirement) DeepCopy() *APIGatewayJWTRequirement {
return new(APIGatewayJWTRequirement) return new(APIGatewayJWTRequirement)
} }
// DeepCopy generates a deep copy of *JWTFilter
func (o *JWTFilter) DeepCopy() *JWTFilter {
return new(JWTFilter)
}

6
api/config_entry_routes.go

@ -201,6 +201,7 @@ type HTTPFilters struct {
URLRewrite *URLRewrite URLRewrite *URLRewrite
RetryFilter *RetryFilter RetryFilter *RetryFilter
TimeoutFilter *TimeoutFilter TimeoutFilter *TimeoutFilter
JWT *JWTFilter
} }
// HTTPHeaderFilter specifies how HTTP headers should be modified. // HTTPHeaderFilter specifies how HTTP headers should be modified.
@ -226,6 +227,11 @@ type TimeoutFilter struct {
IdleTimeout time.Duration IdleTimeout time.Duration
} }
// JWTFilter specifies the JWT configuration for a route
type JWTFilter struct {
Providers []*APIGatewayJWTProvider `json:",omitempty"`
}
// HTTPRouteRule specifies the routing rules used to determine what upstream // HTTPRouteRule specifies the routing rules used to determine what upstream
// service an HTTP request is routed to. // service an HTTP request is routed to.
type HTTPRouteRule struct { type HTTPRouteRule struct {

134
api/config_entry_routes_test.go

@ -0,0 +1,134 @@
package api
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestAPI_ConfigEntries_HTTPRoute(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()
configEntries := c.ConfigEntries()
route1 := &HTTPRouteConfigEntry{
Kind: HTTPRoute,
Name: "route1",
}
route2 := &HTTPRouteConfigEntry{
Kind: HTTPRoute,
Name: "route2",
}
// set it
_, wm, err := configEntries.Set(route1, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)
// also set the second one
_, wm, err = configEntries.Set(route2, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)
// get it
entry, qm, err := configEntries.Get(HTTPRoute, "route1", nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.NotEqual(t, 0, qm.RequestTime)
// verify it
readRoute, ok := entry.(*HTTPRouteConfigEntry)
require.True(t, ok)
require.Equal(t, route1.Kind, readRoute.Kind)
require.Equal(t, route1.Name, readRoute.Name)
require.Equal(t, route1.Meta, readRoute.Meta)
require.Equal(t, route1.Meta, readRoute.GetMeta())
// update it
route1.Rules = []HTTPRouteRule{
{
Filters: HTTPFilters{
URLRewrite: &URLRewrite{
Path: "abc",
},
},
},
}
// CAS fail
written, _, err := configEntries.CAS(route1, 0, nil)
require.NoError(t, err)
require.False(t, written)
// CAS success
written, wm, err = configEntries.CAS(route1, readRoute.ModifyIndex, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)
require.True(t, written)
// re-setting should not yield an error
_, wm, err = configEntries.Set(route1, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)
route2.Rules = []HTTPRouteRule{
{
Filters: HTTPFilters{
URLRewrite: &URLRewrite{
Path: "def",
},
},
},
}
_, wm, err = configEntries.Set(route2, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)
// list them
entries, qm, err := configEntries.List(HTTPRoute, nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.NotEqual(t, 0, qm.RequestTime)
require.Len(t, entries, 2)
for _, entry = range entries {
switch entry.GetName() {
case "route1":
// this also verifies that the update value was persisted and
// the updated values are seen
readRoute, ok = entry.(*HTTPRouteConfigEntry)
require.True(t, ok)
require.Equal(t, route1.Kind, readRoute.Kind)
require.Equal(t, route1.Name, readRoute.Name)
require.Len(t, readRoute.Rules, 1)
require.Equal(t, route1.Rules, readRoute.Rules)
case "route2":
readRoute, ok = entry.(*HTTPRouteConfigEntry)
require.True(t, ok)
require.Equal(t, route2.Kind, readRoute.Kind)
require.Equal(t, route2.Name, readRoute.Name)
require.Len(t, readRoute.Rules, 1)
require.Equal(t, route2.Rules, readRoute.Rules)
}
}
// delete it
wm, err = configEntries.Delete(HTTPRoute, "route1", nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)
// verify deletion
_, _, err = configEntries.Get(HTTPRoute, "route1", nil)
require.Error(t, err)
}

2
proto/private/pbconfigentry/config_entry.gen.go

@ -411,6 +411,7 @@ func HTTPFiltersToStructs(s *HTTPFilters, t *structs.HTTPFilters) {
TimeoutFilterToStructs(s.TimeoutFilter, &x) TimeoutFilterToStructs(s.TimeoutFilter, &x)
t.TimeoutFilter = &x t.TimeoutFilter = &x
} }
t.JWT = routeJWTFilterToStructs(s.JWT)
} }
func HTTPFiltersFromStructs(t *structs.HTTPFilters, s *HTTPFilters) { func HTTPFiltersFromStructs(t *structs.HTTPFilters, s *HTTPFilters) {
if s == nil { if s == nil {
@ -441,6 +442,7 @@ func HTTPFiltersFromStructs(t *structs.HTTPFilters, s *HTTPFilters) {
TimeoutFilterFromStructs(t.TimeoutFilter, &x) TimeoutFilterFromStructs(t.TimeoutFilter, &x)
s.TimeoutFilter = &x s.TimeoutFilter = &x
} }
s.JWT = routeJWTFilterFromStructs(t.JWT)
} }
func HTTPHeaderFilterToStructs(s *HTTPHeaderFilter, t *structs.HTTPHeaderFilter) { func HTTPHeaderFilterToStructs(s *HTTPHeaderFilter, t *structs.HTTPHeaderFilter) {
if s == nil { if s == nil {

10
proto/private/pbconfigentry/config_entry.pb.binary.go

@ -687,6 +687,16 @@ func (msg *TimeoutFilter) UnmarshalBinary(b []byte) error {
return proto.Unmarshal(b, msg) return proto.Unmarshal(b, msg)
} }
// MarshalBinary implements encoding.BinaryMarshaler
func (msg *JWTFilter) MarshalBinary() ([]byte, error) {
return proto.Marshal(msg)
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (msg *JWTFilter) UnmarshalBinary(b []byte) error {
return proto.Unmarshal(b, msg)
}
// MarshalBinary implements encoding.BinaryMarshaler // MarshalBinary implements encoding.BinaryMarshaler
func (msg *HTTPHeaderFilter) MarshalBinary() ([]byte, error) { func (msg *HTTPHeaderFilter) MarshalBinary() ([]byte, error) {
return proto.Marshal(msg) return proto.Marshal(msg)

1300
proto/private/pbconfigentry/config_entry.pb.go

File diff suppressed because it is too large Load Diff

6
proto/private/pbconfigentry/config_entry.proto

@ -912,6 +912,8 @@ message HTTPFilters {
URLRewrite URLRewrite = 2; URLRewrite URLRewrite = 2;
RetryFilter RetryFilter = 3; RetryFilter RetryFilter = 3;
TimeoutFilter TimeoutFilter = 4; TimeoutFilter TimeoutFilter = 4;
// mog: func-to=routeJWTFilterToStructs func-from=routeJWTFilterFromStructs
JWTFilter JWT = 5;
} }
// mog annotation: // mog annotation:
@ -947,6 +949,10 @@ message TimeoutFilter {
google.protobuf.Duration IdleTimeout = 2; google.protobuf.Duration IdleTimeout = 2;
} }
message JWTFilter {
repeated APIGatewayJWTProvider Providers = 1;
}
// mog annotation: // mog annotation:
// //
// target=github.com/hashicorp/consul/agent/structs.HTTPHeaderFilter // target=github.com/hashicorp/consul/agent/structs.HTTPHeaderFilter

8
proto/private/pbconfigentry/config_entry_oss.go

@ -15,3 +15,11 @@ func gwJWTRequirementToStructs(m *APIGatewayJWTRequirement) *structs.APIGatewayJ
func gwJWTRequirementFromStructs(*structs.APIGatewayJWTRequirement) *APIGatewayJWTRequirement { func gwJWTRequirementFromStructs(*structs.APIGatewayJWTRequirement) *APIGatewayJWTRequirement {
return &APIGatewayJWTRequirement{} return &APIGatewayJWTRequirement{}
} }
func routeJWTFilterToStructs(m *JWTFilter) *structs.JWTFilter {
return &structs.JWTFilter{}
}
func routeJWTFilterFromStructs(*structs.JWTFilter) *JWTFilter {
return &JWTFilter{}
}

Loading…
Cancel
Save