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.

AddRequestHeadersIfNotPresent: Request headers modification filter

This filter adds certain request headers if those are not present in the original request. It accepts a list of key value pairs.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
  routes:
  - predicates:
      - Path=/foo
    filters:
      - AddRequestHeadersIfNotPresent=Content-Type:application/json,Connection:keep-alive

In the example, a raw request to /foo will have the headers Content-Type: application/json and Connection: keep-alive included into the original request.

In case the request comes with:

  • Content-Type: only Connection: keep-alive will be added.
  • Connection: only Content-Type: application/json will be added.
  • both Content-Type and Connection: the original request will be left untouched.

This filter provides a convenient method to set maximum number of allowed cookies on a request. It accepts up to the maximum number of cookies integer value and will respond with a 431 Request Header Fields Too Large error if exceeded.

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

In the example, request will proceed if it has 2 cookies or less.


AllowedRequestHeadersCount: Allowed request headers count filter

This filter provides a convenient method to set the maximum allowed headers in the request coming from our target service through the gateway. It accepts a integer value for the maximum number of headers and if it is exceeded it will respond with a 431 Request header fields too large.

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

In the example, request will proceed if it has 4 or fewer headers, including cookies.


AllowedRequestQueryParamsCount: Allowed request query params count filter

This filter provides a convenient method to set a maximum allowed query parameters of the request coming from target service through the gateway. It accepts a number of maximum query parameters and it's exceeded, it will respond with a 414 URL Too Large HTTP error.

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

In the example, request will proceed if it has 3 query parameters or less.


BasicAuth: Basic authorization 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.

BasicAuth filter in Standalone Gateway

