eg-auth

SKILL.md

Configure authentication and authorization for Envoy Gateway using the SecurityPolicy CRD. SecurityPolicy attaches to a Gateway (applies to all routes) or a specific HTTPRoute/GRPCRoute. When a SecurityPolicy targets both a Gateway and a Route, the Route-level policy takes precedence.

Instructions

Step 1: Choose the target reference

Determine whether authentication applies at the Gateway level (all routes) or to a specific HTTPRoute.

For Gateway-level targeting (default):

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: <policy-name>  # TODO: Replace with a descriptive name
  namespace: <namespace>  # Must match the target resource namespace
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: <gateway-name>  # TODO: Replace with your Gateway name

For HTTPRoute-level targeting:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: <policy-name>
  namespace: <namespace>
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace with your HTTPRoute name

You can also use targetSelectors to match resources by label instead of name:

spec:
  targetSelectors:
    - kind: HTTPRoute
      group: gateway.networking.k8s.io
      matchLabels:
        app: my-protected-app

Step 2: Configure the authentication method

Select one of the following authentication methods based on the Method argument.


JWT Authentication (Method: jwt)

Validates JSON Web Tokens using remote or local JWKS. Best for service-to-service and API authentication.

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: jwt-auth
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  jwt:
    providers:
      - name: my-provider  # TODO: Name this provider (referenced in authorization rules)
        issuer: "https://issuer.example.com"  # TODO: Replace with your token issuer URL
        remoteJWKS:
          uri: "https://issuer.example.com/.well-known/jwks.json"  # TODO: Replace with your JWKS endpoint

Using local JWKS (stored in a ConfigMap instead of fetched remotely):

  jwt:
    providers:
      - name: my-provider
        localJWKS:
          type: ValueRef
          valueRef:
            group: ""
            kind: ConfigMap
            name: jwt-local-jwks  # TODO: Create this ConfigMap with a "jwks" key

Extract claims to headers (forward JWT claims to the backend):

  jwt:
    providers:
      - name: my-provider
        issuer: "https://issuer.example.com"
        remoteJWKS:
          uri: "https://issuer.example.com/.well-known/jwks.json"
        extractFrom:
          headers:
            - name: Authorization
              valuePrefix: "Bearer "
          # Also extract from cookies if needed:
          # cookies:
          #   - access-token

Claims-based authorization (require specific claim values):

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: jwt-claim-auth
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  jwt:
    providers:
      - name: my-provider
        issuer: "https://issuer.example.com"  # TODO: Replace
        remoteJWKS:
          uri: "https://issuer.example.com/.well-known/jwks.json"  # TODO: Replace
  authorization:
    defaultAction: Deny
    rules:
      - name: allow-admins
        action: Allow
        principal:
          jwt:
            provider: my-provider  # Must match the provider name above
            scopes: ["read", "write"]  # TODO: Replace with required scopes
            claims:
              - name: user.roles  # TODO: Replace with the claim path to check
                valueType: StringArray
                values: ["admin"]  # TODO: Replace with required claim values

OIDC Authentication (Method: oidc)

Redirects unauthenticated users to an OpenID Connect provider for interactive login. Best for web applications with browser-based users. Requires HTTPS on the Gateway listener.

First, create a Secret containing your OIDC client secret:

apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: oidc-client-secret  # TODO: Choose a name
  namespace: <namespace>
stringData:
  client-secret: "<your-client-secret>"  # TODO: Replace with your OIDC client secret

Then create the SecurityPolicy:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: oidc-auth
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  oidc:
    provider:
      issuer: "https://accounts.google.com"  # TODO: Replace with your OIDC provider issuer
    clientID: "<your-client-id>"  # TODO: Replace with your OIDC client ID
    clientSecret:
      name: oidc-client-secret  # Must match the Secret name above
    # redirectURL and logoutPath MUST match the target HTTPRoute's host and path prefix
    redirectURL: "https://www.example.com/myapp/oauth2/callback"  # TODO: Replace
    logoutPath: "/myapp/logout"  # TODO: Replace
    scopes:
      - openid
      - profile
      - email  # TODO: Adjust scopes as needed
    # Forward the access token to the backend via Authorization header:
    # forwardAccessToken: true
    # Share cookies across subdomains:
    # cookieDomain: "example.com"

Important: The redirectURL must be prefixed with the target HTTPRoute's host and path. The logoutPath must be prefixed with the HTTPRoute's path prefix. Register this redirect URL with your OIDC provider.


API Key Authentication (Method: apikey)

Validates requests against API keys stored in Kubernetes Secrets. Good for machine-to-machine API access.

First, create an Opaque Secret containing valid API keys (key = client ID, value = API key):

apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: apikey-secret  # TODO: Choose a name
  namespace: <namespace>
stringData:
  client1: "supersecret-key-1"  # TODO: Replace with actual API keys
  client2: "supersecret-key-2"
  # NOTE: Do NOT include "Bearer " prefix in the values even if clients send
  # "Authorization: Bearer <key>". Envoy strips the prefix automatically.

Then create the SecurityPolicy:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: apikey-auth
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  apiKeyAuth:
    credentialRefs:
      - group: ""
        kind: Secret
        name: apikey-secret  # Must match the Secret name above
    extractFrom:
      # Extract API key from a custom header:
      - headers:
          - x-api-key  # TODO: Change the header name if needed
      # Or extract from a query parameter:
      # - params:
      #     - api_key
      # Or extract from the Authorization header (Bearer scheme):
      # - headers:
      #     - Authorization

