The open-source Spring Cloud Gateway project includes a number of built-in filters for use in Gateway routes. The following commercial filters provided by VMware Spring Cloud Gateway for Kubernetes can be used in addition to those included in the OSS project.

ApiKey

Note This filter is currently not supported when running the Gateway in standalone mode.

The ApiKey filter validates API keys generated by API Portal for VMware Tanzu. It is expected that every request has a X-API-Key header specified, which the filter validates against the hashed value stored in Hashicorp Vault.

Spring Cloud Gateway can be configured for the ApiKey filter in two ways:

If you are using HashiCorp Vault to store your secrets, you can configure SpringCloudGateway using either approach. If you are using a different provider, you must use the Kubernetes secret approach.

Configuring Spring Cloud Gateway for ApiKey using External Secrets Operator

Using External Secrets Operator (ESO), you can create a Kubernetes secret with all the ApiKey secrets from your secret provider. While it is possible to create and manage the secret manually, ESO automatically updates the Kubernetes secret when the secret provider is updated.

Here is a sample SpringCloudGateway configured to use a Kubernetes secret for the ApiKey filter:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: my-gateway
spec:
  api:
    groupId: accounting
  extensions:
    filters:
      apiKey:
        enabled: true
        secretName: "api-key-secrets"

The Kubernetes secret, api-key-secrets, is created by the scg-operator if it does not exist because the secret is required for the gateway pod to start.

