Strotmann.de
23 Dec 2017

Gophermoon 0.2 - a Gopher-Server in Lua – Part 2

What's in it today

I will not change the (minimal) Gopher server (this time), but will prepare the execution environment for Gophermoon: the result will be a container containing, only one executeable and one Lua-Script besides the Gopher content.

The container will be isolated from the network and the filesystem and process-space of the host machine.

So any coding errors I will create while expanding the Gophermoon-Server will have limited security impact on the host system (unless there is a security issue in the container code of the Linux-Kernel, which there have been in the past. Nothing is fully secure).

I'm testing on a RedHat EL 7 machine, but the same result should be possible on other modern Linux systems with a container manager (systemd-nspawn, docker, rkt, LXC …).

Building a static Lua

One feature that makes the Go programming language popular among users of Linux-Containers is the fact that Go produces static binaries. Static binaries have no code runtime dependencies, they are self contained and do not need to be installed, but can just copied around and "just work".

Lua is written in C, but we can create static binaries in C too.

It is recommended to compile the static Lua binary on an development machine, not on the production Gopher server.

For Red Hat based systems, we need to install few build tools and the static library files for the GNU-Libc:

yum install make gcc wget glibc-static

Next, we're downloading the Lua sourcecode from https://www.lua.org/download.html

mkdir ~/src
cd ~/src
wget https://www.lua.org/ftp/lua-5.3.4.tar.gz
tar xfz  lua-5.3.4.tar.gz
cd lua-5.3.4/src

The option -static lets the gcc compiler and linker build a static binary. Change the MYCFLAGS and MYLDFLAGS lines in Makefile:

[...]
MYCFLAGS= -static
MYLDFLAGS= -static
[...]

Now we can build the new Lua interpreter. I'm building the posix flavor, which is more generic than the Linux flavor and links less external libraries:

make posix
strip lua

The resulting lua binary should be a static ELF executable:

# file lua
lua: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=0631bb0d50ad1209dbf55f2b1dd0eb60fc388df7, stripped
# ls -lh lua
-rwxr-xr-x. 1 root root 1,7M 23. Dez 23:04 lua

Gophermoon inside a systemd-nspawn container

Next the container root directory is created and the static lua interpreter together with the gophermoon script is placed into that container directory:

mkdir -p /srv/gophermoon/bin
cp lua /srv/gophermoon/bin/
mv /usr/local/sbin/gophermoon /srv/gophermoon/bin/

Without timezone information files within the container directory, systemd-nspawn will complain about not being able to update the timezone in the container

Timezone <your-host-timezone> does not exist in container, not updating container timezone.

So far, I found no better way than to copy the timezone information into the container root (only one file is enough if you know the timezone of your host machine):

mkdir -p /srv/gophermoon/usr/share/zoneinfo
cp -a /usr/share/zoneinfo/* /srv/gophermoon/usr/share/zoneinfo/

A quick manual test starting the container from the commandline:

# /bin/systemd-nspawn -q --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon

iWelcome to GopherMoon @ defaultroutes.de               defaultroutes.de        70
i----------------------------------------               defaultroutes.de        70
.

Here is the updated Systemd-Service-Unit (/etc/systemd/system/gophermoon@.service):

[Unit]
Description=GopherMoon Gopher Server in La

[Service]
ExecStart=/bin/systemd-nspawn -q -x --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon
StandardInput=socket

The -x option creates an ephemeral container that is started from an BTRFS snapshot each time a new gopher connection comes in. The snapshot is destroyed as soon as the container terminates. An intruder will not be able to store new files into the container system.

At last we reload the new unit into Systemd and restart the Socket-Activation:

systemctl daemon-reload
systemctl restart gophermoon.socket

Now gophermoon runs in a (more) secure environment.

Other posts
Creative Commons License
strotmann.de by Carsten Strotmann is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License .