Do I like Foundry as a Virtual Table Top?

Yes, love it.

Do I recommend it to others?

Uh, well, it depends on your definition of “recommend” 😬

Some idle screaming-into-the-void on Mastodon prompted a conversation with the Old Scouser Roleplayer about my Foundry setup, and I realised I end up typically saying the same thing a lot about Foundry:

  1. It is a beautiful product, especically for crunchier games
  2. You will absolutely get distracted with doors, and sight lines and music, and audio effects, and away from the actual game
  3. It is an utter pain to use securely.

The word “securely” here is key. There have been some cracking vulnerabilities in the software already (fixed already!). The developers are moving fast and breaking things, and that’s great for making a beautiful VTT with some really novel features, but less good for boring, stuffy, security.

So I love Foundry, but I don’t trust it. Let me explain.

In the previous link, you can see the kinds of bugs that have existed in Foundry. The authentication is easiest to understand - it’s been possible to get into an instance and get admin capabilities without knowing the proper passwords. In the writeup, it turns out the developers had some very odd ideas about how to deal with passwords and hashing.

The second type of vulnerability there is a “directory traversal” which I will handwave over here and say “the Foundry software allowed an attacker to write to the filesystem where it shouldn’t have”. This can result in (at least) two things: your Foundry data getting corrupted/replaced, or something else on the computer running Foundry getting corrupted/replaced. Both of these are bad. The former is more likely due to Foundry needing permission to write to its own directories. It really shouldn’t be able to write anywhere else, if everything else is set up correctly.

So here’s what I did. I tried to reduce the attack surface of Foundry - hide what someone who wants to break my Foundry server can access.

  1. Turn the damn thing off when I’m not using it
  2. Don’t trust Foundry to keep inside its own data directories
  3. Don’t trust Foundry to be able to authenticate users

I use Docker and Apache to do both of these things, respectively. This is because I’m already running both of them, other servers and containers exist. If you’re not using them, explaining that software is beyond a single blog post.

Docker container

I use Mark Feldhousen’s excellent felddy/foundryvtt container, with docker-compose to keep all the options tidy. It has the nice feature that specifying e.g. image: felddy/foundryvtt:10 will grab the latest 10 at startup, but not upgrade you to 11.

I have a /srv/fvtt directory with this in it, and a data subdir which will be used for everything else.

version: "3.8"

services:
  foundry:
    image: felddy/foundryvtt:11
    hostname: my.cool.server.name
    init: true
    restart: "unless-stopped"
    volumes:
      - type: bind
        source: /srv/fvtt/data
        target: /data
    environment:
      - FOUNDRY_PASSWORD=aaa
      - FOUNDRY_USERNAME=bbb
      - FOUNDRY_ADMIN_KEY=ccc
      - FOUNDRY_MINIFY_STATIC_FILES=true
      - FOUNDRY_PROXY_SSL=true
      - FOUNDRY_HOSTNAME=my.cool.server.name
      - CONTAINER_CACHE=/data/container_cache
      - TIMEZONE=Antarctica
    ports:
      - target: 30000
        published: 30000
        protocol: tcp

You can update this automatically with Watchtower but I don’t, because I don’t leave Foundry running! Aside from some stupid errors when I set it up, it’s just worked. Now, a path traversal bug in Foundry will hit the root of the Docker container it’s running within. Additionally, code execution in Foundry would only see the Node process and nothing else. The only socket available is Apache.

There’s been only two downsides:

  1. It’s more difficult to bulk-upload stuff via SSH now as it needs moving into the container root which I’ve locked down a bit more
  2. Docker is great at using loads of disc space so it needs cleaning up as I don’t have a big drive on this server

Apache proxy

So, the purpose here is to provide a proxy to the FVTT engine, and enable HTTPS on top. LetsEncrypt will do the latter part for you, and will even set very strong options.

This part isn’t particularly unusual, the key bit are the Location tags for /join and /setup. They enable Apache’s authentication mechanism, something that I am pretty sure has had more security audits than Foundry. Basic authentication isn’t cryptographically secure, but it’s inside a TLS session, courtesy of the stuff before. Passwords are kept in the file location in the AuthUserFile, which is generated with a tool called htpasswd.

<IfModule mod_ssl.c>
<VirtualHost _default_:443>
        ServerAdmin webmaster@xxx
        ServerName my.cool.server.name

        LogLevel info
        ErrorLog ${APACHE_LOG_DIR}/error_fvtt.log

        CustomLog ${APACHE_LOG_DIR}/access_fvtt.log combined
        SSLEngine on
        # Turns the weak protocols off
        Include    /etc/letsencrypt/options-ssl-apache.conf

        # Do the actual Foundry proxying
        ProxyPreserveHost On
        ProxyPass "/socket.io/""ws://localhost:30000/socket.io/"
        ProxyPass       / http://localhost:30000/
        ProxyPassReverse / http://localhost:30000/

        <Location />
                LimitRequestBody 104857600
                # 100MB upload
        </Location>
        <Location /join>
                AuthType Basic
                AuthName "sekret-vtt"
                AuthBasicProvider file
                AuthUserFile /etc/apache2/fvtt.passwd
                Require valid-user
        </Location>
        <Location /setup>
                AuthType Basic
                AuthName "sekret-vtt"
                AuthBasicProvider file
                AuthUserFile /etc/apache2/fvtt.passwd
                Require user "sekret-admin"
        </Location>
        SSLCertificateFile /etc/letsencrypt/live/my.cool.server.name/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/my.cool.server.name/privkey.pem

<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
</IfModule>
</VirtualHost>
</IfModule>

I create the file and have two users, gm and player. One could have separate player accounts here, but as I trust my players, they can share a password to get to the FVTT join page, and then choose their own user from the drop-down.

htpasswd -c /etc/apache2/fvtt.passwd gm
htpasswd  /etc/apache2/fvtt.passwd player

This is a pain for users that don’t use password managers, they have two logins to use Foundry. However Bitwarden will make that first one just vanish.

Conclusion

This is why I don’t recommend Foundry mindlessly, this was easy for me off the back of doing stuff like this most days of the week. For my less technical friends, it’s very easily to shoot yourself in the foot, digitally speaking.

This doesn’t also protect against any vulnerability in Foundry that you can hit directly from another end-point without going through the two I’ve secured already. I’ve been a bit too lazy to actually look in a browser’s developer console and check what else could be secured.

Any bright ideas on improving this further, hit me up on Mastodon.