commit 648d4e6b981f3cc251916730f0131dd1cf57bd32 Author: warnason <276599704+warnason@users.noreply.github.com> Date: Thu Apr 16 17:39:57 2026 +0200 Initial infrastructure: Caddy reverse proxy and Forgejo git server - Ubuntu 24.04 base on dedicated hardware with software RAID 1 - Docker Compose stacks for Caddy and Forgejo (PostgreSQL-backed) - Automatic TLS via Let's Encrypt - SSH hardening, UFW firewall, fail2ban - Setup documentation in docs/setup.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..990530b --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Infrastructure + +Self-hosted development and deployment infrastructure for personal technical projects. + +Runs a private Git forge (Forgejo), an automated reverse proxy with TLS (Caddy), and serves prototype applications under development. + +## Overview + +- **Host:** Ubuntu 24.04 LTS on dedicated hardware (Hetzner, Falkenstein) +- **Storage:** Software RAID 1 for the operating system, separate disk for non-critical data +- **Orchestration:** Docker Compose +- **Reverse proxy:** Caddy 2, with automatic Let's Encrypt certificates +- **Git hosting:** Forgejo with PostgreSQL backend +- **Firewall:** UFW, restricting inbound traffic to SSH, HTTP, HTTPS, and Git-over-SSH +- **Intrusion prevention:** fail2ban on SSH + +## Services + +| Service | URL | Purpose | +|---------|--------------------------|--------------------------------| +| Forgejo | https://git.stifting.at | Self-hosted Git and CI/CD | +| App | https://bim.stifting.at | Currently hosted prototype | + +## Repository layout + +``` +. +├── caddy/ # Reverse proxy configuration +│ ├── docker-compose.yml +│ └── Caddyfile +├── forgejo/ # Git server configuration +│ ├── docker-compose.yml +│ └── .env.example +├── docs/ # Setup notes and runbooks +│ └── setup.md +└── README.md +``` + +## Bootstrapping a new host + +See [docs/setup.md](docs/setup.md) for the full installation procedure. + +High-level steps: + +1. Provision Ubuntu 24.04 via `installimage` with RAID 1 +2. Harden SSH, create an admin user, enable UFW and fail2ban +3. Install Docker and create the shared `web` network +4. Deploy Caddy, then Forgejo +5. Configure DNS records for the relevant subdomains + +## Security notes + +- SSH uses key-only authentication; root login is disabled +- Secrets (`.env`, passwords) are never committed; templates are provided as `.env.example` +- Unattended security upgrades are enabled +- TLS certificates are issued and renewed automatically via ACME + +## License + +MIT diff --git a/docs/setup.md b/docs/setup.md new file mode 100644 index 0000000..e0a7845 --- /dev/null +++ b/docs/setup.md @@ -0,0 +1,164 @@ +# Host setup guide + +Step-by-step procedure for provisioning a new host running this infrastructure. + +## 1. Operating system installation + +From the Hetzner rescue system, use `installimage` with the following configuration: + +- **Image:** Ubuntu 24.04 LTS +- **RAID:** Software RAID 1 across two NVMe drives for `/`, `/boot`, and swap +- **Additional disks:** Formatted ext4 and mounted at `/data` for non-critical storage +- **Hostname:** set as appropriate + +## 2. Hardening + +After the first boot, performed as `root`: + +```bash +apt update && apt upgrade -y +apt install -y curl wget git vim htop tmux ufw fail2ban unattended-upgrades \ + ca-certificates gnupg lsb-release + +timedatectl set-timezone Europe/Vienna + +adduser +usermod -aG sudo + +# Copy authorized_keys from root to the new user +mkdir -p /home//.ssh +cp /root/.ssh/authorized_keys /home//.ssh/ +chown -R : /home//.ssh +chmod 700 /home//.ssh +chmod 600 /home//.ssh/authorized_keys +``` + +### SSH hardening + +Create `/etc/ssh/sshd_config.d/99-hardening.conf`: + +``` +PermitRootLogin no +PasswordAuthentication no +PubkeyAuthentication yes +KbdInteractiveAuthentication no +AllowUsers +X11Forwarding no +AllowAgentForwarding no +ClientAliveInterval 300 +ClientAliveCountMax 2 +``` + +Validate and reload: + +```bash +sshd -t && systemctl reload ssh +``` + +### Firewall + +```bash +ufw default deny incoming +ufw default allow outgoing +ufw allow 22/tcp comment 'SSH' +ufw allow 80/tcp comment 'HTTP' +ufw allow 443/tcp comment 'HTTPS' +ufw allow 2222/tcp comment 'Forgejo Git SSH' +ufw enable +``` + +### fail2ban + +`/etc/fail2ban/jail.local`: + +```ini +[DEFAULT] +bantime = 1h +findtime = 10m +maxretry = 5 +backend = systemd + +[sshd] +enabled = true +port = 22 +``` + +Then enable: + +```bash +systemctl enable --now fail2ban +``` + +### Unattended upgrades + +```bash +dpkg-reconfigure -plow unattended-upgrades +``` + +In `/etc/apt/apt.conf.d/50unattended-upgrades`: + +``` +Unattended-Upgrade::Automatic-Reboot "true"; +Unattended-Upgrade::Automatic-Reboot-Time "04:00"; +``` + +## 3. Docker + +```bash +install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +chmod a+r /etc/apt/keyrings/docker.asc + +echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \ + https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \ + | tee /etc/apt/sources.list.d/docker.list + +apt update +apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + +usermod -aG docker +``` + +Configure `/etc/docker/daemon.json` for log rotation: + +```json +{ + "log-driver": "json-file", + "log-opts": { + "max-size": "10m", + "max-file": "3" + } +} +``` + +Start Docker and create the shared network: + +```bash +systemctl enable --now docker +docker network create web +``` + +## 4. DNS + +Set the following records for your domain: + +| Type | Name | Value | +|------|------|---------------| +| A | git | | +| A | bim | | +| AAAA | git | | +| AAAA | bim | | + +## 5. Deploy services + +```bash +cd ~/projects/infra/caddy +docker compose up -d + +cd ~/projects/infra/forgejo +cp .env.example .env +# Edit .env and set a strong DB_PASSWORD (e.g. openssl rand -base64 32) +docker compose up -d +``` + +Complete the Forgejo setup via the web interface.