Double proxy (with mTLS encryption)

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.

openssl

Generate SSL keys and certificates.

This sandbox demonstrates a basic “double proxy” configuration, in which a simple Flask app connects to a PostgreSQL database, with two Envoy proxies in between.

Envoy (front) -> Flask -> Envoy (postgres-front) -> Envoy (postgres-back) -> PostgreSQL

This type of setup is common in a service mesh where Envoy acts as a “sidecar” between individual services.

It can also be useful as a way of providing access for application servers to upstream services or databases that may be in a different location or subnet, outside of a service mesh or sidecar-based setup.

Another common use case is with Envoy configured to provide “Points of presence” at the edges of the cloud, and to relay requests to upstream servers and services.

This example encrypts the transmission of data between the two middle proxies and provides mutual authentication using mTLS.

This can be useful if the proxies are physically separated or transmit data over untrusted networks.

In order to use the sandbox you will first need to generate the necessary SSL keys and certificates.

This example walks through creating a certificate authority, and using it to create a domain key and sign certificates for the proxies.

Change to the examples/double-proxy directory.

Step 1: Create a certificate authority

First create a key for the certificate authority:

  1. $ pwd
  2. envoy/examples/double-proxy
  3. $ mkdir -p certs
  4. $ openssl genrsa -out certs/ca.key 4096
  5. Generating RSA private key, 4096 bit long modulus (2 primes)
  6. ..........++++
  7. ..........................................................................................................++++
  8. e is 65537 (0x010001)

Now use the key to generate a certificate authority certificate.

If you wish, you can interactively alter the fields in the certificate.

For the purpose of this example, the defaults should be sufficient.

  1. $ openssl req -x509 -new -nodes -key certs/ca.key -sha256 -days 1024 -out certs/ca.crt
  2. You are about to be asked to enter information that will be incorporated
  3. into your certificate request.
  4. What you are about to enter is what is called a Distinguished Name or a DN.
  5. There are quite a few fields but you can leave some blank
  6. For some fields there will be a default value,
  7. If you enter '.', the field will be left blank.
  8. -----
  9. Country Name (2 letter code) [AU]:
  10. State or Province Name (full name) [Some-State]:
  11. Locality Name (eg, city) []:
  12. Organization Name (eg, company) [Internet Widgits Pty Ltd]:
  13. Organizational Unit Name (eg, section) []:
  14. Common Name (e.g. server FQDN or YOUR name) []:
  15. Email Address []:

Step 2: Create a domain key

Create a key for the example domain:

  1. $ openssl genrsa -out certs/example.com.key 2048
  2. Generating RSA private key, 2048 bit long modulus (2 primes)
  3. ..+++++
  4. .................................................+++++
  5. e is 65537 (0x010001)

Step 3: Generate certificate signing requests for the proxies

Use the domain key to generate certificate signing requests for each of the proxies:

  1. $ openssl req -new -sha256 \
  2. -key certs/example.com.key \
  3. -subj "/C=US/ST=CA/O=MyExample, Inc./CN=proxy-postgres-frontend.example.com" \
  4. -out certs/proxy-postgres-frontend.example.com.csr
  5. $ openssl req -new -sha256 \
  6. -key certs/example.com.key \
  7. -subj "/C=US/ST=CA/O=MyExample, Inc./CN=proxy-postgres-backend.example.com" \
  8. -out certs/proxy-postgres-backend.example.com.csr

Step 4: Sign the proxy certificates

You can now use the certificate authority that you created to sign the certificate requests.

Note the subjectAltName. This is used for reciprocally matching and validating the certificates.

  1. $ openssl x509 -req \
  2. -in certs/proxy-postgres-frontend.example.com.csr \
  3. -CA certs/ca.crt \
  4. -CAkey certs/ca.key \
  5. -CAcreateserial \
  6. -extfile <(printf "subjectAltName=DNS:proxy-postgres-frontend.example.com") \
  7. -out certs/postgres-frontend.example.com.crt \
  8. -days 500 \
  9. -sha256
  10. Signature ok
  11. subject=C = US, ST = CA, O = "MyExample, Inc.", CN = proxy-postgres-frontend.example.com
  12. Getting CA Private Key
  13. $ openssl x509 -req \
  14. -in certs/proxy-postgres-backend.example.com.csr \
  15. -CA certs/ca.crt \
  16. -CAkey certs/ca.key \
  17. -CAcreateserial \
  18. -extfile <(printf "subjectAltName=DNS:proxy-postgres-backend.example.com") \
  19. -out certs/postgres-backend.example.com.crt \
  20. -days 500 \
  21. -sha256
  22. Signature ok
  23. subject=C = US, ST = CA, O = "MyExample, Inc.", CN = proxy-postgres-backend.example.com
  24. Getting CA Private Key

At this point you should have the necessary keys and certificates to secure the connection between the proxies.

The keys and certificates are stored in the certs/ directory.

Step 5: Start all of our containers

Build and start the containers.

This will load the required keys and certificates into the frontend and backend proxies.

  1. $ pwd
  2. envoy/examples/double-proxy
  3. $ docker-compose build --pull
  4. $ docker-compose up -d
  5. $ docker-compose ps
  6. Name Command State Ports
  7. --------------------------------------------------------------------------------------------------------
  8. double-proxy_app_1 python3 /code/service.py Up
  9. double-proxy_postgres_1 docker-entrypoint.sh postgres Up 5432/tcp
  10. double-proxy_proxy-frontend_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp
  11. double-proxy_proxy-postgres-backend_1 /docker-entrypoint.sh /usr ... Up 10000/tcp
  12. double-proxy_proxy-postgres-frontend_1 /docker-entrypoint.sh /usr ... Up 10000/tcp

Step 6: Check the flask app can connect to the database

Checking the response at http://localhost:10000, you should see the output from the Flask app:

  1. $ curl -s http://localhost:10000
  2. Connected to Postgres, version: PostgreSQL 13.0 (Debian 13.0-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit

See also

Securing Envoy quick start guide

Outline of key concepts for securing Envoy.

TLS sandbox

Examples of various TLS termination patterns with Envoy.