Skip to content

Latest commit

 

History

History
281 lines (211 loc) · 8.98 KB

webdis-redis-docker-compose-ssl.md

File metadata and controls

281 lines (211 loc) · 8.98 KB

Running Webdis and Redis in Docker Compose with SSL connections

This page describes how to start Redis and Webdis in Docker Compose, with secure connections between the two.

Requirements

For this, we'll need:

  1. Docker Compose (you should have it if you use Docker Desktop)
  2. Redis version 6 or newer (we'll use a Docker image)
  3. Webdis version 0.1.18 or newer (also in a Docker image)
  4. A client certificate and key
  5. A CA certificate
  6. The openssl command-line tool
  7. Optionally, curl for downloading a few files

We'll keep all our files together in a playground directory:

mkdir playground
cd playground

SSL configuration

Let's start by generating the files required to encrypt connections. These instructions are adapted from the Makefile on this page.

CA certificate

First, the CA cert. Generate a key for it, and then the cert:

openssl genrsa 4096 > ./ca.key
openssl req -new -x509 -nodes -sha256 -key ./ca.key -days 3650 \
    -subj "/C=AU/CN=example" -out ./ca.crt

Certificate Signing Request (CSR)

Let's start with a custom OpenSSL config file (change the path from /etc/ssl if your openssl.cnf is located elsewhere, e.g. at /etc/pki/tls/openssl.cnf on Fedora).

cp /etc/ssl/openssl.cnf .
echo >> ./openssl.cnf
echo '[ san_env ]' >> ./openssl.cnf
echo 'subjectAltName = IP:127.0.0.1' >> ./openssl.cnf
If you can't find your openssl.cnf, click here to show a basic file you can use.

Save the following block as openssl.cnf in your playground directory:

[ req ]
distinguished_name = req_distinguished_name
attributes  = req_attributes

[ req_distinguished_name ]
countryName   = Country Name (2 letter code)
countryName_min   = 2
countryName_max   = 2
stateOrProvinceName  = State or Province Name (full name)
localityName   = Locality Name (eg, city)
0.organizationName  = Organization Name (eg, company)
organizationalUnitName  = Organizational Unit Name (eg, section)
commonName   = Common Name (eg, fully qualified host name)
commonName_max   = 64
emailAddress   = Email Address
emailAddress_max  = 64

[ req_attributes ]
challengePassword  = A challenge password
challengePassword_min  = 4
challengePassword_max  = 20

[ san_env ]
subjectAltName = IP:127.0.0.1

Then we can create the CSR and key:

export SAN='IP:127.0.0.1'
openssl req -reqexts san_env -extensions san_env -config ./openssl.cnf \
    -newkey rsa:4096 -nodes -sha256 -keyout ./redis.key \
    -subj "/C=AU/CN=127.0.0.1" -out ./redis.csr

Make sure this command created redis.key and redis.csr.

Certificate

Finally, let's generate the certificate:

openssl x509 -req -sha256 -extfile ./openssl.cnf -extensions san_env \
    -days 3650 -in ./redis.csr -CA ./ca.crt -CAkey ./ca.key \
    -CAcreateserial -out ./redis.crt

We should now have ca.crt, redis.key, and redis.crt. We'll need these 3 files to configure the encrypted connections between Webdis and Redis. The other files generated by openssl (redis.csr and ca.key) are not needed by Redis or Webdis.

Docker Compose directory structure

Let's start with the config files needed by Redis and Webdis; we'll keep them all in a local directory that is mounted by the containers.

mkdir config
cp ca.crt redis.key redis.crt ./config
curl -sL -o ./config/webdis.json https://github.com/nicolasff/webdis/raw/0.1.22/webdis.json
curl -sL -o ./config/redis.conf https://github.com/redis/redis/raw/7.0.10/redis.conf

If you don't have curl, use the two URLs above to fetch webdis.json and redis.conf and move them to the config directory under playground.

Service configuration

Webdis

Edit ./config/webdis.json and set:

  • "redis_host" to "redis"
  • "redis_port" to 6380
  • "logfile" to "/dev/stderr"

And add a key named "ssl" at the same depth as the two keys above (e.g. just under "database": 0), pointing to:

{
    "enabled": true,
    "ca_cert_bundle": "/config/ca.crt",
    "client_cert": "/config/redis.crt",
    "client_key": "/config/redis.key"
},
Click here to see what webdis.json should look like after these changes.
{
    "redis_host": "redis",
    "redis_port": 6380,

    "http_host": "0.0.0.0",
    "http_port": 7379,

    "threads": 5,
    "pool_size": 20,

    "daemonize": false,
    "websockets": false,

    "database": 0,

    "ssl": {
        "enabled": true,
        "ca_cert_bundle": "/config/ca.crt",
        "client_cert": "/config/redis.crt",
        "client_key": "/config/redis.key"
    },

    "acl": [
        {
            "disabled": ["DEBUG"]
        },
        {
            "http_basic_auth": "user:password",
            "enabled": ["DEBUG"]
        }
    ],

    "verbosity": 4,
    "logfile": "/dev/stderr"
}

If you have jq installed, you can validate that it is valid JSON with:

jq empty ./config/webdis.json && echo 'VALID' || echo 'INVALID'

Redis

Then, edit ./config/redis.conf and uncomment the following configuration options and set their values as listed:

  • tls-port 6380 (this should initially say # tls-port 6379, make sure to change the port number)
  • tls-cert-file /config/redis.crt
  • tls-key-file /config/redis.key
  • tls-ca-cert-file /config/ca.crt
  • protected-mode no

Again, make sure to remove any leading # and spaces from these lines. For example, tls-port is originally commented out in redis.conf and looks like this:

# tls-port 6379

Finally, change the line which starts with bind to:

bind 0.0.0.0

You can also grab redis.conf from this Gist which contains a Redis 6.2.6 config file with the required changes.

Docker Compose configuration

Create a new file named docker-compose.yml in your playground directory, with the following contents:

services:
  webdis:
    image: nicolas/webdis:latest
    command: /usr/local/bin/webdis-ssl /config/webdis.json
    volumes:  # mount volume containing the config files
      - ./config:/config
    networks:
      - secure
    depends_on:  # make sure Redis starts first, so that Webdis can connect to it without retries
      - redis
    ports:  # allow connections from the Docker host on localhost, port 7379
      - "127.0.0.1:7379:7379"

  redis:
    image: redis:7.0.10
    command: /usr/local/bin/redis-server /config/redis.conf
    volumes:  # mount volume containing the config files
      - ./config:/config
    networks:
      - secure
    expose:   # make the TLS port from Redis visible to Webdis
      - "6380"

networks:
  secure: 

This configures two services named webdis and redis, sharing a common network named secure. With the expose property Redis allows connections from Webdis on port 6380. Both containers mount the local config directory under /config and start their binaries using the configuration files we've just created and edited. Finally, Webdis also allows binds its (container) port 7379 to the hosts's loopback interface also on port 7379. This will let us run curl locally to connect to Webdis from the host.

Note: While the Webdis Docker image does bundle a Redis binary, it makes more sense to use multiple containers to demonstrate the use of SSL connections. This bundled Redis service does not run in this example, since we replace the Webdis command with one that only starts Webdis instead of starting Redis and Webdis together in the same container.

Start the Docker Compose stack

From the playground directory, run:

docker-compose up

You should see both services logging to the console in different colors, with an output like:

Creating playground_redis_1 ... done
Creating playground_webdis_1 ... done
Attaching to playground_redis_1, playground_webdis_1
redis_1   | 1:C 21 Aug 2023 01:42:49.704 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1   | 1:C 21 Aug 2023 01:42:49.705 # Redis version=7.0.10, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1   | 1:C 21 Aug 2023 01:42:49.705 # Configuration loaded
redis_1   | 1:M 21 Aug 2023 01:42:49.716 * monotonic clock: POSIX clock_gettime
webdis_1  | [1] 21 Aug 01:42:51 I Webdis listening on port 7379
webdis_1  | [1] 21 Aug 01:42:51 I Webdis 0.1.22 up and running
redis_1   | 1:M 21 Aug 2023 01:42:49.717 * Running mode=standalone, port=6379.
redis_1   | 1:M 21 Aug 2023 01:42:49.717 # Server initialized
redis_1   | 1:M 21 Aug 2023 01:42:49.718 * Ready to accept connections

You can now run commands against Webdis by connecting to port 7379 on localhost, e.g.

$ curl -s http://localhost:7379/ping
{"ping":[true,"PONG"]}

$ curl -s http://localhost:7379/info.txt | grep uptime.in.seconds
uptime_in_seconds:27

Clean-up

Stop the services with ctrl-c and remove the entire Docker Compose stack by running docker-compose rm from the playground directory.