About
A SAML 2.0 Service Provider (SP) HTTP filter that adds browser-based single sign-on authentication to any service behind Envoy Proxy.
Features
- SP-Initiated SSO: Redirects unauthenticated users to your Identity Provider
- Assertion Validation: Full SAML Response signature verification and condition checks
- Session Management: HMAC-signed cookies with configurable expiration
- Attribute Forwarding: Maps SAML attributes to upstream request headers
- SP Metadata Endpoint: Auto-generated metadata XML for easy IdP registration
- Signed AuthnRequests: Optional RSA-SHA256 signing of outbound authentication requests
- Auto-Generated SP Certificates: Ephemeral keypair generated when no certificate is provided
- IdP Metadata Fetching: Optionally load IdP metadata from a URL at config time via Envoy HttpCallout
How It Works
The filter intercepts incoming HTTP requests and checks for a valid session cookie. If no session exists, the browser is redirected to the configured IdP with a SAML AuthnRequest. After authentication, the IdP POSTs a SAMLResponse to the Assertion Consumer Service (ACS) endpoint. The filter validates the assertion, creates a signed session cookie, and redirects the user to their original URL. Subsequent requests include the session cookie and pass through with identity headers set.
Configuration
Required fields:
entity_id: The SP entity ID (usually, audience URI)acs_path: Path for the Assertion Consumer Service endpoint- One of (mutually exclusive):
idp_metadata_xml: IdP metadata XML, as{"inline": "<xml>..."}or{"file": "/path/to/metadata.xml"}. Parsed at config-load time.idp_metadata_url+idp_metadata_cluster: Fetch the IdP metadata XML from the given URL via the named Envoy cluster. The fetch is scheduled to run afteridp_metadata_fetch_delay(default1s), giving Envoy time to bring up the cluster — issuing the callout immediately at config-load time often races with cluster warm-up and fails. While the fetch is pending or in flight, requests receive503 Service Unavailablewith detailssaml-metadata-pending. Once the metadata is loaded, normal SAML processing resumes. If the fetch fails (init failure, non-2xx response, unparseable XML, or callout reset), the fetch is retried up toidp_metadata_fetch_max_attemptstimes (default3), separated byidp_metadata_fetch_delay. After all attempts are exhausted, requests continue to receive 503 until Envoy is reloaded.
Optional fields:
sp_cert_pem/sp_key_pem: PEM-encoded SP certificate and private key, each as{"inline": "..."}or{"file": "/path/to/file.pem"}. If both are omitted, an ephemeral self-signed keypair is auto-generated — convenient for development or when the IdP does not require a pre-registered SP certificate. If provided, both must be set together.- Session settings:
session.duration,session.cookie_name,session.cookie_secure,session.cookie_domain,session.cookie_signing_key bypass_paths,attribute_headers,sign_authn_requests,default_redirect_pathidp_metadata_fetch_delay: Go duration string (e.g."1s","30s"). Delay between config load and the IdP metadata HttpCallout, and between retry attempts. Defaults to"1s". Only valid in URL mode.idp_metadata_fetch_max_attempts: Total number of IdP metadata fetch attempts (initial + retries) before giving up. Must be>= 1. Defaults to3. Only valid in URL mode.
Metrics
The extension emits the following Envoy counters:
| Metric | Type | Tags | Description |
|---|---|---|---|
saml_authn_requests_total |
Counter | - | Incremented on each IdP redirect |
saml_assertions_validated_total |
Counter | result (success / failure) |
Incremented after SAML response validation |
saml_sessions_created_total |
Counter | - | Incremented on successful ACS processing |
saml_sessions_validated_total |
Counter | result (valid / expired / invalid) |
Incremented on session cookie validation |
Usage Examples
Basic SAML SSO
Minimal setup - only the IdP metadata is needed. An ephemeral SP certificate and key are auto-generated.
# Start keycloack
curl -o docker-compose.yaml https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/saml/demo/keycloak/docker-compose-remote.yaml
docker compose -f docker-compose.yaml up --wait
# Download the IdP metadata
curl http://localhost:8080/realms/saml-demo/protocol/saml/descriptor -o idp-metadata.xml
# Run the plugin
boe run --extension saml --config '
{
"entity_id": "http://localhost:10000",
"acs_path": "/saml/acs",
"idp_metadata_xml": {"file": "idp-metadata.xml"}
}'
# Test the plugin in the browser and login as testuser/testpass
open http://localhost:10000
# Cleanup keycloak
docker compose -f docker-compose.yaml down -v IdP Metadata from URL
Fetch the IdP metadata from a URL at config-load time via an Envoy cluster instead of providing it inline or as a file. Useful when the IdP is the source of truth for its metadata and you do not want to redeploy the filter when the IdP rotates signing certificates.
# Start keycloack
curl -o docker-compose.yaml https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/saml/demo/keycloak/docker-compose-remote.yaml
docker compose -f docker-compose.yaml up --wait
# Run the plugin pointing at the IdP metadata endpoint and the cluster that can reach it
boe run --extension saml --cluster-insecure=localhost:8080 --config '
{
"entity_id": "http://localhost:10000",
"acs_path": "/saml/acs",
"idp_metadata_url": "http://localhost:8080/realms/saml-demo/protocol/saml/descriptor",
"idp_metadata_cluster": "localhost:8080"
}'
# Test the plugin in the browser and login as testuser/testpass
open http://localhost:10000
# Cleanup keycloak
docker compose -f docker-compose.yaml down -v Explicit SP Certificate
Provide your own SP certificate and key for production deployments where the IdP requires a pre-registered SP certificate.
# Start keycloack
curl -o docker-compose.yaml https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/saml/demo/keycloak/docker-compose-remote.yaml
docker compose -f docker-compose.yaml up --wait
# Download the IdP metadata
curl http://localhost:8080/realms/saml-demo/protocol/saml/descriptor -o idp-metadata.xml
# Run the plugin
boe run --extension saml --config '
{
"entity_id": "http://localhost:10000",
"acs_path": "/saml/acs",
"idp_metadata_xml": {"file": "idp-metadata.xml"},
"sp_cert_pem": {"file": "sp-cert.pem"},
"sp_key_pem": {"file": "sp-key.pem"}
}'
# Test the plugin in the browser and login as testuser/testpass
open http://localhost:10000
# Cleanup keycloak
docker compose -f docker-compose.yaml down -v Custom Session Settings
Configure session cookie name, duration, domain, security, and a fixed HMAC signing key (64 hex chars = 32 bytes) for cookie persistence across restarts.
# Start keycloack
curl -o docker-compose.yaml https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/saml/demo/keycloak/docker-compose-remote.yaml
docker compose -f docker-compose.yaml up --wait
# Download the IdP metadata
curl http://localhost:8080/realms/saml-demo/protocol/saml/descriptor -o idp-metadata.xml
# Run the plugin
boe run --extension saml --config '
{
"entity_id": "http://localhost:10000",
"acs_path": "/saml/acs",
"idp_metadata_xml": {"file": "idp-metadata.xml"},
"session": {
"cookie_name": "my_session",
"duration": "5m",
"cookie_domain": ".example.com",
"cookie_secure": true,
"cookie_signing_key": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
}'
# Test the plugin in the browser and login as testuser/testpass
# Check the cookie with browser dev tools to see how the settings applied
open http://localhost:10000
# Cleanup keycloak
docker compose -f docker-compose.yaml down -v Attribute Mapping and Bypass Paths
Map SAML attributes to upstream headers and bypass health check paths from authentication.
# Start keycloack
curl -o docker-compose.yaml https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/saml/demo/keycloak/docker-compose-remote.yaml
docker compose -f docker-compose.yaml up --wait
# Download the IdP metadata
curl http://localhost:8080/realms/saml-demo/protocol/saml/descriptor -o idp-metadata.xml
# Run the plugin
boe run --extension saml --config '
{
"entity_id": "http://localhost:10000",
"acs_path": "/saml/acs",
"idp_metadata_xml": {"file": "idp-metadata.xml"},
"attribute_headers": {
"email": "x-saml-email",
"Role": "x-saml-role"
},
"bypass_paths": ["/health", "/ready", "/status/409"],
}'
# Test the plugin in the browser and login as testuser/testpass and check how the headers are set
open http://localhost:10000/headers
# Clear the session cookies and then open again the browser and check how authentication is bypassed
open http://localhost:10000/status/409
# Cleanup keycloak
docker compose -f docker-compose.yaml down -v