The open-source Spring Cloud Gateway project includes a number of built-in filters for use in Gateway routes. Spring Cloud Gateway provides a number of custom filters in addition to those included in the OSS project.

Filters Included In Spring Cloud Gateway OSS

Filters in Spring Cloud Gateway OSS can be used in Spring Cloud Gateway for Kubernetes. Spring Cloud Gateway OSS includes a number of GatewayFilter factories used to create filters for routes. For a complete list of these factories, see the Spring Cloud Gateway OSS documentation.

Filters Added In Spring Cloud Gateway for Kubernetes

Following sections offers information about the custom filters added in VMware Spring Cloud Gateway and how you can use them.

Scope-based Access Control: Scopes Filter

When SSO is enabled, you can add fine-tune access control based on OIDC scopes by adding the Scopes filter.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
  routes:
  - ssoEnabled: true
    predicates:
      - Path=/api/**
    filters:
      - Scopes=api.read,api.write,user

Response Headers modification: RewriteAllResponseHeaders Filter

This filter provides a convenient method to apply a transformation to all headers coming from target service through the gateway. It accepts a regular expression to search for in header values and text to replace the matching expression with.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
  routes:
  - ssoEnabled: true
    predicates:
      - Path=/api/**
    filters:
      - RewriteAllResponseHeaders=password=[^&]+,password=***

In the example, a header containing /42?user=ford&password=omg!what&flag=true will be returned as /42?user=ford&password=***&flag=true.

Response Body modification: RewriteResponseBody Filter

This filter provides a convenient method to apply a transformation to any body content from target service through the gateway, it won't apply any transformation to response headers. It accepts a list of regular expressions (separated by commas) to search for in value and text to replace the matching expression with (separated by colon).

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
  routes:
  - ssoEnabled: true
    predicates:
      - Path=/api/**
    filters:
      - RewriteResponseBody=foo:bar,/path-one/:/path-two/

In the example, in a body content of any type:

  • foo will be replaced by bar
  • /path-one/ will be replaced by /path-two/

Response Body modification: RemoveJsonAttributesResponseBody Filter

This filter provides a convenient method to apply a transformation to JSON body content from target service through the gateway. It accepts a list of attribute names to search for and an optional last parameter from the list can be a boolean to remove the attributes just at root level (default value if not present at the end of the parameter configuration, false) or recursively (true).

Note: Applying the recursive deletion mode on a large JSON data will affect on service latency.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
  routes:
  - ssoEnabled: true
    predicates:
      - Path=/api/**
    filters:
      - RemoveJsonAttributesResponseBody=origin,foo,true

In the example, the attributes origin and bar will be deleted from the JSON content body at any level.

Role-based Access Control: Roles Filter

Similarly to scope-based access control, it's possible to use custom Claim properties to apply role-base access control with the Roles filter. Furthermore, if you are not using SSO feature, you can still use role-based control provided you apply JwtKey filter.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
  routes:
  - ssoEnabled: true
    predicates:
      - Path=/api/**
    filters:
      - Roles=role_01,role_02

By default, SpringCloudGateway will check the role values under the roles claim, but you can change it using spec.sso.roles-attribute-name property in the Gateway. SCG expects the roles claim to be an array (roles = ["user-role-1", "user-role-2"]), but a single string is also accepted when role only contains one value (roles = "user-role").

Additionally, spec.sso.roles-attribute-name also supports nested JSON values like custom-data.user.roles.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: mygateway
spec:
  sso:
    roles-attribute-name: my-roles

Passing User Identity: TokenRelay Filter

A Token Relay is where an OAuth2 or OIDC consumer acts as a Client and forwards the incoming token to outgoing resource requests. In this case, the consumer can be any service accessible from any of the configured routes with ssoEnabled: true.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
  routes:
  - ssoEnabled: true
    tokenRelay: true
    predicates:
      - Path=/api/**

When enabling TokenRelay, Spring Cloud Gateway for Kubernetes will pass the currently-authenticated user's identity token to the app when the user accesses the app's route.

Note: The TokenRelay filter will not work together with the BasicAuth filter as both filters use the Authorization header.

Reroute traffic on error response: CircuitBreaker Filter

The CircuitBreaker filter provides the ability to reroute a request when an API route is responding with an error response code.

When defining a RouteConfiguration, you can add the CircuitBreaker filter by including it in the list of filters for the route. For example, you can add a route with a fallback route to forward on error response:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - CircuitBreaker=myCircuitBreaker,forward:/inCaseOfFailureUseThis

You can also add several options for fine tuning:

  • A list of status codes that will trigger the fallback behaviour, this can be expressed in number and text format separated by a colon.
  • The failure rate threshold above which the circuit breaker will be opened (default 50%, expressed as float value).
  • Duration of wait time before closing again (default 60s).
      - CircuitBreaker=myCircuitBreaker,forward:/inCaseOfFailureUseThis,401:NOT_FOUND:500
      - CircuitBreaker=myCircuitBreaker,forward:/inCaseOfFailureUseThis,401:NOT_FOUND:500,10,30s

Allows adding CircuitBreaker exception details in the headers before forwarding: FallbackHeaders Filter

The FallbackHeaders filter provides the ability to add CircuitBreaker execution exception details in the headers of a request forwarded to a fallback route in an external application

When defining a RouteConfiguration, you can add the FallbackHeaders filter by including it in the list of filters for the fallback route. For example, you can add the fallback route to add X-Exception:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - CircuitBreaker="myCircuitBreaker,forward:/inCaseOfFailureUseThis"
  - uri: http://localhost:9994
    predicates:
      - Path=/fallback
    filters:
      - FallbackHeaders

You can optionally configure just the executionExceptionTypeHeaderName by editing the filter above like:

    filters:
     - FallbackHeaders= My-Execution-Exception-Type

Or change all executionExceptionTypeHeaderName, executionExceptionMessageHeaderName, rootCauseExceptionTypeHeaderName using the following modification

    filters:
     - FallbackHeaders= My-Execution-Exception-Type, My-Execution-Exception-Message, My-Root-Cause-Exception-Type

Multiple client JWT validation: JwtKey Filter

The JwtKey filter allows validating JSON Web Tokens (JWT) generated by different providers with different signature keys. It is expected that every request has a key id that allows identifying which key validates the token signature.

SpringCloudGateway integrates with Vault on Kubernetes and assumes a Vault Agent Injector has been deployed to the cluster. This filter requires additional Vault integration parameters defined in the custom resource to be enabled in SpringCloudGateway. The required parameters are serviceAccount.name, extensions.secretsProviders, and jwtKey.enabled alongside jwtKey.secretsProviderName where:

  • serviceAccount.name is the name of the ServiceAccount used by the gateway instances
  • extensions.secretsProviders is the element from which keys will be obtained
    • name is an arbitrary name to be referenced later on jwtKey configuration
    • vault.roleName is the Vault role with read access to the secrets (according to Vault policies configured)
    • vault.path (optional) is the secret's full path in Vault. For example, for keys my-secrets/scg/keys/123... and my-secrets/scg/keys/456..., path mut be my-secrets/scg/keys. If not set, secrets are expected to be under jwt-keys-for-vmware-tanzu/{namespace}-{gateway_name}
  • jwtKey.enabled is the flag indicating that the Vault integration is enabled
  • jwtKey.secretsProviderName is the vault secrets provider name defined previously
apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: mygateway
spec:
  serviceAccount:
    name: scg-service-account

  extensions:
    secretsProviders:
      - name: vault-jwt-keys
        vault:
          roleName: scg-role
    filter:
      jwtKey:
        enabled: true
        secretsProviderName: vault-jwt-keys

Secrets within Vault must follow this structure:

  • secret full name is the path where the secrets are held. Unless path property is set in the secretProvider it must be composed of jwt-keys-for-vmware-tanzu/{namespace}-{gateway_name}/{kid}
  • kid is the key id to uniquely identify the public key. This kid should match the value obtained in the key id location
  • alg is the algorithm used to encrypt the public key (currently supporting RSA only)
  • key is the actual public key, as a PEM format (supporting both CERTIFICATE and PUBLIC KEY formats)
vault kv put jwt-keys-for-vmware-tanzu/customnamespace-mygateway/client_0 \
              kid="client_0" \
              alg="RSA" \
              key="-----BEGIN CERTIFICATE-----\
              MIIBIyEpEBgkqhkiG9w ..."

Note: If you need to add, remove or just update a key in Vault, you can use any of Vault supported methods (HTTP API, CLI...) Every interaction will update the keys in the gateway after no more than 5 minutes.

When defining a RouteConfiguration, you can add the JwtKey filter by including it in the list of filters for the route.

The configuration provided to the JwtKey filter indicates the key id location. This key id location describes whether the key id is found in an HTTP header or in a JWT claim value with the following syntax:

  • - JwtKey={header:X-JWT-KEYID} the key id location is expected to be in an HTTP header named X-JWT-KEYID
  • - JwtKey={claim:keyId} the key id location is expected to be in a JWT claim named keyId
apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - JwtKey={header:X-JWT-KEYID}

Once the gateway is up and running you can see the loaded keys in the info endpoint. Each key is shown with its id and the time when it was last modified.

"jwtkeys": [
  {
    "id": "client_0"
    "lastRefreshTime": "2021-09-07T07:57:01+0000",
  }
]

Limiting User Requests: RateLimit Filter

The RateLimit filter limits the number of requests allowed per route during a time window.

When defining a RouteConfiguration, you can add the RateLimit filter by including it in the list of filters for the route. The filter accepts 3 options:

  • Number of requests accepted during the window.
  • Duration of the window: by default milliseconds, but you can use s, m or h suffix to specify it in seconds, minutes or hours.
  • (Optional) User partition key: it's also possible to apply rate limiting per user, that is, different users can have its own throughput allowed based on an identifier found in the request. Set whether the key is in a JWT claim or HTTP header with '' or '' syntax.

For example, you can add a route to limit all users to one request every 10 seconds:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - RateLimit=1,10s

Provided you are within the allowed limits, the response will succeed and report the number of accepted request you can still do in the X-Remaining HTTP header. When the limit is exceeded, response will fail with 429 Too Many Requests status, and inform the remaining time until a request will be accepted in X-Retry-In HTTP header (in milliseconds)

If you want to expose a route for different sets of users, each one identified by its own client_id HTTP header, use:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - RateLimit=1,10s,{header:client_id}

The rate limit 1,10s will be applied individually for each set of users. When the header (or claim) is not present access will be rejects with a simple 429 Too Many Requests response (without additional headers).

Validate Client Certificate With the ClientCertificateHeader Filter

The ClientCertificateHeader filter validates the client SSL certificate used to make a request to an app through the Gateway. You can also use this filter to validate the client certifcate's fingerprint.

Note: This filter relies on Kubernetes container's ability to recognize a client certificate's Certificate Authority (CA).

When adding a route to a Gateway service instance, you can add the ClientCertificateHeader filter by including it in the list of filters for the applicable route.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - ClientCertificateHeader=*.example.com

To validate the client SSL certificate's fingerprint, add the name of the hash used for the fingerprint, and the fingerprint value, after the CN, using the following format:

[CN],[HASH]:[FINGERPRINT]

where:

  • [CN] is the Common Name
  • [HASH] is the hash used for the fingerprint, either sha-1 or sha-256
  • [FINGERPRINT] is the fingerprint value

The following example uses the ClientCertificateHeader filter to ensure that a client certificate uses a CN of *.example.com and a SHA-1 fingerprint of aa:bb:00:99:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - ClientCertificateHeader=*.example.com,sha-1:aa:bb:00:99

The fingerprint value is not case-sensitive, and the colon character : is not required to separate hexadecimal digits in a fingerprint. The following example works too:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - ClientCertificateHeader=*.example.com,sha-1:AABB0099

Passing JWT Claims Headers: ClaimHeader Filter

The ClaimHeader filter allows passing a JWT claim value as an HTTP Header. It works both with and without SSO enabled, with the consideration that when SSO is not enabled the JWT token is expected in Authorization Header and won't be validated.

This filter is useful in scenarios where the target service does not handle JWT authorization, but still needs some piece of information from the JWT token.

The ClaimHeader filter configuration requires 2 parameters:

  • Claim name: case sensitive name of the claim to pass.
  • Header name: name of the HTTP

The following configurations shows how to extract the claim Subject and pass in an HTTP Header called X-Claim-Sub.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: myapp-route-config
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/api/**
    filters:
      - ClaimHeader=sub,X-Claim-Sub

If you need to pass more than one claim, simply apply the filter repeatedly.

    filters:
      - ClaimHeader=sub,X-Claim-Sub
      - ClaimHeader=iss,X-Claim-Iss
      - ClaimHeader=iat,X-Claim-Iat

Note: In case the header is already present, the value(s) from the claim will be added to it. That is, previous values sent in the SCG request will be preserved.

Basic Authorization with the BasicAuth Filter

The BasicAuth filter relays Basic Authorization credentials to a route. It will not authenticate requests. It will not return a HTTP 401 Unauthorized status line with a WWW-Authenticate header for unauthenticated requests.

To use it, you must first store the basic auth username and password in a Kubernetes secret, with their respective keys, username and password.

This can be done via:

kubectl create secret generic basic-auth-secret --from-literal=username=***** --from-literal=password=*****

The secret must be in the same namespace as the SpringCloudGatewayRouteConfig.

Next, in your SpringCloudGatewayRouteConfig, put the name of the secret you created at spec.basicAuth.secret.

Finally, add the BasicAuth filter to the route.

An example is shown below:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: test-gateway-routes
spec:
  service:
    name: myapp
  basicAuth:
    secret: basic-auth-secret
  routes:
    - predicates:
        - Path=/api/**
      filters:
        - StripPrefix=0
        - BasicAuth

If you have multiple routes, the Basic Auth credentials will only be relayed to the routes that include the BasicAuth filter.

If the secret cannot be found, the RouteConfig will not be created. A Kubernetes event will be emitted in that namespace, like so:

$ kubectl get event
LAST SEEN   TYPE      REASON                      OBJECT                                               MESSAGE
117s        Warning   RoutesDefinitionException   springcloudgatewaymapping/test-gateway-mapping       Failed to retrieve routes from route config in mapping test-gateway-mapping: Failed to find secret 'basic-auth-secret' in the 'user-namespace' namespace.

This will also be logged in the scg-operator pod, which is in the spring-cloud-gateway namespace by default:

$ kubectl logs deployment.apps/scg-operator
2021-06-16 19:38:01.459 ERROR 1 --- [ingController-2] c.v.t.s.route.RoutesDefinitionResolver   : Failed to find secret 'basic-auth-secret' in the 'user-namespace' namespace.

Note: The BasicAuth filter will not work together with the TokenRelay filter as both filters use the Authorization header.

check-circle-line exclamation-circle-line close-line
Scroll to top icon