To follow this guide, you will need Docker v1.10 or higher. Docker v1.10 has some volume and networking features that will come in handy for setting up a cluster. While this guide uses a single host with a cluster composed of several RethinkDB containers, the multi-host networking features of Docker can be used to apply the following example to a multi-host Docker cluster (Docker Swarm).
For this guide, we'll be using the same key pair and self-signed certificate for all connections. While this makes setup easy, it essentially makes the key pair a shared secret. In a real production environment, you should use a separate key pair for each server node and client with a root CA key stored offline which is only used to issue new certificates. How to manage this is outside the scope of this guide but it should give you an idea of how configure TLS to secure your RethinkDB cluster.
First, create a volume in Docker which will be used to store the TLS key pair:
docker volume create --name rdb-tls
Use a container to create the TLS key pair and save them to this volume:
docker run -i --rm -v rdb-tls:/tls alpine:3.3 sh << 'EOF'
set -ex
apk update
apk upgrade libcrypto1.0 libssl1.0
apk add openssl
openssl genrsa -out /tls/key.pem 2048
openssl req -new -x509 -key /tls/key.pem -out /tls/cert.pem -days 3650 -subj '/CN=*.rdb'
cp /tls/cert.pem /tls/ca.pem
EOF
Each node in the cluster will be able to discover and communicate with each
other over the network. This network is called rdb
. Note that the certificate
created in the previous step has a subject of CN=*.rdb
. This allows servers to
authenticate to clients using any name on this network.
docker network create rdb
The following command will create the first node in the cluster (inline
comments should be ignored by bash
):
docker run \
-v rdb-01-data:/var/data `# create a volume for this node's data, mounted at /var/data` \
-v rdb-tls:/tls `# mount the tls key pair at /tls` \
--net rdb `# add this container to the rdb network` \
--name rdb-01 `# the container will have the domain name 'rdb-01.rdb' on the network` \
--net-alias rdb `# the domain name 'rdb.rdb' will refer to the longest-running container with this alias` \
jlhawn/rethinkdb-tls `# this is the container image to run. It has the entrypoint '/bin/rethinkdb'` \
--bind all `# bind to the network interface` \
--no-http-admin \
--server-name rdb_01 \
--canonical-address rdb-01.rdb \
--directory /var/data/rethinkdb \
--join rdb.rdb `# The node should ignore joining itself` \
--driver-tls \
--driver-tls-key /tls/key.pem \
--driver-tls-cert /tls/cert.pem \
--cluster-tls \
--cluster-tls-key /tls/key.pem \
--cluster-tls-cert /tls/cert.pem \
--cluster-tls-ca /tls/ca.pem
The following command creates a second node and joins it to the first node
using the network alias rdb.rdb
which will resolve to the same address as
rdb-01.rdb
. Notice that the only difference between this command and the
previous one is that we use 02
instead of 01
.
docker run \
-v rdb-02-data:/var/data `# create a volume for this node's data, mounted at /var/data` \
-v rdb-tls:/tls `# mount the tls key pair at /tls` \
--net rdb `# add this container to the rdb network` \
--name rdb-02 `# the container will have the domain name 'rdb-02.rdb' on the network` \
--net-alias rdb `# the domain name 'rdb.rdb' will refer to the longest-running container with this alias` \
jlhawn/rethinkdb-tls `# this is the container image to run. It has the entrypoint '/bin/rethinkdb'` \
--bind all `# bind to the network interface` \
--no-http-admin \
--server-name rdb_02 \
--canonical-address rdb-02.rdb \
--directory /var/data/rethinkdb \
--join rdb.rdb `# The node should ignore joining itself` \
--driver-tls \
--driver-tls-key /tls/key.pem \
--driver-tls-cert /tls/cert.pem \
--cluster-tls \
--cluster-tls-key /tls/key.pem \
--cluster-tls-cert /tls/cert.pem \
--cluster-tls-ca /tls/ca.pem
The following command creates a third node and joins it to the first node using
the same network alias rdb.rdb
which will resolve to the same address as
rdb-01.rdb
. This new node will discover and connect to the second node using
it's canonical address rdb-02.rdb
which the third node can also resolve via
the network. Notice that the only difference between this command and the
previous one is that we use 03
instead of 02
.
docker run \
-v rdb-03-data:/var/data `# create a volume for this node's data, mounted at /var/data` \
-v rdb-tls:/tls `# mount the tls key pair at /tls` \
--net rdb `# add this container to the rdb network` \
--name rdb-03 `# the container will have the domain name 'rdb-03.rdb' on the network` \
--net-alias rdb `# the domain name 'rdb.rdb' will refer to the longest-running container with this alias` \
jlhawn/rethinkdb-tls `# this is the container image to run. It has the entrypoint '/bin/rethinkdb'` \
--bind all `# bind to the network interface` \
--no-http-admin \
--server-name rdb_03 \
--canonical-address rdb-03.rdb \
--directory /var/data/rethinkdb \
--join rdb.rdb `# The node should ignore joining itself` \
--driver-tls \
--driver-tls-key /tls/key.pem \
--driver-tls-cert /tls/cert.pem \
--cluster-tls \
--cluster-tls-key /tls/key.pem \
--cluster-tls-cert /tls/cert.pem \
--cluster-tls-ca /tls/ca.pem
The following command creates a RethinkDB proxy server which will join the cluster and listen for Web Admin connections and client driver connections, both using TLS.
docker run \
-v rdb-tls:/tls \
--net rdb \
--name rdb-proxy \
-p 8080:8080 `# Forward the Web port from the host so that your browser can connect.` \
jlhawn/rethinkdb-tls \
proxy \
--bind all \
--join rdb.rdb \
--web-tls \
--web-tls-key /tls/key.pem \
--web-tls-cert /tls/cert.pem \
--driver-tls \
--driver-tls-key /tls/key.pem \
--driver-tls-cert /tls/cert.pem \
--cluster-tls \
--cluster-tls-key /tls/key.pem \
--cluster-tls-cert /tls/cert.pem \
--cluster-tls-ca /tls/ca.pem
The proxy is patiently waiting for you to connect. You'll need to connect to your Docker host in your web browser over HTTPS on port 8080. If your Docker host is local (not in a virtual machine or on a remote host), then you should be able to connect to:
`https://localhost:8080`
in your web browser. Otherwise, check the value of your DOCKER_HOST
environment variable. My Docker host runs in a virtual machine. Here's mine:
$ echo $DOCKER_HOST
tcp://192.168.56.103:2376
So, I'll connect to:
`https://192.168.56.103:8080`
NOTE: Use a modern browser which supports the same TLS cipher suites as RethinkDB. I recommend Mozilla Firefox or Google Chrome.
NOTE: Because we are using a self-signed certificate, your browser will not initially connect to the proxy because it does not recognize the issuer of the certificate. You'll need to acknowledge that you want to proceed anyway.
You should now be able to see the Web Admin Dashboard. If you go to the Tables
tab, you'll see that no Databases exist yet. Go ahead and create a new database
named test
.
Next, head over to the Data Explorer tab and follow this guide to setup a client driver auth key:
https://www.rethinkdb.com/docs/security/#securing-the-driver-port
The command to run should look something like this:
r.db('rethinkdb').table('cluster_config').get('auth').update({auth_key: 'clientauthkey'})
Now, let's connect this all together by having a client connect to the cluster through the proxy. I prefer Python so I've written this guide with that as an example.
We'll create a new container to run a Python shell with the RethinkDB client driver module installed:
docker run -it --rm \
-v rdb-tls:/tls \
--net rdb `# By adding the container to the network, we can connect to the proxy as 'rdb-proxy.rdb'` \
--name rdb-client \
python:alpine sh -c \
'pip install rethinkdb && python'
The first thing you should do once you get a Python shell prompt is this:
>>> import rethinkdb as r
>>> r.connect('rdb-proxy.rdb', 28015, auth_key='clientauthkey', ssl={'ca_certs': '/tls/ca.pem'}).repl()
<rethinkdb.net.DefaultConnection object at 0x7f7522167a58>
The above command connects to the RethinkDB proxy container with a setting for the Auth Key that we configured in the Web Admin Console and, most importantly, verifies the TLS connection with the proxy using the CA certificate from the volume we created earlier.
NOTE: While the Python client driver does not support TLS client authentication, the server and other client drivers may support it. This would allow you to forgo use of the Auth Key method for client authentication, opting instead for strong, mutual TLS authentication throughout your application.
From here, you should be able to use RethinkDB like you would normally.
Go ahead and follow the 10-Minute Guide with RethinkDB and Python:
https://www.rethinkdb.com/docs/guide/python/#create-a-new-table
If you've been following the guide, you should now have an authors
table in
the test
database which contains a few rows of data.
Now, let's reconfigure this table to increase the replica count to 3:
r.table('authors').reconfigure(shards=1, replicas=3).run()
In the Web Admin Console, you should now be able to see that the table has 3 replicas, across overy node in the cluster.
Keep your eye on the Web Admin Console. We're about to bring down a node! Pick
whichever server the Admin Console shows as the primary replica. My primary is
rdb_02
, so I'm going to stop the rdb-02
container (you can also 'control+C'
the terminal which you used to start that container):
docker stop rdb-02
You should soon see, via the Web Admin Console, that the cluster has reassigned another replica as the "Acting primary replica". Now, go ahead and restart the container that you just stopped:
docker start -a rdb-02
Watch the Web Admin Console again as the table recovers into a fully "Ready" state. Incredible! You should recommend RethinkDB to all of your friends and colleagues! And tell them to use TLS everywhere!