TLS Server name indication (SNI)

Requirements

Sandbox environment

Setup your sandbox environment with Docker and Docker Compose, and clone the Envoy repository with Git.

curl

Used to make HTTP requests.

jq

Parse json output from the upstream echo servers.

This example demonstrates an Envoy proxy that listens on three TLS domains on the same IP address.

The first two domains (domain1 and domain2) terminate the TLS and proxy to upstream HTTP hosts.

The other domain (domain3) is proxied unterminated, based on the SNI headers.

It also demonstrates Envoy acting as a client proxy connecting to upstream SNI services.

Step 1: Create keypairs for each of the domain endpoints

Change directory to examples/tls-sni in the Envoy repository.

The example creates two Envoy TLS endpoints and they will require their own keypairs.

Create self-signed certificates for these endpoints as follows:

  1. $ pwd
  2. envoy/examples/tls-sni
  3. $ mkdir -p certs
  4. $ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \
  5. -subj "/C=US/ST=CA/O=MyExample, Inc./CN=domain1.example.com" \
  6. -keyout certs/domain1.key.pem \
  7. -out certs/domain1.crt.pem
  8. Generating a RSA private key
  9. .............+++++
  10. ...................+++++
  11. writing new private key to 'certs/domain1.key.pem'
  12. -----
  13. $ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \
  14. -subj "/C=US/ST=CA/O=MyExample, Inc./CN=domain2.example.com" \
  15. -keyout certs/domain2.key.pem \
  16. -out certs/domain2.crt.pem
  17. Generating a RSA private key
  18. .............+++++
  19. ...................+++++
  20. writing new private key to 'certs/domain2.key.pem'
  21. -----

Warning

SNI does not validate that the certificates presented are correct for the domain, or that they were issued by a recognised certificate authority.

See the Securing Envoy quick start guide for more information about validating cerfificates.

Step 2: Start the containers

Build and start the containers.

This starts two upstream HTTP containers listening on the internal Docker network on port 80, and an upstream HTTPS service listening on internal port 443

In front of these is an Envoy proxy that listens on https://localhost:10000 and serves three SNI routed TLS domains:

  • domain1.example.com

  • domain2.example.com

  • domain3.example.com

The first two domains use the keys and certificates you created in step 1 to terminate TLS and proxy to the two upstream HTTP servers.

The third domain proxies to the upstream TLS server based on the requested SNI address, but does no TLS termination itself.

The composition also starts an Envoy proxy client which listens on http://localhost:20000.

The client proxy has no TLS termination but instead proxies three routed paths - /domain1, /domain2 and /domain3 - to the SNI-enabled proxy.

  1. $ pwd
  2. envoy/examples/tls-sni
  3. $ docker-compose build --pull
  4. $ docker-compose up -d
  5. $ docker-compose ps
  6. Name Command State Ports
  7. -------------------------------------------------------------------------------------------
  8. tls-sni_http-upstream1_1 node ./index.js Up
  9. tls-sni_http-upstream2_1 node ./index.js Up
  10. tls-sni_http-upstream3_1 node ./index.js Up
  11. tls-sni_proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp
  12. tls-sni_proxy-client_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:20000->10000/tcp

Step 2: Query the SNI endpoints directly with curl

You can use curl to query the SNI-routed HTTPS endpoints of the Envoy proxy directly.

To do this you must explicitly tell curl to resolve the DNS for the endpoints correctly.

Each endpoint should proxy to the respective http-upstream or https-upstream service.

  1. $ curl -sk --resolve domain1.example.com:10000:127.0.0.1 \
  2. https://domain1.example.com:10000 \
  3. | jq -r '.os.hostname'
  4. http-upstream1
  5. $ curl -sk --resolve domain2.example.com:10000:127.0.0.1 \
  6. https://domain2.example.com:10000 \
  7. | jq -r '.os.hostname'
  8. http-upstream2
  9. $ curl -sk --resolve domain3.example.com:10000:127.0.0.1 \
  10. https://domain3.example.com:10000 \
  11. | jq -r '.os.hostname'
  12. https-upstream3

Step 3: Query the SNI endpoints via an Envoy proxy client

Next, query the Envoy proxy client using the routed paths.

These route via the SNI proxy endpoints to the respective http-upstream or https-upstream services.

  1. $ curl -s http://localhost:20000/domain1 \
  2. | jq '.os.hostname'
  3. http-upstream1
  4. $ curl -s http://localhost:20000/domain2 \
  5. | jq '.os.hostname'
  6. http-upstream2
  7. $ curl -s http://localhost:20000/domain3 \
  8. | jq '.os.hostname'
  9. https-upstream3

See also

Securing Envoy quick start guide

Outline of key concepts for securing Envoy.

TLS sandbox

Sandbox featuring examples of how Envoy can be configured to make use of encrypted connections using HTTP over TLS.

Double proxy sandbox

An example of securing traffic between proxies with validation and mutual authentication using mTLS with non-HTTP traffic.