Skip to content

Instantly share code, notes, and snippets.

@tsaarni
Last active April 13, 2026 07:52
Show Gist options
  • Select an option

  • Save tsaarni/df84d55f2953707de7f5543f30554b32 to your computer and use it in GitHub Desktop.

Select an option

Save tsaarni/df84d55f2953707de7f5543f30554b32 to your computer and use it in GitHub Desktop.
Add support for Envoy X-Forwarded-For-Client-Cert

This document contains manual test steps for PR openbao#2863.

All required files are attached in this gist.

1. Create test certificates.

cat <<EOF >certs.yaml
subject: cn=server-ca
---
subject: cn=client-ca
---
subject: cn=envoy
issuer: cn=server-ca
sans:
  - DNS:localhost
---
subject: cn=client, o=example\, inc\;
issuer: cn=client-ca
ext_key_usages:
  - ClientAuth
sans:
  - DNS:client.example.com
  - URI:spiffe://example.com/client
EOF

mkdir -p certs
certyaml -d certs
chmod +r certs/*.pem  # make readable for the process in envoy container

For installation instructions for certyaml see here.

2. Start OpenBao

cat <<EOF >openbao-config.hcl
listener "tcp" {
  address = "127.0.0.1:18200"
  tls_disable = true
  x_forwarded_for_reject_not_authorized = true
  x_forwarded_for_reject_not_present = true
  x_forwarded_for_authorized_addrs = "127.0.0.1"
  x_forwarded_for_client_cert_header = "X-Forwarded-Client-Cert"
  x_forwarded_for_client_cert_decoders = ["Envoy", "PEM"]
}
EOF

bao server -dev -config=openbao-config.hcl

3. Configure authentication

export BAO_ADDR=http://127.0.0.1:8200

bao auth enable cert
bao write auth/cert/certs/envoy-test \
  display_name=envoy-test \
  certificate=@certs/client-ca.pem \
  token_policies=default

4. Download Envoy configuration file from this gist

curl --output envoy-xfcc.yaml \
  https://gist.githubusercontent.com/tsaarni/df84d55f2953707de7f5543f30554b32/raw/b4a090c74ddde477d75d67bc10e150b402853b9c/envoy-xfcc.yaml

Start Envoy within docker, use host networking, and mount current directory into container so that it can read the certificates

docker run --rm \
    --network host \
    --volume "$PWD:/host:ro" \
    --volume "$PWD/envoy-xfcc.yaml:/etc/envoy/envoy.yaml:ro" \
    envoyproxy/envoy:v1.37-latest

5. Authenticate by forwarded client certificate in XFCC header

curl --silent \
     --cacert certs/server-ca.pem \
     --cert certs/client.pem \
     --key certs/client-key.pem \
     --request POST https://localhost:8443/v1/auth/cert/login | jq .

Optional: To see the header in action you can use wireshark:

wireshark -i lo -f "tcp port 18200" -k -Y http

With the example envoy config that enables all cert details, the header is

x-forwarded-client-cert: Hash=5e71338740504102028950cece8f39b9b7e210c0455a3dcc2dca01311db9ebf3;Cert="-----BEGIN%20CERTIFICATE-----%0AMIIBujCCAV%2BgAwIBAgIIGKXbNaXOu6UwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ%0AY2xpZW50LWNhMB4XDTI2MDQxMzA3NTA1MVoXDTI3MDQxMzA3NTA1MVowKTEWMBQG%0AA1UECgwNZXhhbXBsZSwgaW5jOzEPMA0GA1UEAxMGY2xpZW50MFkwEwYHKoZIzj0C%0AAQYIKoZIzj0DAQcDQgAEuZzIPl6w3hMB6ND9JeXZtkXkn7AtbrM5fVsgB95Mr0ga%0AmplOz%2Biv46SAL6WHrKppxhd00GSsUmxZBftZJAgZmaOBhTCBgjAOBgNVHQ8BAf8E%0ABAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUIvljS43QMTS8%0AM7g9iPgxbKTx6d4wOgYDVR0RBDMwMYISY2xpZW50LmV4YW1wbGUuY29thhtzcGlm%0AZmU6Ly9leGFtcGxlLmNvbS9jbGllbnQwCgYIKoZIzj0EAwIDSQAwRgIhANICYQkr%0AI%2F5D6F%2BUkTyA6RNrKZsTatzGG%2FY5L7K3nMtCAiEAkXhjbmumxl%2BFJc4snyCPu%2F%2FC%0APkit2QRxGZ21mKUEwQA%3D%0A-----END%20CERTIFICATE-----%0A";Chain="-----BEGIN%20CERTIFICATE-----%0AMIIBujCCAV%2BgAwIBAgIIGKXbNaXOu6UwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ%0AY2xpZW50LWNhMB4XDTI2MDQxMzA3NTA1MVoXDTI3MDQxMzA3NTA1MVowKTEWMBQG%0AA1UECgwNZXhhbXBsZSwgaW5jOzEPMA0GA1UEAxMGY2xpZW50MFkwEwYHKoZIzj0C%0AAQYIKoZIzj0DAQcDQgAEuZzIPl6w3hMB6ND9JeXZtkXkn7AtbrM5fVsgB95Mr0ga%0AmplOz%2Biv46SAL6WHrKppxhd00GSsUmxZBftZJAgZmaOBhTCBgjAOBgNVHQ8BAf8E%0ABAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUIvljS43QMTS8%0AM7g9iPgxbKTx6d4wOgYDVR0RBDMwMYISY2xpZW50LmV4YW1wbGUuY29thhtzcGlm%0AZmU6Ly9leGFtcGxlLmNvbS9jbGllbnQwCgYIKoZIzj0EAwIDSQAwRgIhANICYQkr%0AI%2F5D6F%2BUkTyA6RNrKZsTatzGG%2FY5L7K3nMtCAiEAkXhjbmumxl%2BFJc4snyCPu%2F%2FC%0APkit2QRxGZ21mKUEwQA%3D%0A-----END%20CERTIFICATE-----%0A";Subject="CN=client,O=example\, inc\;";URI=spiffe://example.com/client;DNS=client.example.com
admin:
address:
socket_address:
address: 127.0.0.1
port_value: 9901
static_resources:
clusters:
- name: openbao
type: STRICT_DNS
connect_timeout: 5s
load_assignment:
cluster_name: openbao
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 18200
listeners:
- name: https
address:
socket_address:
address: 127.0.0.1
port_value: 8443
filter_chains:
- filters:
- name: envoy.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
# Makes Envoy add X-Forwarded-For (required by OpenBao to be able to parse the client certificate from the header).
use_remote_address: true
access_log:
- name: fileaccesslog
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
forward_client_cert_details: SANITIZE_SET
set_current_client_cert_details:
subject: true
cert: true
chain: true
dns: true
uri: true
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local
virtual_hosts:
- name: openbao
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: openbao
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain:
filename: /host/certs/envoy.pem
private_key:
filename: /host/certs/envoy-key.pem
validation_context:
trusted_ca:
filename: /host/certs/client-ca.pem
require_client_certificate: false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment