Public Repository

Last pushed: 2 years ago
Short Description
Short description is empty for this repo.
Full Description

RethinkDB with TLS

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!

Docker Pull Command
Owner
jlhawn