Box

Setup for my personal (pet) server.

Contributing

Found an error or have a suggestion? Please open an issue on GitHub (github.com/pojntfx/box)!

If you like the docs, a GitHub star is always appreciated :)

License

AGPL-3.0 license badge

Box (c) 2022 Felicitas Pojtinger and contributors

SPDX-License-Identifier: AGPL-3.0

Linux

Debian

Debian, also known as Debian GNU/Linux, is a GNU/Linux distribution composed of free and open-source software, developed by the community-supported Debian Project, which was established by Ian Murdock on August 16, 19931.

First, download and flash a prebuilt vanilla Debian image for your Raspberry Pi model:

Adjust /dev/sda to the path of your SD card.

# Raspberry Pi Zero (W)/1
curl -L 'https://raspi.debian.net/tested/20220121_raspi_1_bullseye.img.xz' | xzcat >/tmp/debian.img
# Raspberry Pi 2
curl -L 'https://raspi.debian.net/tested/20220121_raspi_2_bullseye.img.xz' | xzcat >/tmp/debian.img
# Raspberry Pi 3
curl -L 'https://raspi.debian.net/tested/20220121_raspi_3_bullseye.img.xz' | xzcat >/tmp/debian.img
# Raspberry Pi 4/400
curl -L 'https://raspi.debian.net/tested/20220121_raspi_4_bullseye.img.xz' | xzcat >/tmp/debian.img

sudo umount /dev/sda{,0,1,2}

sudo dd if=/tmp/debian.img of=/dev/sda bs=4M status=progress

sync
sudo umount /dev/sda{,0,1,2}

For vanilla Debian, you can now mount the SD card and set the root password to a random string, add your authorized_keys and set the hostname like so:

sudo umount /dev/sda{,0,1,2}

sudo mkdir -p /mnt/raspi-boot
sudo mount /dev/sda1 /mnt/raspi-boot

sudo tee -a /mnt/raspi-boot/sysconf.txt<<EOT
root_pw=$(openssl rand -base64 12)
root_authorized_key=$(cat ~/.ssh/id_rsa.pub)
hostname=jeans-box
EOT

sync
sudo umount /dev/sda{,0,1,2}

Raspbian

Raspberry Pi OS (formerly Raspbian) is a Debian-based operating system for Raspberry Pi2.

Alternatively, you may want to download and flash Raspbian instead of vanilla Debian by using the Raspberry Pi Imager. You can set the options mentioned above using the imager GUI; don't configure WLAN using it, as we'll use a static method instead.

1

