Skip to content
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
1 change: 0 additions & 1 deletion api/v1alpha1/basic_auth_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,5 @@ type BasicAuth struct {
// If it is not specified, the username will not be forwarded.
//
// +optional
// +notImplementedHide
ForwardUsernameHeader *string `json:"forwardUsernameHeader,omitempty"`
}
5 changes: 3 additions & 2 deletions internal/gatewayapi/securitypolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,8 +1119,9 @@ func (t *Translator) buildBasicAuth(
}

return &ir.BasicAuth{
Name: irConfigName(policy),
Users: usersSecretBytes,
Name: irConfigName(policy),
Users: usersSecretBytes,
ForwardUsernameHeader: basicAuth.ForwardUsernameHeader,
}, nil
}

Expand Down
6 changes: 6 additions & 0 deletions internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,12 @@ type BasicAuth struct {

// The username-password pairs in htpasswd format.
Users PrivateBytes `json:"users,omitempty" yaml:"users,omitempty"`

// This field specifies the header name to forward a successfully authenticated user to
// the backend. The header will be added to the request with the username as the value.
//
// If it is not specified, the username will not be forwarded.
ForwardUsernameHeader *string `json:"forwardUsernameHeader,omitempty" yaml:"forwardUsernameHeader,omitempty"`
}

// APIKeyAuth defines the schema for the API Key Authentication.
Expand Down
5 changes: 5 additions & 0 deletions internal/ir/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 40 additions & 27 deletions internal/xds/translator/basicauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,49 @@ type basicAuth struct{}

var _ httpFilter = &basicAuth{}

// patchHCM builds and appends the basic_auth Filter to the HTTP Connection Manager
// if applicable, and it does not already exist.
// patchHCM updates the HTTPConnectionManager with a Basic Auth HTTP filter for routes requiring authentication.
// It scans through all routes in the provided HTTPListener, and if a route has a BasicAuth configuration,
// it checks for the presence of a corresponding filter in the manager. If the filter is not already present,
// it generates and appends a new Basic Auth filter.
// The function returns an error if either the HTTPConnectionManager or HTTPListener is nil, or if an error occurs
// during the filter creation process.
func (*basicAuth) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error {
if mgr == nil {
return errors.New("hcm is nil")
}
if irListener == nil {
return errors.New("ir listener is nil")
}
if hcmContainsFilter(mgr, egv1a1.EnvoyFilterBasicAuth.String()) {
return nil
}

var (
irBasicAuth *ir.BasicAuth
filter *hcmv3.HttpFilter
err error
)
var errs error

for _, route := range irListener.Routes {
if route.Security != nil && route.Security.BasicAuth != nil {
irBasicAuth = route.Security.BasicAuth
break
if route.Security == nil || route.Security.BasicAuth == nil {
continue
}
}
if irBasicAuth == nil {
return nil
}

// We use the first route that contains the basicAuth config to build the filter.
// The HCM-level filter config doesn't matter since it is overridden at the route level.
if filter, err = buildHCMBasicAuthFilter(irBasicAuth); err != nil {
return err
// Only generates one Basic Auth Envoy filter for each unique name.
// For example, if there are two routes under the same gateway with the
// same BasicAuth config, only one BasicAuth filter will be generated.
if hcmContainsFilter(mgr, basicAuthFilterName(route.Security.BasicAuth)) {
continue
}

filter, err := buildHCMBasicAuthFilter(route.Security.BasicAuth)
if err != nil {
errs = errors.Join(errs, err)
continue
}

mgr.HttpFilters = append(mgr.HttpFilters, filter)
}
mgr.HttpFilters = append(mgr.HttpFilters, filter)
return err

return errs
}

// patchHCM builds and appends the basic_auth Filter to the HTTP Connection Manager
// if applicable, and it does not already exist.

// buildHCMBasicAuthFilter returns a basic_auth HTTP filter from the provided IR HTTPRoute.
func buildHCMBasicAuthFilter(basicAuth *ir.BasicAuth) (*hcmv3.HttpFilter, error) {
var (
Expand All @@ -82,20 +87,28 @@ func buildHCMBasicAuthFilter(basicAuth *ir.BasicAuth) (*hcmv3.HttpFilter, error)
},
},
}
// Set the ForwardUsernameHeader field if it is specified.
if basicAuth.ForwardUsernameHeader != nil && *basicAuth.ForwardUsernameHeader != "" {
basicAuthProto.ForwardUsernameHeader = *basicAuth.ForwardUsernameHeader
}

if basicAuthAny, err = proto.ToAnyWithValidation(basicAuthProto); err != nil {
return nil, err
}

return &hcmv3.HttpFilter{
Name: egv1a1.EnvoyFilterBasicAuth.String(),
Name: basicAuthFilterName(basicAuth),
ConfigType: &hcmv3.HttpFilter_TypedConfig{
TypedConfig: basicAuthAny,
},
Disabled: true,
}, nil
}

func basicAuthFilterName(basicAuth *ir.BasicAuth) string {
return perRouteFilterName(egv1a1.EnvoyFilterBasicAuth, basicAuth.Name)
}

func (*basicAuth) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error {
return nil
}
Expand All @@ -118,9 +131,9 @@ func (*basicAuth) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error
basicAuthAny *anypb.Any
err error
)

filterName := basicAuthFilterName(irRoute.Security.BasicAuth)
perFilterCfg = route.GetTypedPerFilterConfig()
if _, ok := perFilterCfg[egv1a1.EnvoyFilterBasicAuth.String()]; ok {
if _, ok := perFilterCfg[filterName]; ok {
// This should not happen since this is the only place where the filter
// config is added in a route.
return fmt.Errorf("route already contains filter config: %s, %+v",
Expand All @@ -136,7 +149,7 @@ func (*basicAuth) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error
if perFilterCfg == nil {
route.TypedPerFilterConfig = make(map[string]*anypb.Any)
}
route.TypedPerFilterConfig[egv1a1.EnvoyFilterBasicAuth.String()] = basicAuthAny
route.TypedPerFilterConfig[filterName] = basicAuthAny

return nil
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
http:
- address: 0.0.0.0
hostnames:
- '*'
isHTTP2: false
name: default/gateway-1/http
path:
escapedSlashesAction: UnescapeAndRedirect
mergeSlashes: true
port: 10080
routes:
- name: httproute/default/httproute-1/rule/0/match/0/www_foo_com
hostname: www.foo.com
isHTTP2: false
pathMatch:
distinct: false
name: ""
prefix: /foo1
backendWeights:
invalid: 0
valid: 0
destination:
name: httproute/default/httproute-1/rule/0
settings:
- addressType: IP
endpoints:
- host: 7.7.7.7
port: 8080
protocol: HTTP
weight: 1
security:
basicAuth:
name: securitypolicy/default/policy-for-http-route-1
users: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=
forwardUsernameHeader: x-username
- name: httproute/default/httproute-1/rule/1/match/0/www_foo_com
backendWeights:
hostname: www.foo.com
isHTTP2: false
pathMatch:
distinct: false
name: ""
prefix: /foo2
invalid: 0
valid: 0
destination:
name: httproute/default/httproute-1/rule/1
settings:
- addressType: IP
endpoints:
- host: 7.7.7.7
port: 8080
protocol: HTTP
weight: 1
security:
basicAuth:
name: securitypolicy/default/policy-for-http-route-1
users: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=
- name: httproute/default/httproute-2/rule/0/match/0/www_bar_com
hostname: www.bar.com
isHTTP2: false
pathMatch:
distinct: false
name: ""
prefix: /bar
backendWeights:
invalid: 0
valid: 0
destination:
name: httproute/default/httproute-2/rule/0
settings:
- addressType: IP
endpoints:
- host: 7.7.7.7
port: 8080
protocol: HTTP
weight: 1
security:
basicAuth:
name: securitypolicy/default/policy-for-gateway-1
users: Zm9vOntTSEF9WXMyM0FnLzVJT1dxWkN3OVFHYVZEZEh3SDAwPQpmb28xOntTSEF9ZGpaMTFxSFkwS09pamV5bUs3YUt2WXV2aHZNPQo=
forwardUsernameHeader: x-user-id
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
- circuitBreakers:
thresholds:
- maxRetries: 1024
commonLbConfig:
localityWeightedLbConfig: {}
connectTimeout: 10s
dnsLookupFamily: V4_PREFERRED
edsClusterConfig:
edsConfig:
ads: {}
resourceApiVersion: V3
serviceName: httproute/default/httproute-1/rule/0
ignoreHealthOnHostRemoval: true
lbPolicy: LEAST_REQUEST
name: httproute/default/httproute-1/rule/0
perConnectionBufferLimitBytes: 32768
type: EDS
- circuitBreakers:
thresholds:
- maxRetries: 1024
commonLbConfig:
localityWeightedLbConfig: {}
connectTimeout: 10s
dnsLookupFamily: V4_PREFERRED
edsClusterConfig:
edsConfig:
ads: {}
resourceApiVersion: V3
serviceName: httproute/default/httproute-1/rule/1
ignoreHealthOnHostRemoval: true
lbPolicy: LEAST_REQUEST
name: httproute/default/httproute-1/rule/1
perConnectionBufferLimitBytes: 32768
type: EDS
- circuitBreakers:
thresholds:
- maxRetries: 1024
commonLbConfig:
localityWeightedLbConfig: {}
connectTimeout: 10s
dnsLookupFamily: V4_PREFERRED
edsClusterConfig:
edsConfig:
ads: {}
resourceApiVersion: V3
serviceName: httproute/default/httproute-2/rule/0
ignoreHealthOnHostRemoval: true
lbPolicy: LEAST_REQUEST
name: httproute/default/httproute-2/rule/0
perConnectionBufferLimitBytes: 32768
type: EDS
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
- clusterName: httproute/default/httproute-1/rule/0
endpoints:
- lbEndpoints:
- endpoint:
address:
socketAddress:
address: 7.7.7.7
portValue: 8080
loadBalancingWeight: 1
loadBalancingWeight: 1
locality: {}
- clusterName: httproute/default/httproute-1/rule/1
endpoints:
- lbEndpoints:
- endpoint:
address:
socketAddress:
address: 7.7.7.7
portValue: 8080
loadBalancingWeight: 1
loadBalancingWeight: 1
locality: {}
- clusterName: httproute/default/httproute-2/rule/0
endpoints:
- lbEndpoints:
- endpoint:
address:
socketAddress:
address: 7.7.7.7
portValue: 8080
loadBalancingWeight: 1
loadBalancingWeight: 1
locality: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
- address:
socketAddress:
address: 0.0.0.0
portValue: 10080
defaultFilterChain:
filters:
- name: envoy.filters.network.http_connection_manager
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
commonHttpProtocolOptions:
headersWithUnderscoresAction: REJECT_REQUEST
http2ProtocolOptions:
initialConnectionWindowSize: 1048576
initialStreamWindowSize: 65536
maxConcurrentStreams: 100
httpFilters:
- disabled: true
name: envoy.filters.http.basic_auth/securitypolicy/default/policy-for-http-route-1
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth
forwardUsernameHeader: x-username
users:
inlineBytes: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=
- disabled: true
name: envoy.filters.http.basic_auth/securitypolicy/default/policy-for-gateway-1
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth
forwardUsernameHeader: x-user-id
users:
inlineBytes: Zm9vOntTSEF9WXMyM0FnLzVJT1dxWkN3OVFHYVZEZEh3SDAwPQpmb28xOntTSEF9ZGpaMTFxSFkwS09pamV5bUs3YUt2WXV2aHZNPQo=
- name: envoy.filters.http.router
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
suppressEnvoyHeaders: true
mergeSlashes: true
normalizePath: true
pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
rds:
configSource:
ads: {}
resourceApiVersion: V3
routeConfigName: default/gateway-1/http
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
useRemoteAddress: true
name: default/gateway-1/http
name: default/gateway-1/http
perConnectionBufferLimitBytes: 32768
Loading