Nuxi The CloudABI Development Blog

Running a sandboxed copy of memcached

March 15, 2017 by Ed Schouten

Over the last year we’ve managed to port a fair number of interesting pieces of software to CloudABI. Last month we published an article on how we ported LevelDB. Two weeks later, Bitcoin developer Wladimir van der Laan amazed us by publishing an insightful article on his blog, sharing his personal experiences porting Bitcoin Core. In today’s article, let’s take a look at a piece of software that got ported to CloudABI quite some time ago, but didn’t get a lot of attention yet: memcached.

What is memcached?

Memcached is a fast and compact memory caching daemon. Memcached stores data in the form of key-value pairs a hash table, which, as the name suggests, is always stored in memory. Keys are required to be at most 250 bytes in size, while values are allowed to consume up to a megabyte of memory. Multiple instances of memcached can be grouped together by sharding the key space at the client side.

Memcached is a tool that is often used by web applications to cache parts of HTTP responses. For example, a news site can use memcached to store precomputed snippets (headlines, articles, comments), which are compiled into full pages when requested.

Porting memcached

Getting memcached ported over to CloudABI turned out to be relatively easy. Memcached is designed in such a way that it only communicates with the outside world over a very small number of channels, such as a TCP socket for processing incoming requests and a file descriptor to which to write log messages. This patch alters memcached to no longer create these file descriptors itself, but to allow them to be passed in using cloudabi-run.

In addition to this patch, we also had to make a small number of portability fixes. Many of these have already been sent upstream. Once these are integrated, it should remain easy for us to stay in sync with the latest official releases.

Running memcached

Getting CloudABI’s copy of memcached up and running should be fairly easy. First of all, you will need to install the x86_64-unknown-cloudabi-memcached package through CloudABI Ports, as that package provides a copy of the cross compiled executable. This executable can then be spawned as follows:

$ cat memcached.yaml
%TAG ! tag:nuxi.nl,2015:cloudabi/
---
socket: !socket
  bind: 0.0.0.0:11211
$ cloudabi-run /usr/local/x86_64-unknown-cloudabi/bin/memcached < memcached.yaml

Once memcached is up and running, it should be possible to connect to port 11211 and submit commands:

$ telnet localhost 11211
...
Connected to localhost.
Escape character is '^]'.
➥ get hello
END
➥ set hello 0 0 5
➥ world
STORED
➥ get hello
VALUE hello 0 5
world
END

What is nice about the configuration used to spawn memcached is that during its lifetime, it only allows memcached to interact with an already bound TCP socket. Even if an attacker manages to take over the execution of this process, he/she will effectively remain locked up within.

In practice you may want to start memcached with logging enabled as well. The configuration below shows how memcached can be started to log to stdout, with its verboseness set to level 2 (similar to invoking a stock copy of memcached with -vv).

$ cat memcached.yaml
%TAG ! tag:nuxi.nl,2015:cloudabi/
---
socket: !socket
  bind: 0.0.0.0:11211
verbose: 2
logfile: !fd stdout
$ cloudabi-run /usr/local/x86_64-unknown-cloudabi/bin/memcached < memcached.yaml
slab class   1: chunk size        96 perslab   10922
slab class   2: chunk size       120 perslab    8738
...
slab class  41: chunk size    771184 perslab       1
slab class  42: chunk size   1048576 perslab       1
<0 server listening (auto-negotiate)

Be sure to give CloudABI’s version of memcached a try and let us know what you think. Feel free to submit new issues on GitHub in case you run into things that can be improved. Thanks!