You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
8.8 KiB
Markdown
275 lines
8.8 KiB
Markdown
3 years ago
|
# Running Webdis & Redis in Docker Compose with SSL connections
|
||
|
|
||
|
This page describes how to start Redis and Webdis in [Docker Compose](https://docs.docker.com/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](https://www.docker.com/products/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:
|
||
|
|
||
|
```shell
|
||
|
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](https://nishanths.svbtle.com/setting-up-redis-with-tls).
|
||
|
|
||
|
### CA certificate
|
||
|
|
||
|
First, the CA cert. Generate a key for it, and then the cert:
|
||
|
|
||
|
```shell
|
||
|
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).
|
||
|
|
||
|
```shell
|
||
|
cp /etc/ssl/openssl.cnf .
|
||
|
echo >> ./openssl.cnf
|
||
|
echo '[ san_env ]' >> ./openssl.cnf
|
||
|
echo 'subjectAltName = IP:127.0.0.1' >> ./openssl.cnf
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>If you can't find your openssl.cnf, click here to show a basic file you can use.</summary>
|
||
|
|
||
|
Save the following block as `openssl.cnf` in your `playground` directory:
|
||
|
|
||
|
```ini
|
||
|
[ 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
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
Then we can create the CSR and key:
|
||
|
```shell
|
||
|
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:
|
||
|
|
||
|
```shell
|
||
|
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.
|
||
|
|
||
|
```shell
|
||
|
mkdir config
|
||
|
cp ca.crt redis.key redis.crt ./config
|
||
|
curl -sL -o ./config/webdis.json https://github.com/nicolasff/webdis/raw/0.1.18/webdis.json
|
||
|
curl -sL -o ./config/redis.conf https://github.com/redis/redis/raw/6.2.6/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:
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"enabled": true,
|
||
|
"ca_cert_bundle": "/config/ca.crt",
|
||
|
"client_cert": "/config/redis.crt",
|
||
|
"client_key": "/config/redis.key"
|
||
|
},
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>Click here to see what webdis.json should look like after these changes.</summary>
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"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"
|
||
|
}
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
If you have `jq` installed, you can validate that it is valid JSON with:
|
||
|
|
||
|
```shell
|
||
|
jq empty ./config/webdis.json && echo 'VALID' || echo 'INVALID'
|
||
|
```
|
||
|
|
||
|
### Redis
|
||
|
|
||
|
Then, edit `./config/redis.conf` and uncomment the following lines and set their values as listed:
|
||
|
|
||
|
- `tls-port 6380` (on line 145, this should initially say `# tls-port 6379`, make sure to change the port number)
|
||
|
- `tls-cert-file /config/redis.crt` (on line 151)
|
||
|
- `tls-key-file /config/redis.key` (on line 152)
|
||
|
- `tls-ca-cert-file /config/ca.crt` (on line 184)
|
||
|
|
||
|
Then change line 75 which starts with `bind`, so that it looks like this:
|
||
|
|
||
|
```
|
||
|
bind 0.0.0.0
|
||
|
```
|
||
|
|
||
|
You can also grab `redis.conf` from [this Gist](https://gist.github.com/nicolasff/513d3ebd9d6f4268d6deb1d979fa44b8) 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:
|
||
|
|
||
|
```yaml
|
||
|
services:
|
||
|
webdis:
|
||
|
image: nicolas/webdis:0.1.18
|
||
|
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:6.2.6
|
||
|
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:
|
||
|
|
||
|
```shell
|
||
|
docker-compose up
|
||
|
```
|
||
|
|
||
|
You should see both services logging to the console in different colors, with an output like:
|
||
|
```none
|
||
|
Creating playground_redis_1 ... done
|
||
|
Creating playground_webdis_1 ... done
|
||
|
Attaching to playground_redis_1, playground_webdis_1
|
||
|
redis_1 | 1:C 23 Oct 2021 01:42:49.704 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
|
||
|
redis_1 | 1:C 23 Oct 2021 01:42:49.705 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=1, just started
|
||
|
redis_1 | 1:C 23 Oct 2021 01:42:49.705 # Configuration loaded
|
||
|
redis_1 | 1:M 23 Oct 2021 01:42:49.716 * monotonic clock: POSIX clock_gettime
|
||
|
webdis_1 | [1] 23 Oct 01:42:51 I Webdis listening on port 7379
|
||
|
webdis_1 | [1] 23 Oct 01:42:51 I Webdis 0.1.18 up and running
|
||
|
redis_1 | 1:M 23 Oct 2021 01:42:49.717 * Running mode=standalone, port=6379.
|
||
|
redis_1 | 1:M 23 Oct 2021 01:42:49.717 # Server initialized
|
||
|
redis_1 | 1:M 23 Oct 2021 01:42:49.718 * Ready to accept connections
|
||
|
```
|
||
|
|
||
|
You can now run commands against Webdis by connecting to port 7379 on `localhost`, e.g.
|
||
|
|
||
|
```sh
|
||
|
$ curl -s 'http://localhost:7379/ping'
|
||
|
{"ping":[true,"PONG"]}
|
||
|
|
||
|
$ curl -s 'http://localhost:7379/info' | jq -r .info.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.
|