Blame doc/tls.txt

Packit 4e8bc4
Securing Memcached with TLS
Packit 4e8bc4
Packit 4e8bc4
Requirements
Packit 4e8bc4
------------
Packit 4e8bc4
We are required to encrypt Memcached network traffic as we deploy our servers in public cloud
Packit 4e8bc4
environments. We decided to implement SSL/TLS for TCP at the network layer of Memcached
Packit 4e8bc4
using OpenSSL libraries. This provides following benefits with the expense of added latency
Packit 4e8bc4
and reduced throughput (to be quantified).
Packit 4e8bc4
Packit 4e8bc4
# Encryption :Data is encrypted on the wire between Memcached client and server.
Packit 4e8bc4
# Authentication : Optionally, both server and client authenticate each other.
Packit 4e8bc4
# Integrity: Data is not tampered or altered when transmitted between client and server
Packit 4e8bc4
Packit 4e8bc4
Following are a few additional features.
Packit 4e8bc4
# Certificate refresh: when the server gets a new certificate, new connections
Packit 4e8bc4
will use new certificates without a need of re-starting the server process.
Packit 4e8bc4
Packit 4e8bc4
# Multiple ports with and without TLS : by default all TCP ports are secured. Optionally we can setup
Packit 4e8bc4
the server to secure a specific TCP port.
Packit 4e8bc4
Packit 4e8bc4
Note that initial implementation does not support session resumption or renegotiation.
Packit 4e8bc4
Packit 4e8bc4
Design
Packit 4e8bc4
------
Packit 4e8bc4
We experimented two options for implementing TLS, with SSL buffered events and directly using
Packit 4e8bc4
OpenSSL API.
Packit 4e8bc4
Packit 4e8bc4
Bufferevents can use the OpenSSL library to implement SSL/TLS. Our experiment used
Packit 4e8bc4
a socket-based bufferevent that tells OpenSSL to communicate with the network directly over.
Packit 4e8bc4
Unlike a worker thread sets callback on the socket, this uses a “bufferevent” object for
Packit 4e8bc4
callbacks. Memcached still has to setup the SSL Context but SSL handshake and object
Packit 4e8bc4
management is done via the “bufferevent_” API. While this was fairly easy to implement,
Packit 4e8bc4
we noticed a higher memory usage as we don’t have much control over allocating evbuffer
Packit 4e8bc4
objects in bufferevents. More over there is a discussion on removing the libevent dependency
Packit 4e8bc4
from Memcached; hence this option was not chosen.
Packit 4e8bc4
Packit 4e8bc4
OpenSSL library provides APIs for us to directly read/write from a socket. With this option,
Packit 4e8bc4
we create an SSL Context and many SSL objects. The SSL Context object, created at the process level,
Packit 4e8bc4
holds certificates, a private key, and options regarding the TLS protocol and algorithms.
Packit 4e8bc4
SSL objects, created at the connection level, represents SSL sessions. SSL objects are responsible
Packit 4e8bc4
for encryption, and session handshake among other things.
Packit 4e8bc4
Packit 4e8bc4
There are two ways to do network IO over TLS, either only use SSL_read/SSL_write with a network socket or
Packit 4e8bc4
use the API along with an output/input buffer pair. These buffers are referred as BIO
Packit 4e8bc4
(Basic Input Output) buffers.
Packit 4e8bc4
Packit 4e8bc4
We started with the first option, create SSL objects with the socket and only interact with SSL_read/SSL_write.
Packit 4e8bc4
Packit 4e8bc4
  +------+                                    +-----+
Packit 4e8bc4
  |......|--> read(fd) --> BIO_write(rbio) -->|.....|--> SSL_read(ssl)  --> IN
Packit 4e8bc4
  |......|                                    |.....|
Packit 4e8bc4
  |.sock.|                                    |.SSL.|
Packit 4e8bc4
  |......|                                    |.....|
Packit 4e8bc4
  |......|<-- write(fd) <-- BIO_read(wbio) <--|.....|<-- SSL_write(ssl) <-- OUT
Packit 4e8bc4
  +------+                                    +-----+
Packit 4e8bc4
          |                                  |       |                     |
Packit 4e8bc4
          |<-------------------------------->|       |<------------------->|
Packit 4e8bc4
          |         encrypted bytes          |       |  unencrypted bytes  |
Packit 4e8bc4
Packit 4e8bc4
                      Figure 1 : Network sockets, BIO buffers and SSL_read/SSL_write