When using the filter in a Standalone instance you must add the credentials alongside the filter in the form username:password. The example below shows the configuration when username and password are respectively my-username and `my-password``

spring:
  cloud:
    gateway:
      routes:
      - id: basic-auth-relay
        uri: https://example.org
        filters:
        - BasicAuth=bXktdXNlcm5hbWU6bXktcGFzc3dvcmQ=

BlockAccess: Global Filter to block access

This is a Global Filter that provides the ability to block access by ip/domain or JWT claims that apply to all existing routes and requests. As it works globally, it must be activated and composed using configuration properties.

  • spring.cloud.gateway.k8s.block.access.enabled must be set to true to enable this filter

There are three configuration properties to setup the possible blocks:

  • By IP/domain:

    • spring.cloud.gateway.k8s.block.access.domains: it accepts a list of IPs or domains separated by commas and will block any request coming from the configured values.
  • By JWT claims:

    • spring.cloud.gateway.k8s.block.access.claimValues: it accepts a list of claim values separated by commas, it will search for the specified values in the JWT Claims and will block any authenticated request with any of the configured claim values.
    • spring.cloud.gateway.k8s.block.access.claimNames: is a complementary property to the previous one, it accepts a list of claim names separated by commas and it will search for the specified values in the claimValues property in the specified claim names in this property. It will block any authenticated request with any of the configured claim values.

Example using only claimValues property:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: my-gateway
spec:
  env:
    - name: spring.cloud.gateway.k8s.block.access.enabled
      value: "true"
    - name: spring.cloud.gateway.k8s.block.access.domains
      value: "192.168.0.1,test.com"
    - name: spring.cloud.gateway.k8s.block.access.claimValues
      value: "client.write,cc_testuser"

Will block access if the request comes from test.com or the IP 192.168.0.1, it also will block access if any of the JWT claims contains client.write or cc_testuser values.

Example using claimValues and claimNames properties:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: my-gateway
spec:
  env:
    - name: spring.cloud.gateway.k8s.block.access.enabled
      value: "true"
    - name: spring.cloud.gateway.k8s.block.access.domains
      value: "test.com"
    - name: spring.cloud.gateway.k8s.block.access.claimNames
      value: "sub"
    - name: spring.cloud.gateway.k8s.block.access.claimValues
      value: "write,cc_testuser"

Will block access if the request comes from test.com and it also will block access if the JWT claims sub contains write or cc_testuser values.

Note: The JWT Claim Block Access global filter only supports the block on API calls with the authentication header, it doesn't support blocking by cookie session.


CircuitBreaker: Reroute traffic on error response 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

Circuit breaker status

By querying for the circuit breaker metrics, you can monitor the status of the circuit breaker:

actuator/metrics/resilience4j.circuitbreaker.state?tag=state:{circuit-breaker-state}&tag=name:{circuit-breaker-name}
  • where {circuit-breaker-state} is one of closed, disabled, half_open, forced_open, open, metrics_only
  • where {circuit-breaker-name} is the name of your circuit breaker, e.g. myCircuitBreaker

The metrics endpoint will return a value of 1 in the $.measurements[].value JSON path if the circuit breaker is in this state.

For more more information and other metrics, see Resilience4j CircuitBreaker Metrics.


ClaimHeader: Passing JWT claims header 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.


ClientCertificateHeader: Validate client certificate 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

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

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

Cors: Configuring per-route Cross-Origin Resource Sharing (CORS) behavior

You can define CORS behavior on a route with the Cors filter, instead of configuring it on the gateway.

In this example, the allowedOrigins is set to https://example.com, and the allowedMethods are GET, POST, DELETE.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  routes:
    - uri: https://httpbin.org
      predicates:
        - Path=/get/**
      filters:
        - Cors=[allowedOrigins:https://example.com,allowedMethods:GET;POST;DELETE]

The following table describes the parameters you can configure on the filter:

Parameter Function Example
allowedOrigins Allowed origins to make cross-site requests. The special value "*" allows all domains. These values will be combined with the values from allowedOriginPatterns. Cors=[allowedOrigins:https://example.com]
allowedOriginPatterns Alternative to allowedOrigins that supports more flexible origins patterns with "*" anywhere in the host name in addition to port lists. These values will be combined with the values from allowedOrigins. Cors=[allowedOriginPatterns:https://*.test.com:8080]
allowedMethods Allowed HTTP methods on cross-site requests. The special value "*" allows all methods. If not set, "GET" and "HEAD" are allowed by default. Cors=[allowedMethods:GET;PUT;POST]
allowedHeaders Allowed headers in cross-site requests. The special value "*" allows actual requests to send any header. Cors=[allowedHeaders:X-Custom-Header]
maxAge How long, in seconds, the response from a pre-flight request can be cached by clients. Cors=[maxAge:300]
allowCredentials Whether user credentials are supported on cross-site requests. Valid values: `true`, `false`. Cors=[allowCredentials:true]
exposedHeaders HTTP response headers to expose for cross-site requests. Cors=[exposedHeaders:X-Custom-Header]

GemfireResponseCache: Configuring per-route Gemfire response cache

This filter allows to override global cache configuration at route level, so same considerations must be taken into account when enabling this feature:

  • It only handles bodyless GET requests.
  • It only caches the response as long has one of the following status codes: HTTP 200 (OK), HTTP 206 (Partial Content) and HTTP 301 (Moved Permanently).
  • Response data will not be cached if Cache-Control header doesn't allow it (no-store in the request, no-store or private in the response).
  • If response is already cached and a new request is performed with no-cache value in Cache-Control header, it will return a bodyless response with 304 (Not Modified).

Take into account that this filter to configure Gemfire response cache per route only will be available if gemfire response global cache is enabled.

It only accepts one parameter specifying the name of the Gemfire region that will handle the response cache for this route, the region details must be already configured on Gemfire in order to work.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  routes:
    - uri: https://httpbin.org
      predicates:
        - Path=/get/**
      filters:
        - GemfireResponseCache=my-route-region

GemfireResponseCache filter in Standalone Gateway

The GemfireResponseCache filter can also be used in Standalone gateway to support global or shared caches across routes. Once a region is setup, is can be used by as many routes as you need by specifying the name in the filter.

The example shows a configuration in which we have shared-region used by two routes, and my-route-region used only by the last one.

spring:
  cloud:
    gateway:
      routes:
      - id: route-1
        uri: https://example.org
        predicates:
        - Path=/one/**
        filters:
		- GemfireResponseCache=shared-region
	  - id: route-2
        uri: https://example.org
		predicates:
        - Path=/two/**
        filters:
		- GemfireResponseCache=shared-region
	  - id: route-3
        uri: https://example.org
        predicates:
        - Path=/three/**
        filters:
		- GemfireResponseCache=my-route-region

When using this model, keep in mind that the cache(s) won't distinguish across routes. That means that high traffic routes may impact caching capacity for routes less used.


JwtKey: Multiple client JWT validation filter

Note: This filter is not currently supported when running as Standalone Gateway.

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 must be my-secrets/scg/keys.
    • vault.authPath (optional) is the authentication path for Vault's Kubernetes auth method. For example, /auth/cluster-1-auth, /auth/cluster-2-auth. 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
    filters:
      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 (RSA) or the private key (HMAC). 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) or the private key (HS256, HS384, or HS512)
  • key is the actual public key, as a PEM format (supporting both CERTIFICATE and PUBLIC KEY formats), or private key with at least 32 bytes in length

RSA:

vault kv put jwt-keys-for-vmware-tanzu/customnamespace-mygateway/client_0 \
              kid="client_0" \
              alg="RSA" \
              key="-----BEGIN CERTIFICATE-----\
              MIIBIyEpEBgkqhkiG9w ..."

HMAC:

vault kv put jwt-keys-for-vmware-tanzu/customnamespace-mygateway/client_0 \
              kid="client_0" \
              alg="HS256" \
              key="Key-Must-Be-at-least-32-bytes-in-length!"

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 or header 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:kid} the key id location is expected to be in a JWT claim or header named kid
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",
  }
]

LocalResponse: Configuring per-route local response cache

This filter allows to override global cache configuration at route level, so same considerations must be taken into account when enabling this feature:

  • It only handles bodyless GET requests.
  • It only caches the response as long has one of the following status codes: HTTP 200 (OK), HTTP 206 (Partial Content) and HTTP 301 (Moved Permanently).
  • Response data will not be cached if Cache-Control header doesn't allow it (no-store in the request, no-store or private in the response).
  • If response is already cached and a new request is performed with no-cache value in Cache-Control header, it will return a bodyless response with 304 (Not Modified).

Take into account that this filter to configure local response cache per route only will be available if local response global cache is enabled.

It accepts a first parameter to override the time to expire a cache entry expressed in s for seconds, m for minutes and h for hours, and a second parameter to override the maximum size of cache to evict entries for this route, it accepts size format in KB, MB and GB.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  routes:
    - uri: https://httpbin.org
      predicates:
        - Path=/get/**
      filters:
        - LocalResponseCache=3m,1MB

This filter also implements the automatic calculation of the max-age value in the HTTP Cache-Control header. Only if "max-age" is present on the original response the value will be rewritten with the number of seconds set in the timeToLive configuration parameter; and in consecutive calls this value will be recalculated with the number of seconds left until the response expires.


ApiKey: API key validation filter

Note: This filter is not currently supported when running as Standalone Gateway.

The ApiKey filter allows validating API keys generated by API portal for VMware Tanzu 1.1.0. It is expected that every request has a X-API-Key header specified that allows the filter to validate against the hashed value stored in Hashicorp Vault.

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 apiKey.enabled alongside apiKey.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 apiKey configuration
    • vault.roleName is the Vault role with read access to the secrets (according to Vault policies configured)
    • vault.path (optional) is the same vault path you configured when setting up API key management in API portal. If not set, the value will be api-portal-for-vmware-tanzu by default
  • apiKey.enabled is the flag indicating that the API key validation on all requests is enabled
  • apiKey.secretsProviderName is the vault secrets provider name defined previously
apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: mygateway
spec:
  api:
    groupId: my-group-id

  serviceAccount:
    name: scg-service-account

  extensions:
    secretsProviders:
      - name: vault-api-keys
        vault:
          roleName: api-key-role
          path: my-api-portal
    filters:
      apiKey:
        enabled: true
        secretsProviderName: vault-api-keys

For the example configuration above, to ensure access to the vault path, you need to configure your Hashicorp Vault instance:

  1. Create a Vault access policy to API portal path for the Gateway, including your gateway groupId (see the configuring instances section for more details)

    $ vault policy write scg-policy - <<EOF
      path "my-api-portal/data/my-group-id" {
        capabilities = ["read"]
      }
      path "my-api-portal/metadata/my-group-id" {
        capabilities = ["list"]
      }
      EOF
    

    The sample command above uses scg-policy as the name. You may use a different name for the policy, just make sure you use the same policy name in next step.

  2. Create a role that binds a namespaced service account to that policy, following Kubernetes Auth Method:

    $ vault write auth/kubernetes/role/api-key-role \
      bound_service_account_names=scg-service-account \
      bound_service_account_namespaces=scg-namespace \
      policies=scg-policy \
      ttl=24h
    

    The bound_service_account_namespaces above needs to be the namespace where you install your Spring Cloud Gateway instance, and the bound_service_account_names needs to refer to a service account in the same namespace.

After applying the configuration, all routes defined in the SpringCloudGatewayRouteConfig will require the X-API-Key header to be accessed.

For example using an HTTP client such as HTTP or cURL:

  $ http GET my-gateway.my-example-domain.com/github X-API-Key:{my-api-key}
  $ curl -X GET my-gateway.my-example-domain.com/github --header "X-API-key:{my-api-key}"

If you want to double-check that API key management is enabled and that keys have been loaded, you can visit actuator/info endpoint which should display:

apikey:
   enabled:	true
   loaded:	true

RateLimit: Limiting user requests 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 4 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.
  • (Optional) It is possible to rate limit by IP addresses. Note, this cannot be combined with the rate limiting per user.

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).

Limiting by IP Address

Rate limiting by IP address can accept a multiple IP addresses, seperated by a semi-colon.

When rate limiting by IP address, the filter checks the X-Forwarded-For header, if present, for the IP. As there can be multiple IPs added to this header, you can optionally set the max trusted index to read from, by setting this as the first value.

The default value of 1 will read the last IP from the header, while a value of 2 will read the second last IP, and so on. The index must be greater than zero.

Here is an example to rate limit by IP address:

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

In the example above, the max trusted index is set to 2. If the X-Forwarded-For header had a value of 4.4.4.4, 8.8.8.8, 127.0.0.1, the gateway would return 403 forbidden because the second-last IP, 8.8.8.8, is not in the allowed IPs. However, if the header was set to 4.4.4.4, 127.0.0.1, 8.8.8.8, the gateway will return successfully.

Note: If you are using an ingress, ensure it is configured to pass the incoming X-Forwarded-For header upstream to the gateway.


RemoveJsonAttributesResponseBody: Response body modification 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 foo will be deleted from the JSON content body at any level.


RewriteAllResponseHeaders Response headers modification 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=\d,0

In the example, any header value containing a number (\d matches any number from 0 to 9) will be replaced by 0.


RewriteResponseBody: Response body modification 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/

RewriteJsonAttributesResponseBody: Response body JSON modification filter

This filter provides a convenient method to apply a transformation to JSON content from target service through the gateway using JSON Path notations. It accepts a list of elements (separated by commas) where the first parameter is the selector of the JSON node and the second one is the value to set into that JSON node, those two parameters must be 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:
      -  RewriteJsonAttributesResponseBody=slides[1].title:Welcome,date:11-11-2022

Given the following JSON in a body content:

{
   "date":"01-01-2022 11:00",
   "slides":[
      {
         "title":"Presentation",
         "type":"all"
      },
      {
         "title":"Overview",
         "type":"image"
      }
   ],
   "title":"Sample Title"
}

Applying the example:

  • date at root level will be replaced by 11-11-2022
  • title at second element of the slides array will be replaced by Welcome
{
   "date":"11-11-2022",
   "slides":[
      {
         "title":"Presentation",
         "type":"all"
      },
      {
         "title":"Welcome",
         "type":"image"
      }
   ],
   "title":"Sample Title"
}

Roles: Role-based access control 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

Scopes: Scope-based access control 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

StoreIpAddress: Store IP address filter

This filter provides a convenient method to store the IP address of the request coming from target service through the gateway, it can be useful for tracing purposes. It accepts a parameter name under which to store the IP.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
    filters:
      - StoreIpAddress=ipAddress

In the example, will store the IP address in the context of the application under ipAddress attribute. Attributes can be pulled implementing a custom extension:

((exchange, chain) -> {
    String attribute = exchange.getAttributeOrDefault("ipAddress", "Attribute not found");
    ...
    return chain.filter(exchange);
});

StoreHeader Store headers filter

This filter provides a convenient method to populate a header value into the context of the application coming from target service through the gateway, it can be useful for tracing purposes. It accepts a list of header names to search for and a last parameter with the attribute name under which want to store the header value. It's important to highlight that the list of header names must be in order of priority, once it finds one header, it stops looking for the rest and includes it in the context of the application under the last parameter received.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
    filters:
      - StoreHeader=x-tracing-header,custom-id,x-custom-id,tracingParam

In the example, will search for x-tracing-header, custom-id and x-custom-id and once it finds one, it will store its value on the application context under tracingParam attribute. Attributes can be pulled implementing a custom extension:

((exchange, chain) -> {
    List<String> attributes = exchange.getAttributeOrDefault("tracingParam", Collections.emptyList());
    ...
    return chain.filter(exchange);
});

SsoAutoAuthorize: SSO auto-authorized credentials filter

This filter must be applied only for development purposes, it accepts a list of roles or scopes (separated by commas) to inject a fake SSO authorization with those authorities associated.

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGatewayRouteConfig
metadata:
  name: my-gateway-routes
spec:
  service:
    name: myapp
    filters:
      - SsoAutoAuthorize=SCOPE_test,ROLE_test

Additional configuration is required to reduce the change that this local development utility is not deployed to production environments:

  • System property (JAVA_OPTS property) com.vmware.tanzu.springcloudgateway.dev.mode.enabled must be set to true.
  • Configuration property com.vmware.tanzu.springcloudgateway.sso.auto.authorize.enabled must be set to true.
apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: my-gateway
spec:
  env:
    - name: com.vmware.tanzu.springcloudgateway.sso.auto.authorize.enabled
      value: "true"
  java-opts: "-Dcom.vmware.tanzu.springcloudgateway.dev.mode.enabled=true"

Note: If no SSO configuration is present, you will need to create a dummy configuration activating an SSO profile and setting a valid issuer uri, for example the Google Issuer URL (https://accounts.google.com).

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: my-gateway
spec:
  env:
    - name: spring.profiles.include
      value: "sso"
    - name: spring.security.oauth2.client.provider.sso.issuer-uri
      value: "https://accounts.google.com"
    - name: com.vmware.tanzu.springcloudgateway.sso.auto.authorize.enabled
      value: "true"
  java-opts: "-Dcom.vmware.tanzu.springcloudgateway.dev.mode.enabled=true"

TokenRelay: Passing user identity 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.

TokenRelay filter in Standalone Gateway

The filter works the same in Standalone as in Kubernetes. However, given the configuration is not handled automatically, there are some considerations:

  • Configuration requires adding the filter named TokenRelay to the route's filters list.
  • If used in conjunction with OIDC SSO, the TokenRelay filter must be placed after SsoLogin filter.
spring:
  cloud:
    gateway:
      routes:
      - id: token-relay
        uri: https://example.org
        filters:
		- SsoLogin
        - TokenRelay

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