From Wikipedia, last checked 2022-02-19 (https://en.wikipedia.org/wiki/Debian) 2: From Wikipedia, last checked 2022-03-07 (https://en.wikipedia.org/wiki/Raspberry_Pi_OS)

Wireless Networking

Debian

Adjust the IPv6 address, SSID and PSK to your own values.

sudo umount /dev/sda{,0,1,2}

sudo mkdir -p /mnt/raspi-root
sudo mount /dev/sda2 /mnt/raspi-root

# For a static IPv6 address
sudo tee /mnt/raspi-root/etc/network/interfaces.d/wlan0<<'EOT'
allow-hotplug wlan0

iface wlan0 inet dhcp

iface wlan0 inet6 static
    address 2003:ef:1f01:7f00:76c9:d842:c369:8bf2
    autoconf 1
    accept_ra 2

    wpa-ssid myssid
    wpa-psk mypsk
EOT

# For a dynamic IPv6 address
sudo tee /mnt/raspi-root/etc/network/interfaces.d/wlan0<<'EOT'
allow-hotplug wlan0

iface wlan0 inet dhcp

iface wlan0 inet6 auto
    wpa-ssid myssid
    wpa-psk mypsk
EOT

sync
sudo umount /dev/sda{,0,1,2}

Raspbian

Adjust the IPv6 address, SSID and PSK to your own values.

sudo umount /dev/sda{,0,1,2}

sudo mkdir -p /mnt/raspi-{boot,root}
sudo mount /dev/sda1 /mnt/raspi-boot
sudo mount /dev/sda2 /mnt/raspi-root

sudo tee /mnt/raspi-boot/wpa_supplicant.conf<<'EOT'
country=de
update_config=1
ctrl_interface=/var/run/wpa_supplicant

network={
    scan_ssid=1
    ssid="myssid"
    psk="mypsk"
}
EOT

# For a static IPv6 address
sudo tee /mnt/raspi-root/etc/network/interfaces.d/wlan0<<'EOT'
allow-hotplug wlan0

iface wlan0 inet dhcp

iface wlan0 inet6 static
    address 2003:ef:1f01:7f00:76c9:d842:c369:8bf2
    autoconf 1
    accept_ra 2
EOT

# For a dynamic IPv6 address
sudo tee /mnt/raspi-root/etc/network/interfaces.d/wlan0<<'EOT'
allow-hotplug wlan0

iface wlan0 inet dhcp

iface wlan0 inet6 auto
EOT

sync
sudo umount /dev/sda{,0,1,2}

Wired Networking

Adjust the IPv6 address to your own value.

sudo umount /dev/sda{,0,1,2}

sudo mkdir -p /mnt/raspi-root
sudo mount /dev/sda2 /mnt/raspi-root

# For a static IPv6 address
sudo tee /mnt/raspi-root/etc/network/interfaces.d/eth0<<'EOT'
auto eth0

iface eth0 inet dhcp

iface eth0 inet6 static
    address 2003:ef:1f01:7f00:76c9:d842:c369:8bf3
    autoconf 1
    accept_ra 2
EOT

# For a dynamic IPv6 address
sudo tee /mnt/raspi-root/etc/network/interfaces.d/eth0<<'EOT'
auto eth0

iface eth0 inet dhcp

iface eth0 inet6 auto
EOT

sync
sudo umount /dev/sda{,0,1,2}

DNS

sudo umount /dev/sda{,0,1,2}

sudo mkdir -p /mnt/raspi-root
sudo mount /dev/sda2 /mnt/raspi-root

sudo tee /mnt/raspi-root/etc/resolv.conf<<'EOT'
nameserver 2606:4700:4700::1111
nameserver 2606:4700:4700::1001
nameserver 1.1.1.1
nameserver 1.0.0.1
EOT
sudo chattr +i /mnt/raspi-root/etc/resolv.conf

sudo sed -i /mnt/raspi-root/etc/hosts -e 's/\tlocalhost/\tlocalhost jeans-box/g'

sync
sudo umount /dev/sda{,0,1,2}

You can now also add the AAAA records for the device to your DNS zone (assuming that you've chosen a static IPv6 address):

Adjust the IPv6 address to either the address you gave the wireless or wired network interface previously.

jeans-box   10800   IN  AAAA    2003:ef:1f01:7f00:76c9:d842:c369:8bf2
*.jeans-box 10800   IN  AAAA    2003:ef:1f01:7f00:76c9:d842:c369:8bf2

SSH

The Secure Shell Protocol (SSH) is a cryptographic network protocol for operating network services securely over an unsecured network1.

Access the server:

# Debian
ssh root@jeans-box.alphahorizon.io
# Raspbian
ssh -tt pi@jeans-box.alphahorizon.io sudo su -l

If your Raspberry Pi doesn't have a static IPv6 address, you can connect to it by using jeans-box instead of jeans-box.alphahorizon.io, e.g. by using ssh root@jeans-box, which uses mDNS to resolve the Raspberry Pi's address.

Create the user:

echo "LC_ALL=en_US.UTF-8" | tee -a /etc/environment
echo "en_US.UTF-8 UTF-8" | tee /etc/locale.gen
echo "LANG=en_US.UTF-8" | tee /etc/locale.conf
locale-gen en_US.UTF-8

adduser jean
su jean -c "mkdir -m 700 -p ~/.ssh && curl 'https://github.com/jean.keys' | tee -a ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
usermod -aG sudo jean

# Only on Raspbian: Delete the default `pi` user
sh -c 'killall -u pi && deluser --remove-home pi'

Setup SSH:

ssh jean@jeans-box.alphahorizon.io

sudo apt update
sudo apt install -y sudo curl openssh-server locales
sudo systemctl enable --now ssh

echo 'PermitRootLogin no' | sudo tee /etc/ssh/ssh_config.d/no-root.conf

sudo passwd -d root
sudo passwd -l root
sudo chsh -s /sbin/nologin
sudo rm -f /root/.ssh/authorized_keys

sudo systemctl restart ssh
1

From Wikipedia, last checked 2022-02-19 (https://en.wikipedia.org/wiki/Secure_Shell)

firewalld

firewalld is a firewall management tool for Linux operating systems1.

ssh jean@jeans-box.alphahorizon.io

sudo apt update
sudo apt install -y firewalld

sudo systemctl enable --now firewalld

sudo firewall-cmd --zone=public --add-interface=wlan0 --permanent
sudo firewall-cmd --zone=public --add-interface=eth0 --permanent

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=mdns
sudo firewall-cmd --permanent --add-service=llmnr

sudo firewall-cmd --reload
1

From Wikipedia, last checked 2022-02-19 (https://en.wikipedia.org/wiki/Firewalld)

APT

Advanced package tool, or APT, is a free-software user interface that works with core libraries to handle the installation and removal of software on Debian, and Debian-based Linux distributions1.

ssh jean@jeans-box.alphahorizon.io

sudo apt update
sudo apt install -y unattended-upgrades

sudo tee /etc/apt/apt.conf.d/50unattended-upgrades <<'EOT'
Unattended-Upgrade::Origins-Pattern {
  "origin=*";
}
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
EOT

sudo systemctl enable --now unattended-upgrades

sudo unattended-upgrades --debug
1

From Wikipedia, last checked 2022-02-19 (https://en.wikipedia.org/wiki/APT_(software))

Podman

Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System1.

ssh jean@jeans-box.alphahorizon.io

echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_11/ /' | sudo tee /etc/apt/sources.list.d/libcontainers.list
curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_11/Release.key" | sudo apt-key add -

sudo apt update
sudo apt upgrade -y # Prevent conflicts with eventual prior Podman install from Debian repos
sudo apt install -t Debian_11 -y podman
sudo apt install -y containernetworking-plugins

echo 'unqualified-search-registries=["docker.io"]' | sudo tee /etc/containers/registries.conf.d/docker.conf

sudo systemctl unmask podman-auto-update.service
sudo systemctl unmask podman-auto-update.timer

sudo systemctl enable --now podman-auto-update.timer
sudo systemctl enable --now podman-restart
1

From the Podman website, last checked 2022-02-19 (https://podman.io/)

Traefik

Traefik (pronounced traffic) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy1.

ssh jean@jeans-box.alphahorizon.io

sudo mkdir -p /etc/traefik
sudo tee /etc/traefik/traefik.yaml<<'EOT'
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          to: websecure
          scheme: https

  websecure:
    address: ":443"

  sshalt:
    address: ":2222"

  websecurealt:
    address: ":8443"

providers:
  file:
    filename: /etc/traefik/services.yaml
    watch: true

api:
  dashboard: true

certificatesResolvers:
  letsencrypt:
    acme:
      email: jean@example.com
      storage: /var/lib/traefik/acme.json
      httpChallenge:
        entryPoint: web

log:
  level: INFO
EOT

sudo tee /etc/traefik/services.yaml<<'EOT'
tcp:
  routers:
      ssh:
      entryPoints:
        - websecurealt
      rule: HostSNI(`*`)
      service: ssh
    giteaSSH:
      entryPoints:
        - sshalt
      rule: HostSNI(`*`)
      service: giteaSSH
    sshOverTLS:
      entryPoints:
        - websecure
      rule: HostSNI(`ssh.jeans-box.alphahorizon.io`)
      service: ssh
      tls:
        certResolver: letsencrypt
        domains:
          - main: ssh.jeans-box.alphahorizon.io
  services:
    ssh:
      loadBalancer:
        servers:
          - address: localhost:22
    giteaSSH:
      loadBalancer:
        servers:
          - address: localhost:3022

http:
  routers:
    dashboard:
      rule: Host(`traefik.jeans-box.alphahorizon.io`)
      tls:
        certResolver: letsencrypt
        domains:
          - main: traefik.jeans-box.alphahorizon.io
      service: api@internal
      entryPoints:
        - websecure
      middlewares:
        - dashboard
    cockpit:
      rule: Host(`cockpit.jeans-box.alphahorizon.io`)
      tls:
        certResolver: letsencrypt
        domains:
          - main: cockpit.jeans-box.alphahorizon.io
      service: cockpit
      entryPoints:
        - websecure
    gitea:
      rule: Host(`gitea.jeans-box.alphahorizon.io`)
      tls:
        certResolver: letsencrypt
        domains:
          - main: gitea.jeans-box.alphahorizon.io
      service: gitea
      entryPoints:
        - websecure
    dex:
      rule: Host(`dex.jeans-box.alphahorizon.io`)
      tls:
        certResolver: letsencrypt
        domains:
          - main: dex.jeans-box.alphahorizon.io
      service: dex
      entryPoints:
        - websecure
    liwasc:
      rule: Host(`liwasc.jeans-box.alphahorizon.io`)
      tls:
        certResolver: letsencrypt
        domains:
          - main: liwasc.jeans-box.alphahorizon.io
      service: liwasc
      entryPoints:
        - websecure
    bofied:
      rule: Host(`bofied.jeans-box.alphahorizon.io`)
      tls:
        certResolver: letsencrypt
        domains:
          - main: bofied.jeans-box.alphahorizon.io
      service: bofied
      entryPoints:
        - websecure

  middlewares:
    dashboard:
      basicauth:
        users:
          - "jean:$apr1$dYdt8Zrl$TsEfzaedPGyjdrDk8EfRN." # htpasswd -nb jean asdf

  services:
    cockpit:
      loadBalancer:
        serversTransport: cockpit
        servers:
          - url: https://localhost:9090
    gitea:
      loadBalancer:
        servers:
          - url: http://localhost:3000
    dex:
      loadBalancer:
        servers:
          - url: http://localhost:5556
    liwasc:
      loadBalancer:
        servers:
          - url: http://localhost:15124
    bofied:
      loadBalancer:
        servers:
          - url: http://localhost:15256

  serversTransports:
    cockpit:
      insecureSkipVerify: true
EOT

sudo mkdir -p /var/lib/traefik

sudo podman run -d --restart=always --label "io.containers.autoupdate=image" --net=host -v /var/lib/traefik/:/var/lib/traefik -v /etc/traefik/:/etc/traefik --name traefik traefik
sudo podman generate systemd --new traefik | sudo tee /lib/systemd/system/traefik.service

sudo systemctl daemon-reload
sudo systemctl enable --now traefik

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=8443/tcp
sudo firewall-cmd --reload

curl -Lu jean:asdf https://traefik.jeans-box.alphahorizon.io/ # Test the Traefik dashboard
ssh -p 8443 jean@jeans-box.alphahorizon.io # Test SSH over TCP
ssh -o ProxyCommand="openssl s_client -connect ssh.jeans-box.alphahorizon.io:443 -quiet" jean # Test SSH over TLS
1

From GitHub, last checked 2022-02-19 (https://github.com/traefik/traefik)

Cockpit

Cockpit is a web-based graphical interface for servers1.

ssh jean@jeans-box.alphahorizon.io

echo 'deb http://deb.debian.org/debian bullseye-backports main' | sudo tee /etc/apt/sources.list.d/backports.list
sudo apt update
sudo apt install -t bullseye-backports -y cockpit cockpit-podman cockpit-pcp

curl https://cockpit.jeans-box.alphahorizon.io/ # Test Cockpit
1

From the Cockpit website, last checked 2022-02-19 (https://cockpit-project.org/)

Gitea

Gitea is an open-source forge software package for hosting software development version control using Git as well as other collaborative features like bug tracking, wikis and code review1.

ssh jean@jeans-box.alphahorizon.io

sudo mkdir -p /var/lib/gitea

sudo podman run -d --restart=always --label "io.containers.autoupdate=image" --net slirp4netns:allow_host_loopback=true,enable_ipv6=true -p 3000:3000 -p 3022:22 -v /var/lib/gitea/:/data -v /etc/timezone:/etc/timezone:ro -v /etc/localtime:/etc/localtime:ro -e 'USER_UID=1000' -e 'USER_GID=1000' --name gitea gitea/gitea
sudo podman generate systemd --new gitea | sudo tee /lib/systemd/system/gitea.service

sudo systemctl daemon-reload
sudo systemctl enable --now gitea

sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload

curl https://gitea.jeans-box.alphahorizon.io/ # Test Gitea

Visit https://gitea.jeans-box.alphahorizon.io/ and run the Wizard; use the following values:

  • Site Title: Jean's Gitea
  • SSH Server Domain: gitea.jeans-box.alphahorizon.io
  • SSH Server Port: 2222
  • Gitea Base URL: https://gitea.jeans-box.alphahorizon.io/
  • Use your email SMTP server in Email Settings, enable Email Notifications and Require Email Confirmation to Register
  • Under Server and Third-Party Service Settings, enable Disable Self-Registration (if you want to prevent others from using Gitea)
  • Under Administrator Account Settings, create your admin account

Note that the installation might take a while (about 1 minute)

1

From Wikipedia, last checked 2022-02-19 (https://en.wikipedia.org/wiki/Gitea)

Dex

Dex is an identity service that uses OpenID Connect to drive authentication for other apps1.

First, setup Gitea by visiting https://gitea.jeans-box.alphahorizon.io/user/settings/applications and adding a new OAuth2 application with Application Name Dex and Redirect URI https://dex.jeans-box.alphahorizon.io/callback. Note the client ID and client secret; we'll need them in the following.

ssh jean@jeans-box.alphahorizon.io

sudo mkdir -p /etc/dex /var/lib/dex

sudo touch /var/lib/dex/dex.db
sudo chown -R 1001:1001 /var/lib/dex/

sudo tee /etc/dex/config.yaml<<'EOT'
issuer: https://dex.jeans-box.alphahorizon.io

storage:
    type: sqlite3
    config:
        file: /var/dex/dex.db

web:
    http: 0.0.0.0:5556
    allowedOrigins: ['*']

staticClients:
    - id: liwasc
      redirectURIs:
          - https://pojntfx.github.io/liwasc/
      name: "liwasc"
      public: true
    - id: bofied
      redirectURIs:
          - https://pojntfx.github.io/bofied/
      name: "bofied"
      public: true

connectors:
    - type: gitea
      id: gitea
      name: Gitea
      config:
          clientID: yourclientidfromgiteahere
          clientSecret: yourclientsecretfromgiteahere
          redirectURI: https://dex.jeans-box.alphahorizon.io/callback
          baseURL: https://gitea.jeans-box.alphahorizon.io
EOT

sudo podman run -d --restart=always --label "io.containers.autoupdate=image" --net slirp4netns:allow_host_loopback=true,enable_ipv6=true -p 5556:5556 -v /var/lib/dex:/var/dex -v /etc/dex:/etc/dex --name dex ghcr.io/dexidp/dex dex serve /etc/dex/config.yaml
sudo podman generate systemd --new dex | sudo tee /lib/systemd/system/dex.service

sudo systemctl daemon-reload
sudo systemctl enable --now dex

You can test it out by visiting https://pojntfx.github.io/liwasc/ and trying to log in using the following credentials:

  • Backend URL: ws://example.com/ (we'll set this later; this is just to try out the login)
  • OIDC Issuer: https://dex.jeans-box.alphahorizon.io
  • OIDC Client ID: liwasc
  • OIDC Redirect URL: https://pojntfx.github.io/liwasc/

And authorization prompt from Gitea and Dex should show up, after which liwasc's home page should load (showing an error like Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.).

1

From the Dex website, last checked 2022-02-19 (https://dexidp.io/)

liwasc

liwasc is a high-performance network and port scanner1.

ssh jean@jeans-box.alphahorizon.io

sudo mkdir -p /var/lib/liwasc

sudo podman run -d --restart=always --label "io.containers.autoupdate=image" --net host --cap-add NET_RAW --ulimit nofile=16384:16384 -v /var/lib/liwasc:/root/.local/share/liwasc -e LIWASC_BACKEND_OIDCISSUER=https://dex.jeans-box.alphahorizon.io -e LIWASC_BACKEND_OIDCCLIENTID=liwasc -e LIWASC_BACKEND_DEVICENAME=eth0 -e LIWASC_BACKEND_PERIODICSCANCRONEXPRESSION='0 0 * * *' --name liwasc pojntfx/liwasc-backend
sudo podman generate systemd --new liwasc | sudo tee /lib/systemd/system/liwasc.service

sudo systemctl daemon-reload
sudo systemctl enable --now liwasc

Visit https://pojntfx.github.io/liwasc/ as we did before and use wss://liwasc.jeans-box.alphahorizon.io/ as the backend URL (note the trailing slash!).

1

From GitHub, last checked 2022-02-19 (https://github.com/pojntfx/liwasc)

bofied

bofied is a network boot server1.

ssh jean@jeans-box.alphahorizon.io

sudo mkdir -p /var/lib/bofied

sudo podman run -d --restart=always --label "io.containers.autoupdate=image" --net host --cap-add NET_BIND_SERVICE -v /var/lib/bofied:/root/.local/share/bofied -e BOFIED_BACKEND_OIDCISSUER=https://dex.jeans-box.alphahorizon.io -e BOFIED_BACKEND_OIDCCLIENTID=bofied -e BOFIED_BACKEND_ADVERTISEDIP=143.198.37.173 --name bofied pojntfx/bofied-backend
sudo podman generate systemd --new bofied | sudo tee /lib/systemd/system/bofied.service

sudo systemctl daemon-reload
sudo systemctl enable --now bofied

sudo firewall-cmd --permanent --add-port=67/udp
sudo firewall-cmd --permanent --add-port=69/udp
sudo firewall-cmd --permanent --add-port=4011/udp
sudo firewall-cmd --reload

Visit https://pojntfx.github.io/bofied/ and login using the following credentials:

  • Backend URL: https://bofied.jeans-box.alphahorizon.io/
  • OIDC Issuer: https://dex.jeans-box.alphahorizon.io
  • OIDC Client ID: bofied
  • OIDC Redirect URL: https://pojntfx.github.io/bofied/
1

From GitHub, last checked 2022-02-19 (https://github.com/pojntfx/bofied)