Packit 4e8bc4
Packit 4e8bc4
(reference:  https://gist.github.com/darrenjs/4645f115d10aa4b5cebf57483ec82eca)
Packit 4e8bc4
Packit 4e8bc4
Memcached uses non blocking sockets and implements a rather complex state machine for
Packit 4e8bc4
network IO. A listener thread does the TCP handshake and initiates the SSL handshake after
Packit 4e8bc4
creating an SSL object based on the SSL Context object of the server. If there are no
Packit 4e8bc4
fatal errors, the listener thread hands over the socket to a worker thread. A worker completes
Packit 4e8bc4
the SSL handshake.
Packit 4e8bc4
Packit 4e8bc4
-----------                       ----------------------
Packit 4e8bc4
          |                       |
Packit 4e8bc4
  Client  |                       |  Memcached Server
Packit 4e8bc4
          |                       |
Packit 4e8bc4
          |                       |---------------------
Packit 4e8bc4
          |                       |   Listener thread  |
Packit 4e8bc4
          |     TCP connect       |                    |
Packit 4e8bc4
          |---------------------> | (accept)           |
Packit 4e8bc4
          |    ClientHello        |                    |
Packit 4e8bc4
          |---------------------> | (SSL_accept)       |
Packit 4e8bc4
          |                       |                    |
Packit 4e8bc4
          |    ServerHello and    |                    |
Packit 4e8bc4
          |    Certificate,       |                    |
Packit 4e8bc4
          |    ServerHelloDone    |                    |
Packit 4e8bc4
          | <---------------------|                    |
Packit 4e8bc4
          |                       |---------------------
Packit 4e8bc4
          |                       |         |
Packit 4e8bc4
          |                       |         V
Packit 4e8bc4
          |                       |-------------------
Packit 4e8bc4
          |                       |  Worker thread   |
Packit 4e8bc4
          | ClientKeyExchange,    |                  |
Packit 4e8bc4
          | ChangeCipherSpec,     |                  |
Packit 4e8bc4
          | Finished              |                  |
Packit 4e8bc4
          |---------------------> | (SSL_read)       |
Packit 4e8bc4
          |                       |                  |
Packit 4e8bc4
          |                       |                  |
Packit 4e8bc4
          | NewSessionTicket,     |                  |
Packit 4e8bc4
          | ChangeCipherSpec,     |                  |
Packit 4e8bc4
          | Finished              |                  |
Packit 4e8bc4
          | <---------------------|                  |
Packit 4e8bc4
          |                       |                  |
Packit 4e8bc4
          | Memcached request/    |                  |
Packit 4e8bc4
          |    response           |                  |
Packit 4e8bc4
          | <-------------------> | (SSL_read/       |
Packit 4e8bc4
          |                       |   SSL_write)     |
Packit 4e8bc4
-----------                       -------------------------
Packit 4e8bc4
Packit 4e8bc4
                      Figure 2 : The initial SSL handshake
Packit 4e8bc4
Packit 4e8bc4
Packit 4e8bc4
Setting-up callbacks when the socket is ready for reading/writing is the same
Packit 4e8bc4
for both TLS and non-TLS connections. When the socket is ready, the state machine kicks off
Packit 4e8bc4
and issues a SSL_read/ SSL_write. Note that we implement a SSL_sendmsg wrapper on top of
Packit 4e8bc4
SSL_write to simulate the sendmsg API.
Packit 4e8bc4
This way we don't explicitly use BIO buffers or do BIO_write/BIO_read, but let OpenSSL
Packit 4e8bc4
library to do it on our behalf. Existing state machine takes care of reading the correct amount
Packit 4e8bc4
of bytes and do the error handling when needed.
Packit 4e8bc4
Packit 4e8bc4
As a best practice, server certificates and keys are periodically refreshed by the PKI.
Packit 4e8bc4
When this happens we want server to use the new certificate without restarting the process.
Packit 4e8bc4
Memcached is a cache and restarting servers affects the latency of applications. We implement
Packit 4e8bc4
the automatic certificate refresh through a command. Upon receiving the "refresh_certs" command,
Packit 4e8bc4
the server reloads the certificates and key to the SSL Context object. Existing connection won't be
Packit 4e8bc4
interrupted but new connections will use the new certificate.
Packit 4e8bc4
Packit 4e8bc4
We understand not all users want to use TLS or have the OpenSSL dependency. Therefore
Packit 4e8bc4
it's an optional module at the compile time. We can build a TLS capable Memcached server with
Packit 4e8bc4
"./configure --enable-tls". Once the server is built with TLS support, we can enabled it with
Packit 4e8bc4
"-Z" flag or "--enable-ssl". Certificate (-o ssl_chain_cert) and (-o ssl_key) are required
Packit 4e8bc4
parameters while others are optional. Supported options can be listed through "memcached -h".
Packit 4e8bc4
Packit 4e8bc4
Developers need to have libio-socket-ssl-perl installed for running unit tests. When the server is
Packit 4e8bc4
built with TLS support, we can use "test_tls" make target to run all existing tests over TLS and some
Packit 4e8bc4
additional TLS specific tests. The minimum required OpenSSL version is 1.1.0g.