External Authorization (Method: extauth)

Delegates auth decisions to an external HTTP or gRPC service. Use ExtAuth when your authorization logic requires database lookups, custom business rules, or integration with systems not natively supported by Envoy Gateway.

HTTP ExtAuth:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: ext-auth-http
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  extAuth:
    http:
      backendRefs:
        - name: http-ext-auth  # TODO: Replace with your auth service name
          port: 9002  # TODO: Replace with your auth service port
      headersToBackend:
        - x-current-user  # Headers from auth response to forward to the backend
    # Optional: send specific headers to the auth service
    # By default HTTP ExtAuth only receives: Host, Method, Path, Content-Length, Authorization
    headersToExtAuth:
      - x-custom-header  # TODO: Add headers your auth service needs
    # failOpen: false  # Set to true to allow traffic if the auth service is unavailable
    # timeout: 10s  # Timeout for auth service requests (default: 10s)

gRPC ExtAuth:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: ext-auth-grpc
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  extAuth:
    grpc:
      backendRefs:
        - name: grpc-ext-auth  # TODO: Replace with your gRPC auth service name
          port: 9002  # TODO: Replace with your gRPC auth service port
    # gRPC services receive ALL request headers by default (unlike HTTP)
    # failOpen: false
    # timeout: 10s

When to use ExtAuth vs. built-in methods:

  • Use JWT/OIDC/API Key when the built-in methods cover your requirements -- they are simpler, faster, and need no external service.
  • Use ExtAuth when you need custom logic, database lookups, or integration with authorization systems like OPA, Casbin, or proprietary services.

Basic Auth (Method: basic)

WARNING (EGTM-023): Basic authentication uses SHA1 hashing and does not enforce password complexity. It is recommended to use JWT or OIDC instead. If you must use Basic Auth, always pair it with TLS to prevent credentials from being transmitted in plain text.

First, generate an htpasswd file and create a Secret:

# Generate .htpasswd file (SHA algorithm)
htpasswd -cbs .htpasswd user1 password1
htpasswd -bs .htpasswd user2 password2

# Create the Kubernetes Secret
kubectl create secret generic basic-auth --from-file=.htpasswd -n <namespace>

Then create the SecurityPolicy:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: basic-auth
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  basicAuth:
    users:
      name: basic-auth  # Must match the Secret name above
    # forwardUsernameHeader: x-username  # Optional: forward authenticated username to backend

Step 3: Configure authorization rules (optional)

Authorization rules work independently or alongside any authentication method. They control which authenticated requests are allowed or denied.

IP-based allow/deny lists:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: ip-allow-list
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  authorization:
    defaultAction: Deny
    rules:
      - name: allow-internal
        action: Allow
        principal:
          clientCIDRs:
            - 10.0.0.0/8  # TODO: Replace with your allowed CIDR ranges
            - 192.168.0.0/16

JWT claim-based authorization (combine with JWT authentication):

  authorization:
    defaultAction: Deny
    rules:
      - name: allow-by-claim
        action: Allow
        principal:
          jwt:
            provider: my-provider  # Must match a JWT provider name
            claims:
              - name: groups
                valueType: StringArray
                values: ["engineering", "platform"]

Step 4: Configure CORS (optional)

CORS can be configured in the same SecurityPolicy alongside authentication.

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: cors-and-auth
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <route-name>  # TODO: Replace
  cors:
    allowOrigins:
      - "https://app.example.com"  # TODO: Replace with your allowed origins
      - "https://*.example.com"  # Wildcard subdomain support
    allowMethods:
      - GET
      - POST
      - PUT
      - DELETE
    allowHeaders:
      - Authorization
      - Content-Type
    exposeHeaders:
      - X-Request-Id
    maxAge: 86400s  # Cache preflight for 24 hours
    # allowCredentials: true  # Enable if cookies/auth headers needed cross-origin
  jwt:
    providers:
      - name: my-provider
        issuer: "https://issuer.example.com"
        remoteJWKS:
          uri: "https://issuer.example.com/.well-known/jwks.json"

Step 5: Apply and verify

kubectl apply -f security-policy.yaml

# Verify the SecurityPolicy status
kubectl get securitypolicy/<policy-name> -o yaml

# Check that conditions show Accepted: True
kubectl get securitypolicy/<policy-name> -o jsonpath='{.status.conditions}'

Checklist

  • SecurityPolicy targets the correct resource (Gateway or HTTPRoute) in the same namespace
  • For JWT: issuer URL and JWKS URI are correct and reachable from the cluster
  • For OIDC: redirectURL and logoutPath match the target HTTPRoute host and path prefix
  • For OIDC: client secret is stored in an Opaque Secret with key "client-secret"
  • For OIDC: HTTPS listener is configured on the Gateway (OIDC requires TLS)
  • For API Key: Secret values do NOT include "Bearer " prefix
  • For ExtAuth: auth service is deployed and reachable within the cluster
  • For Basic Auth: TLS is enabled (EGTM-023 -- never use Basic Auth over plain HTTP)
  • For Basic Auth: consider migrating to JWT/OIDC for stronger authentication
  • CORS allowOrigins are restricted to known domains (avoid wildcard "*" in production)
  • Authorization defaultAction is set to Deny when using allow-list rules
  • Route-level SecurityPolicy is aware that it overrides any Gateway-level SecurityPolicy
  • SecurityPolicy status shows Accepted: True with no error conditions
Weekly Installs
1
First Seen
5 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1