Posted on

Table of Contents

Introduction

Fediverse relays are like Internet Exchange Points (IXP) for the fediverse.

I've wanted to run a fediverse relay for a while now, but sadly it's been challenging for me to setup. The documentation out there is not robust. It seems there's not a lot of people like me who like to deploy this infrastructure then turn around and document their experieinces for others to learn from. I think that FOSS software devs should attempt to solicit feedback from people who have not used things like Cargo or Docker so that their documentation can be better.

After much research, I chose asonix's "ActivityPub relay in Rust", aka "AodeRelay". I like Rust a lot, and I think asonix has the best documentation for relay deployment. But I still struggled with it!

This article aims to lower the bar for new fediverse relay admins, and to enhance the overall experience for existing relay admins. Good documentation can help new users, which in turn expands the user base of a project. Good documentation also helps advanced users that want to verify they have setup their environment correctly, minimizing operational or security risks.

After many hours trying to understand and deploy AodeRelay, I now run an open relay @ relay.disobey.net!

Getting started with AodeRelay

Building AodeRelay from source is what allowed me to get a relay launched. The main reason for this is because building it from source gave me all of the local files and folders that appeared missing when I used Cargo directly. However, even though I built AodeRelay from source, I setup systemd to run it via Cargo. I document all of this below.

AodeRelay should have a reverse proxy infront of it, like nginx. This wasn't clear to me in the documentation. I was wondering why the documentation uses non-standard ports, like 8080, instead of 443. Running AodeRelay on a local IP (127.0.0.1) appears best -- no need to have TLS certs, nginx and Let's Encrypt can handle that.

My environment

I'm using Ubuntu server 22.04 LTS. Cargo (v1.80.1), nginx (v1.27.1), and certbot (1.21.0) are installed already.

Note, since my relay is running on bare metal, I'm using a ZFS pool for data redundancy and backups. I built AodeRelay from source in my /zpool directory.

Be sure your domain name is setup, both A and AAAA records. In my case, i'm using a sub-domain, relay.disobey.net.

AodeRelay configuration

I installed AodeRelay from source per the documentation: https://git.asonix.dog/asonix/relay/

Example user permissions

I made a new user and group that is the exclusive owner of the soon-to-be AodeRelay service, and made sure the working directory is also the new user's home directory. Be sure to update the home directory based on your expectations.

sudo adduser --system --home /zpool/relay --group --shell /usr/sbin/nologin aoderelay

sudo chown -R aoderelay:aoderelay /zpool/relay

sudo chmod 750 /zpool/relay

The .env file

cd /zpool/relay

$ sudo vim .env

HOSTNAME=relay.disobey.net
ADDR=127.0.0.1
PORT=8080
HTTPS=true
DEBUG=false
RESTRICTED_MODE=false
VALIDATE_SIGNATURES=true
PUBLISH_BLOCKS=false
CLIENT_TIMEOUT=10
DELIVER_CONCURRENCY=16
SIGNATURE_THREADS=16
LOCAL_BLURB="<p>This is a great place for publishing a Terms of Use to minimize abuse.</p>"
FOOTER_BLURB="Contact <a href=\"https://disobey.net/@yawnbox\">@yawnbox</a> for inquiries"
API_TOKEN="something_random_and_long_please_dont_use_this"

Please note the API_TOKEN. I used openssl to generate this from the local server's terminal. The following command will replace the existing API_TOKEN line in the .env file. That's all you need to do after making a backup of the file:

sudo cp .env .env.bak

sudo sed -i "s|^API_TOKEN=.*|API_TOKEN=\"$(openssl rand -base64 64 | tr -d '\n')\"|" .env

Since I am running on bare metal and have many CPU cores to work with, I upped the DELIVER_CONCURRENCY and SIGNATURE_THREADS to 16.

Example nginx .conf file

Create a new Let's Encrypt key pair for the new domain, which presumes you have Certbot installed. I purposefully want EC-384 certs.

sudo certbot certonly --key-type ecdsa --elliptic-curve secp384r1 -d relay.disobey.net

I use nginx v1.27, the latest release. You too can use this version with the following Ubuntu PPA:

sudo add-apt-repository ppa:ondrej/nginx-mainline

sudo apt install nginx -V

nginx -v

nginx version: nginx/1.27.1

Create a new nginx configuration file for the new sub-domain:

sudo vim /etc/nginx/conf.d/relay.disobey.net.conf

Please note I care a great deal about cryptography, so the following config is just what I use, enfocing only TLS 1.3 which works perfectly in the fediverse. Also please note HSTS with HSTS-preload is set in this configuration.

server {
    listen [2620:18c:0:192::253]:80;
    listen 103.232.207.253:80;
    server_name relay.disobey.net;
    return 301 https://$host$request_uri;
}

server {
    listen [2620:18c:0:192::253]:443 ssl;
    listen 103.232.207.253:443 ssl;
    http2 on;
    server_name relay.disobey.net;

    # Hardened TLS settings
    ssl_session_cache shared:le_nginx_SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.3;
    ssl_ecdh_curve X25519:secp384r1;
    ssl_conf_command Options PrioritizeChaCha;
    ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384;

    # Use the specified certificate paths
    ssl_certificate /etc/letsencrypt/live/relay.disobey.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/relay.disobey.net/privkey.pem;

    # Logging
    access_log /var/log/nginx/relay.access.log;
    error_log /var/log/nginx/relay.error.log;

    # Proxy settings
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Frame-Options SAMEORIGIN;
    add_header Content-Security-Policy "default-src 'self';";
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
}

Verify the configuration:

nginx -t

Restart nginx:

sudo service nginx restart

Example systemd relay service

Running a systemd relay service is ideal on Ubuntu. It allows the relay to be stopped, started, restarted, or to see the status of the relay trivially. It also allows me to further lock down the security of the service.

sudo vim /etc/systemd/system/relay.service

[Unit]
Description=AodeRelay Service
After=network.target

[Service]
User=aoderelay
Group=aoderelay
WorkingDirectory=/zpool/relay
ExecStart=/usr/bin/cargo run --release
Restart=on-failure
EnvironmentFile=/zpool/relay/.env
Environment=HOME=/zpool/relay
Environment=CARGO_HOME=/zpool/relay/.cargo
Environment=GIT_CONFIG=/zpool/relay/.gitconfig

# Security Enhancements
ProtectSystem=full
ProtectHome=true
NoNewPrivileges=true
PrivateTmp=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadOnlyPaths=/etc
ReadOnlyPaths=/usr
ReadOnlyPaths=/bin
ReadOnlyPaths=/sbin
ReadWritePaths=/zpool/relay
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
PrivateDevices=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
SystemCallFilter=@system-service

[Install]
WantedBy=multi-user.target

Enable and start the relay:

sudo systemctl daemon-reload

sudo systemctl start aoderelay.service

sudo systemctl enable aoderelay.service

View the status of the relay:

sudo systemctl status aoderelay.service

View the log:

sudo journalctl -u aoderelay.service

Making relay changes

If you want to update the blurb or change any other settings, all you have to do is make your changes then restart the relay service:

sudo service aoderelay restart