mallory.computer

HOWTO: Install Transmission on FreeBSD 14.0

In this HOWTO we'll install Transmission on a Raspberry Pi. We'll run Transmission behind nginx which will have a TLS certificate provided by certbot/Let's Encrypt. Transmission will communicate with the internet through a WireGuard VPN, and Transmission will not be allowed to leak when the VPN goes down thanks to PF. In order to grant a TLS certificate to the server we'll need a domain name and the host will need an IPv6 GUA.

Setting up the Raspberry Pi

  • Set up a domain or subdomain to point at your Raspberry Pi's globally routable IPv6 address (we'll use example.invalid in this HOWTO)
  • Set up your router's firewall to pass in TCP packets on port 80 bound for your Raspberry Pi's IPv6 address (port 80 will only be used to redirect to HTTPS and to renew certificates, so it will be safe to leave open)
  • Burn FreeBSD 14.0 to MicroSD card, insert into the Raspberry Pi, attach ethernet and power (guide to burning media on FreeBSD)
  • ssh-copy-id freebsd@example.invalid (the password is freebsd)
  • scp my-wireguard-config.conf freebsd@example.invalid
  • ssh freebsd@example.invalid
    • passwd (change your password to something other than freebsd)
    • su (the password is root)
    • mkdir -p /usr/local/etc
    • echo permit nopass :wheel > /usr/local/etc/doas.conf
    • pkg install -y doas

Basic setup

From now, all commands will assume you're logged into the Pi as user freebsd.

  • doas pkg install -y ca_root_nss
  • doas sysrc hostname=hostname
  • doas sysrc ntpd_enable=YES ntpd_sync_on_start=YES
  • echo 'name_servers="2620:fe::fe 9.9.9.10"' | doas tee /etc/resolvconf.conf
  • echo UseDNS no | doas tee -a /etc/ssh/sshd_config
  • doas mkdir -p /store/www /store/downloads
  • ln -s /store/downloads

Set up nginx & Let's Encrypt

  • doas pkg install -y nginx py39-certbot-nginx
  • doas sysrc nginx_enable=YES
  • doas sysrc -f /etc/periodic.conf weekly_certbot_enable=YES weekly_certbot_service=wireguard
  • cat <<EOF | doas tee /store/www/index.html
    <title>example.invalid</title>
    <link rel="icon" href="/transmission/web/images/favicon.png">
    <h1>example.invalid</h1>
    <ul>
    <li><a href="/transmission/web/">Transmission</a></li>
    <li><a href="/downloads/">Downloads</a></li>
    </ul>
    EOF
  • cat <<EOF | doas tee /usr/local/etc/nginx/nginx.conf
    worker_processes  1;
    events {
      worker_connections  1024;
    }
    http {
      include mime.types;
      default_type application/octet-stream;
      sendfile on;
      keepalive_timeout 65;
      gzip on;
      server {
        listen [::]:80;
        server_name example.invalid;
        location / {
          alias /store/www/;
          index index.html;
        }
        location /downloads/ {
          alias /store/downloads/;
          autoindex on;
          autoindex_exact_size off;
        }
        location /transmission/ {
          proxy_pass http://127.0.0.1:9091;
        }
      }
    }
    EOF
  • doas certbot --nginx -d example.invalid --email=your-email@example.invalid --agree-tos --no-eff-email
    You should see the following message:
    Deploying certificate
    Successfully deployed certificate for example.invalid to /usr/local/etc/nginx/nginx.conf
    Congratulations! You have successfully enabled HTTPS on https://example.invalid
    If you don't you should stop to investigate.

Set up Transmission

  • doas pkg install -y transmission-daemon
  • doas chown transmission:transmission /store/downloads
  • doas sysrc transmission_download_dir=/store/downloads transmission_enable=YES

Set up WireGuard

  • doas pkg install -y wireguard-tools
  • doas cp my-wireguard-config.conf /usr/local/etc/wireguard/wg0.conf
  • doas sysrc wireguard_interfaces=wg0 wireguard_enable=YES
  • doas service wireguard start
  • Test that your IP addresses have changed: fetch --quiet --output=- http://ip6only.me/api/ && fetch --quiet --output=- http://ip4only.me/api/

Set up PF

  • cat <<EOF | doas tee /etc/pf.conf
    set skip on lo0
    
    # Block all incoming connections from wg0
    block in on wg0
    
    # Prevent leaks from Transmission
    block user transmission
    pass on wg0 user transmission
    
    # But allow other users to do anything
    pass user != transmission
    EOF

Let's test that pf works correctly:

  • doas service pf onestart
  • If pf prevents you interacting over ssh, reboot it by pulling out unplugging the power cable and plugging it back in again.
  • doas service wireguard stop
  • doas -u transmission fetch --quiet --output=- http://ip6only.me/api/ ; doas -u transmission fetch --quiet --output=- http://ip4only.me/api/
  • You should see Host does not resolve twice.
  • doas service wireguard start
  • doas -u transmission fetch --quiet --output=- http://ip6only.me/api/ ; doas -u transmission fetch --quiet --output=- http://ip4only.me/api/
  • You should see the VPN's IP addresses.

Once you're satisfied that it's working correctly (and you've verified that you're not being blocked from sshing in), we can enable pf on boot:

  • doas sysrc pf_enable=YES

Login info & shell scripts

  • echo 'Welcome to hostname!' | doas tee /etc/motd.template
  • echo uptime > .login
  • cat <<EOF | tee test.sh
    #!/bin/sh
    set -e
    
    whoami
    fetch --quiet --output=- http://ip6only.me/api/ | cut -d, -f2
    fetch --quiet --output=- http://ip4only.me/api/ | cut -d, -f2
    
    doas -u transmission whoami
    doas -u transmission fetch --quiet --output=- http://ip6only.me/api/ | cut -d, -f2
    doas -u transmission fetch --quiet --output=- http://ip4only.me/api/ | cut -d, -f2
    EOF
  • chmod 755 test.sh

Reboot & done

  • doas reboot
  • Visit https://example.invalid/ to see your server with links to Transmission and the downloads directory.

Now you're ready to download your favorite BSD without every other BitTorrent peer knowing your IP address!

Transmission's web interface showing FreeBSD 13.2 seeding