You can configure an ExternalSecret to merge its found secrets to the existing Kubernetes secret using creationPolicy: Merge, as shown below:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: vault-example
spec:
  refreshInterval: "5s"
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: api-key-secrets
    creationPolicy: Merge
    deletionPolicy: Merge
  dataFrom:
    - find:
        conversionStrategy: Default
        decodingStrategy: None
        name:
          regexp: "api-portal-path/accounting/.*"
      rewrite:
      - regexp:
          source: "api-portal-path/accounting/(.*)"
          target: "accounting_$1"
  • deletionPolicy: Merge removes keys but keeps the Kubernetes secret for the gateway to use.
  • The dataFrom spec finds secrets stored under api-portal-path/accounting/* and rewrites the key to accounting_{file-name}.

Note As the Kubernetes secret has a limit of 1 MiB, there is a limit to the number of keys you can have. Assuming a key/value pair is 256 bytes long, you could have 4096 entries.

Configuring Spring Cloud Gateway for ApiKey using Vault

Spring Cloud Gateway for Kubernetes integrates with Vault on Kubernetes, and assumes that a Vault Agent Injector has been deployed to the cluster. The ApiKey filter requires additional Vault integration parameters to be configured in the SpringCloudGateway resource for each Gateway instance that uses it:

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: api-portal-keys
    filters:
      apiKey:
        enabled: true
        secretsProviderName: vault-api-keys

Where:

  • serviceAccount.name is the name of the ServiceAccount used by the Gateway instance.
  • extensions.secretsProviders describes the location from which keys will be obtained:
    • name is the unique name by which the Vault secrets provider can be referenced in the apiKey.secretsProviderName property.
    • vault.roleName is the Vault role with read access to the secrets.
    • 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 default to api-portal-for-vmware-tanzu.
  • filters.apiKey describes the ApiKey filter configuration:
    • enabled is the flag indicating that API key validation on all requests is activated.
    • secretsProviderName is the Vault secrets provider name defined previously.

Configuring HashiCorp Vault

For the example SpringCloudGateway configuration above, to ensure access to the Vault path, you must configure your Hashicorp Vault instance as follows:

  1. Create a Vault access policy to the API Portal path for the Gateway, including your Gateway groupId (see the SpringCloudGateway resource specification for more details).

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

    This sample command uses scg-policy as the policy name, but any name can be chosen here. This name will be required in the next step.

  2. Create a role that binds a namespaced service account to the policy, following the 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 value must be set to the name of the namespace where you create your Spring Cloud Gateway instance, and the bound_service_account_names value must refer to a service account in the same namespace.

Validating the configuration

After applying the configuration above, all routes served by the Gateway instance will require the X-API-Key header in order to be accessed.

To test this using an HTTP client such as httpie or cURL, use the relevant command:

  • htt
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 check that API key management is activated and that keys have been loaded on any Gateway instance, you can visit its /actuator/info endpoint, which should display:

apikey:
  enabled: true
  loaded: true
  headerName: X-Api-Key

Customizing apiKey HTTP header

X-API-Key is the default header used to send the key to the Gateway, but you can change it using the headerName property in the SpringCloudGateway resource.

spec:
  extensions:
    filters:
      apiKey:
        enabled: true
        secretsProviderName: vault-api-keys
        headerName: X-Custom-Header

You can test this configuration with cURL using the new header.

curl -X GET my-gateway.my-example-domain.com/github --header "X-Custom-Header:{my-api-key}"

You can also see the new value reflected in the /actuator/info endpoint.


JwtKey

Note This filter is currently not supported when running the Gateway in standalone mode.

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

The JwtKey secret has the following structure:

  • kid is the key ID to uniquely identify the public key (RSA) or the private key (HMAC). This key ID should match the value obtained from the key ID location.
  • alg is the algorithm used to encrypt the public key (currently supports RSA only) or the private key (HS256, HS384, or HS512).
  • key is the public key in PEM format (supporting both CERTIFICATE and PUBLIC KEY formats), or private key (must be at least 32 bytes in length).

Spring Cloud Gateway can be configured for the JwtKey filter in two ways:

If you are using HashiCorp Vault to store your secrets, you can configure SpringCloudGateway using either approach. If you are using a different provider, you must use the Kubernetes secret approach.

Configuring Spring Cloud Gateway for JwtKey using External Secrets Operator

Using External Secrets Operator (ESO), you can create a Kubernetes secret with all the JWT keys secrets from your secret provider. While it is possible to create and manage the secret manually, ESO will automatically update the Kubernetes secret when the secret provider is updated.

Important The secret must exist before the gateway pod starts. If the secret doesn't exist, the gateway pod errors out looking for the secret.

The ExternalSecret in the example below returns secrets matching the regex jwt-keys/.*. It is recommended that you have a common prefix in your secrets to make filtering easier. Also check that the generated Kubernetes secret includes only the JWT key secrets you intend to share.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: vault-example
spec:
  refreshInterval: "15s"
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: jwt-key-secrets
  dataFrom:
    - find:
        name:
          regexp: "jwt-keys/.*"

If you stored a secret in your secret provider as:

{"alg":"HS256","key":"my-super-secure-hs256-secret-key","kid":"client_3"}

Then the Kubernetes secret created by ESO, jwt-key-secrets, should look similar like this:

apiVersion: v1
kind: Secret
metadata:
  name: jwt-key-secrets
data:
  jwt-keys_client_3: eyJhbGciOiJIUzI1NiIsImtleSI6ImFub3RoZXItdWx0cmEtc2VjdXJlLXNlY3JldC0zMzMzIiwia2lkIjoiY2xpZW50XzMifQ==

You can then use the Kubernetes secret jwt-key-secrets in your SpringCloudGateway resource:

apiVersion: "tanzu.vmware.com/v1"
kind: SpringCloudGateway
metadata:
  name: mygateway
spec:
  extensions:
    filters:
      jwtKey:
        enabled: true
        secretName: jwt-key-secrets

Where:

  • filters.jwtKey describes the JwtKey filter configuration:
    • enabled indicates that the JWT key validation on all requests is activated
    • secretName is the name of the Kubernetes secret with all the JwtKey secrets

Note As the Kubernetes secret has a limit of 1 MiB, there is a limit to the number of keys you can have. Assuming 128 bytes for an HS256 key/value pair, you could have at most 8192 entries. The RSA algorithm uses more bytes per key/value pair.

Configuring Spring Cloud Gateway for JwtKey using Vault

Spring Cloud Gateway for Kubernetes integrates with Vault on Kubernetes, and assumes that a Vault Agent Injector has been deployed to the cluster. The JwtKey filter requires additional Vault integration parameters to be configured in the SpringCloudGateway resource for each Gateway instance that uses it:

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

Where:

  • serviceAccount.name is the name of the ServiceAccount used by the gateway instances
  • extensions.secretsProviders describes the location from which keys will be obtained:
    • name is the unique name by which the Vault secrets provider can be referenced in the jwtKey.secretsProviderName property.
    • vault.roleName is the Vault role with read access to the secrets.
    • vault.path (optional) is the full path to the secrets in Vault. For example, for keys my-secrets/scg/keys/123... and my-secrets/scg/keys/456..., the vault.path setting would be my-secrets/scg/keys. If not set, the value will default to jwt-keys-for-vmware-tanzu/{namespace}-{gateway_name}
    • vault.authPath (optional) is the authentication path for Vault's Kubernetes auth method. For example, /auth/cluster-1-auth, /auth/cluster-2-auth.
  • filters.jwtKey describes the JwtKey filter configuration:
    • enabled is the flag indicating that the JWT key validation on all requests is activated
    • secretsProviderName is the Vault secrets provider name defined previously

Configuring routes

When defining the routes to be protected in a SpringCloudGatewayRouteConfig, 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 JWT header, 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 JWT header named kid

For example:

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}

Configuring HashiCorp Vault

In addition to the JWT key structure above, the secrets in Vault must be stored in a specific location. Unless the path property is set in the secretsProvider, it must be composed of jwt-keys-for-vmware-tanzu/{namespace}-{gateway_name}/{kid}.

RSA algorithm:

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

HMAC algorithm

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 update a key in Vault, you can use any of Vault's supported methods, such as the HTTP API or CLI. Every interaction updates the keys in the Gateway after no more than 5 minutes.

Validating the configuration

After the Gateway is up and running, you can see the loaded keys in its /actuator/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"
  }
]

Roles

The Roles filter uses custom JWT claim properties to apply role-based access control (RBAC) to routes.

If you are not using the Single Sign-On feature, you can still use role-based control by applying the 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, Spring Cloud Gateway for Kubernetes checks for role values under the roles claim, but you can change this by setting the spec.sso.roles-attribute-name property in your SpringCloudGateway resource.

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

In addition to supporting simple claim names, spec.sso.roles-attribute-name supports nested JSON values, such as custom-data.user.roles.

Spring Cloud Gateway expects the roles claim (or custom claim specified in spec.sso.roles-attribute-name) to contain an array (e.g. roles = ["user-role-1", "user-role-2"]), but a single string is also accepted when roles contains only one value (for example, roles = "user-role").


Scopes

When Single Sign-On is activated, you can fine-tune route access control based on OpenID Connect 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

SsoAutoAuthorize

Caution This filter must be applied for development purposes only. It accepts a list of roles or scopes (separated by commas) to inject into a fake SSO authorization.

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

This filter is intended to be a convenience to aid development only, so additional configuration is required to reduce the risk that it is accidentally activated in 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"

If no SSO configuration is present, you must create a placeholder configuration that activates an SSO profile and sets 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

A Token Relay involves an OAuth2 or OpenID Connect consumer acting as a client, and forwarding the incoming token to outgoing resource requests. In the case of Spring Cloud Gateway, activating TokenRelay on a route instructs the Gateway to forward the currently authenticated user's identity token (ID Token) to the downstream routed service.

The client app and the web browser send auth details through SGG for K8x to the API and OIDC provider.

Important TokenRelay is set using boolean fields spec.service.tokenRelay and/or spec.routes.tokenRelay. If you add it to the filters array it is ignored. This helps ensure that these are applied to the Gateway in the correct order.

To do this, use:

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

Spring Cloud Gateway updates the token and internal SCG session automatically using the Refresh Token sent by the Identity Provider. Review your Provider documentation to see how to enable refresh for users.

If no Refresh Token is provided, users are asked to authenticate again using the Authorization Flow. The later re-authentication happens automatically if the user session is still active in the Identity Provider. Users may report seeing the page reload.

Important You cannot use the TokenRelay filter with the BasicAuth filter, because both filters use the Authorization header.

TokenRelay in standalone mode

The token relay feature is supported when running Spring Cloud Gateway in standalone mode outside of Kubernetes. See TokenRelay Standalone Configuration

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