Skip to content

Instantly share code, notes, and snippets.

@nightlyworker
Created August 31, 2016 02:12
Show Gist options
  • Save nightlyworker/3a9f385e884f850382351054ddf3ecec to your computer and use it in GitHub Desktop.
Save nightlyworker/3a9f385e884f850382351054ddf3ecec to your computer and use it in GitHub Desktop.
RethinkDB TLS Guide

RethinkDB with TLS

Thinker Lock

What You'll Need

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).

Create a TLS Key Pair

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

Create a RethinkDB Network

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

Create the First Node in the Cluster

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

Join a Second Node to the Cluster

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

Join a Third Node to the Cluster

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

Add a RethinkDB Proxy

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

Connect to the RethinkDB Administration Console in your Browser

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.

Setup a Test Database and Driver Auth Key

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'})

Connect a Client Driver to the Cluster

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

Scale the "authors" Table Across the Cluster

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.

Test Cluster Resiliency

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment