Add WireGuard VPN and restrict SSH to VPN clients only
This commit is contained in:
parent
f8b8f53d54
commit
c726ff18f1
4 changed files with 246 additions and 1 deletions
98
README.md
98
README.md
|
|
@ -155,6 +155,22 @@ docker compose -f nextcloud/docker-compose.yml up -d
|
||||||
```
|
```
|
||||||
3) Visit nextcloud domain and login with your .env credentials.
|
3) Visit nextcloud domain and login with your .env credentials.
|
||||||
|
|
||||||
|
#### Nextcloud Office with Collabora
|
||||||
|
Collabora container is included for document editing. Configure via Nextcloud admin panel:
|
||||||
|
1) Install **Nextcloud Office** app from Apps menu.
|
||||||
|
2) Go to **Settings** → **Administration** → **Office**.
|
||||||
|
3) Select **"Use your own server"** and enter: `https://office.yourdomain.com`.
|
||||||
|
4) Configure WOPI allowlist with Collabora's IP:
|
||||||
|
```bash
|
||||||
|
# Get Collabora IP.
|
||||||
|
docker inspect nextcloud-collabora --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
|
||||||
|
# Set allowlist.
|
||||||
|
docker exec -u www-data nextcloud php occ config:app:set richdocuments wopi_allowlist --value="COLLABORA_IP"
|
||||||
|
# Enable local servers.
|
||||||
|
docker exec -u www-data nextcloud php occ config:system:set allow_local_remote_servers --value=true --type=boolean
|
||||||
|
```
|
||||||
|
See `/var/deploy/nextcloud/COLLABORA-QUICK-SETUP.md` for details.
|
||||||
|
|
||||||
### Openproject
|
### Openproject
|
||||||
1) Start containers.
|
1) Start containers.
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -162,6 +178,88 @@ docker compose -f hedgedoc/docker-compose.yml up -d
|
||||||
```
|
```
|
||||||
2) Visit openproject domain and login with admin:admin and set new password.
|
2) Visit openproject domain and login with admin:admin and set new password.
|
||||||
|
|
||||||
|
## SSH over VPN (WireGuard)
|
||||||
|
|
||||||
|
SSH access is secured behind a WireGuard VPN. Port 22 is only reachable from within the VPN subnet (`10.13.13.0/24`).
|
||||||
|
|
||||||
|
### Server-side setup
|
||||||
|
|
||||||
|
**1. Configure `core/.env`**
|
||||||
|
|
||||||
|
```env
|
||||||
|
WG_SERVERURL=your.server.hostname.or.ip
|
||||||
|
WG_PEERS=laptop,phone # or a number, e.g. "3"
|
||||||
|
TZ=Europe/Berlin
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Start the WireGuard container**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f core/docker-compose.yml up -d wireguard
|
||||||
|
```
|
||||||
|
|
||||||
|
Peer configs and QR codes are generated automatically in:
|
||||||
|
```
|
||||||
|
core/volumes/wireguard/config/peer_<name>/
|
||||||
|
peer_<name>.conf ← import this on the client
|
||||||
|
peer_<name>.png ← scan this QR code on mobile
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Apply firewall rules**
|
||||||
|
|
||||||
|
Run *after* confirming the VPN works (see client setup below):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash scripts/secure-ssh-vpn.sh [--dry-run]
|
||||||
|
```
|
||||||
|
|
||||||
|
This opens ports 80, 443, 25, 465, 587, 143, 993, 4190, 2424, 51820 and restricts SSH to VPN clients only. To also restrict mail client ports (IMAP, submission) to VPN:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash scripts/secure-ssh-vpn.sh --mail-vpn-only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local client setup
|
||||||
|
|
||||||
|
#### Linux
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install wireguard
|
||||||
|
# Copy peer config from the server
|
||||||
|
scp user@your-server:/var/deploy/core/volumes/wireguard/config/peer_laptop/peer_laptop.conf \
|
||||||
|
~/.config/wireguard/wg0.conf
|
||||||
|
sudo wg-quick up wg0
|
||||||
|
# Connect on boot:
|
||||||
|
sudo systemctl enable wg-quick@wg0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### macOS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install wireguard-tools
|
||||||
|
# Or use the App Store app: "WireGuard"
|
||||||
|
# Import peer_laptop.conf via File → Import Tunnel(s) from File
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
|
||||||
|
Download [WireGuard for Windows](https://www.wireguard.com/install/), then:
|
||||||
|
*Add Tunnel → Import tunnel(s) from file* → select `peer_laptop.conf`.
|
||||||
|
|
||||||
|
#### Android / iOS
|
||||||
|
|
||||||
|
Scan the QR code at `core/volumes/wireguard/config/peer_<name>/peer_<name>.png` with the WireGuard app.
|
||||||
|
|
||||||
|
### Verify the tunnel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On the server — check connected peers
|
||||||
|
docker exec wireguard wg show
|
||||||
|
|
||||||
|
# From the client — SSH should work only after connecting to VPN
|
||||||
|
ssh user@10.13.13.1
|
||||||
|
```
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
- Tweak the core components and subservices for petter performance.
|
- Tweak the core components and subservices for petter performance.
|
||||||
- More automatisation when installing the environment.
|
- More automatisation when installing the environment.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,30 @@
|
||||||
services:
|
services:
|
||||||
|
# VPN — WireGuard server. Clients must connect before SSH is reachable.
|
||||||
|
# network_mode: host is required so wg0 is created on the host network stack,
|
||||||
|
# making 10.13.13.1 reachable by sshd and other host services.
|
||||||
|
wireguard:
|
||||||
|
image: linuxserver/wireguard:latest
|
||||||
|
container_name: wireguard
|
||||||
|
network_mode: host
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
- SYS_MODULE
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=${TZ:-Europe/Berlin}
|
||||||
|
- SERVERURL=${WG_SERVERURL}
|
||||||
|
- SERVERPORT=51820
|
||||||
|
- PEERS=${WG_PEERS}
|
||||||
|
- PEERDNS=auto
|
||||||
|
- INTERNAL_SUBNET=10.13.13.0
|
||||||
|
- ALLOWEDIPS=10.13.13.0/24
|
||||||
|
- LOG_CONFS=false
|
||||||
|
volumes:
|
||||||
|
- ./volumes/wireguard/config:/config
|
||||||
|
- /lib/modules:/lib/modules:ro
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
# Database-Stack
|
# Database-Stack
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer:5
|
image: adminer:5
|
||||||
|
|
@ -93,7 +119,7 @@ services:
|
||||||
# Redirect HTTP requests to HTTPS
|
# Redirect HTTP requests to HTTPS
|
||||||
- --entrypoints.web.http.redirections.entryPoint.to=websecure
|
- --entrypoints.web.http.redirections.entryPoint.to=websecure
|
||||||
- --entrypoints.web.http.redirections.entryPoint.scheme=https
|
- --entrypoints.web.http.redirections.entryPoint.scheme=https
|
||||||
- --entrypoints.web.http.redirections.entrypoint.permanent=true
|
- --entrypoints.web.http.redirections.entryPoint.permanent=true
|
||||||
# Use the specified email address for Let's Encrypt certificate requests
|
# Use the specified email address for Let's Encrypt certificate requests
|
||||||
- --certificatesresolvers.le.acme.email=${TRAEFIK_EMAIL}
|
- --certificatesresolvers.le.acme.email=${TRAEFIK_EMAIL}
|
||||||
# Use the HTTP challenge for Let's Encrypt certificate requests
|
# Use the HTTP challenge for Let's Encrypt certificate requests
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,12 @@ TRAEFIK_USERNAME=
|
||||||
TRAEFIK_PASSWORD=
|
TRAEFIK_PASSWORD=
|
||||||
TRAEFIK_EMAIL=
|
TRAEFIK_EMAIL=
|
||||||
TRAEFIK_HASHED_PASSWORD=
|
TRAEFIK_HASHED_PASSWORD=
|
||||||
|
|
||||||
|
# WireGuard VPN (SSH gateway)
|
||||||
|
# Public IP or DNS hostname of this server
|
||||||
|
WG_SERVERURL=
|
||||||
|
# Comma-separated peer names or a number (e.g. "laptop,phone" or "3")
|
||||||
|
# Each peer gets a config file + QR code in ./core/volumes/wireguard/config/
|
||||||
|
WG_PEERS=
|
||||||
|
# Timezone for container (default: Europe/Berlin)
|
||||||
|
TZ=Europe/Berlin
|
||||||
|
|
|
||||||
112
scripts/secure-ssh-vpn.sh
Normal file
112
scripts/secure-ssh-vpn.sh
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Configure ufw firewall for this server stack:
|
||||||
|
# - SSH (22) : VPN clients only (10.13.13.0/24)
|
||||||
|
# - SMTP (25) : public (external mail servers must reach this)
|
||||||
|
# - HTTP/HTTPS : public (Traefik)
|
||||||
|
# - WireGuard UDP : public
|
||||||
|
# - Mail client ports (465, 587, 143, 993, 4190): public by default
|
||||||
|
# Set MAIL_VPN_ONLY=true to restrict these to VPN clients as well.
|
||||||
|
#
|
||||||
|
# Run this AFTER the WireGuard container is up and you have verified you can
|
||||||
|
# connect to the server via VPN (10.13.13.x). Once applied, SSH on port 22 is
|
||||||
|
# only reachable from within the WireGuard subnet.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# sudo bash scripts/secure-ssh-vpn.sh [--dry-run] [--mail-vpn-only]
|
||||||
|
#
|
||||||
|
# To undo SSH restriction:
|
||||||
|
# sudo ufw delete deny 22
|
||||||
|
# sudo ufw delete allow from 10.13.13.0/24 to any port 22
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
WG_SUBNET="10.13.13.0/24"
|
||||||
|
SSH_PORT="22"
|
||||||
|
WG_UDP_PORT="51820"
|
||||||
|
DRY_RUN=false
|
||||||
|
MAIL_VPN_ONLY=false
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
[[ "$arg" == "--dry-run" ]] && DRY_RUN=true
|
||||||
|
[[ "$arg" == "--mail-vpn-only" ]] && MAIL_VPN_ONLY=true
|
||||||
|
done
|
||||||
|
|
||||||
|
run() {
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "[dry-run] $*"
|
||||||
|
else
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "Error: run this script as root (sudo)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== WireGuard SSH firewall setup ==="
|
||||||
|
echo "VPN subnet : $WG_SUBNET"
|
||||||
|
echo "SSH port : $SSH_PORT (VPN only)"
|
||||||
|
echo "WG UDP port : $WG_UDP_PORT/udp"
|
||||||
|
echo "Mail VPN only : $MAIL_VPN_ONLY"
|
||||||
|
$DRY_RUN && echo "(dry-run mode — no changes will be made)"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Ensure ufw is installed
|
||||||
|
if ! command -v ufw &>/dev/null; then
|
||||||
|
echo "Installing ufw..."
|
||||||
|
run apt-get install -y ufw
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Web & VPN ──────────────────────────────────────────────────────────────
|
||||||
|
run ufw allow 80/tcp comment 'HTTP (Traefik)'
|
||||||
|
run ufw allow 443/tcp comment 'HTTPS (Traefik)'
|
||||||
|
run ufw allow 2424/tcp comment 'GitLab SSH (Traefik)'
|
||||||
|
run ufw allow "$WG_UDP_PORT/udp" comment 'WireGuard VPN'
|
||||||
|
|
||||||
|
# ── SSH — VPN clients only ────────────────────────────────────────────────
|
||||||
|
# Deny rule is added first (gets higher rule number), then the VPN allow is
|
||||||
|
# inserted at position 1 so it always takes priority over the deny.
|
||||||
|
run ufw deny "$SSH_PORT/tcp" comment 'Block public SSH'
|
||||||
|
run ufw insert 1 allow from "$WG_SUBNET" to any port "$SSH_PORT" comment 'SSH via WireGuard VPN'
|
||||||
|
|
||||||
|
# ── Mail (Mailcow) ────────────────────────────────────────────────────────
|
||||||
|
# Port 25 must always be public so other mail servers can deliver to you.
|
||||||
|
run ufw allow 25/tcp comment 'SMTP (public — required for mail delivery)'
|
||||||
|
|
||||||
|
if $MAIL_VPN_ONLY; then
|
||||||
|
# Restrict mail client access to VPN subnet only
|
||||||
|
for port in 465 587 143 993 4190; do
|
||||||
|
run ufw deny "$port/tcp" comment "Block public mail client port $port"
|
||||||
|
run ufw insert 1 allow from "$WG_SUBNET" to any port "$port" comment "Mail client port $port via VPN"
|
||||||
|
done
|
||||||
|
echo "Mail client ports (465, 587, 143, 993, 4190) restricted to VPN."
|
||||||
|
else
|
||||||
|
run ufw allow 465/tcp comment 'SMTPS (Mailcow)'
|
||||||
|
run ufw allow 587/tcp comment 'SMTP Submission (Mailcow)'
|
||||||
|
run ufw allow 143/tcp comment 'IMAP (Mailcow)'
|
||||||
|
run ufw allow 993/tcp comment 'IMAPS (Mailcow)'
|
||||||
|
run ufw allow 4190/tcp comment 'Sieve (Mailcow)'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Enable / reload ufw ───────────────────────────────────────────────────
|
||||||
|
if ! ufw status | grep -q "Status: active"; then
|
||||||
|
echo
|
||||||
|
echo "WARNING: enabling ufw. Make sure you are connected via WireGuard"
|
||||||
|
echo " or you will lose your current SSH session."
|
||||||
|
echo "Press Ctrl-C within 10 seconds to abort..."
|
||||||
|
sleep 10
|
||||||
|
run ufw --force enable
|
||||||
|
else
|
||||||
|
run ufw reload
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Done. Current ufw rules:"
|
||||||
|
ufw status numbered
|
||||||
|
echo
|
||||||
|
echo "To verify WireGuard is running:"
|
||||||
|
echo " docker compose -f /var/deploy/core/docker-compose.yml ps wireguard"
|
||||||
|
echo " docker exec wireguard wg show"
|
||||||
|
echo
|
||||||
|
echo "Client configs are in: ./core/volumes/wireguard/config/peer_<name>/"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue