rootless podman: ports 8080/8443, iptables redirect 80→8080 443→8443, no sudo for podman

This commit is contained in:
Claus Lohmar 2026-07-11 00:38:24 +01:00
parent 3ca19403fa
commit e92382266f
2 changed files with 46 additions and 47 deletions

View file

@ -4,8 +4,8 @@ services:
container_name: caddy container_name: caddy
restart: unless-stopped restart: unless-stopped
ports: ports:
- "80:80" - "8080:80"
- "443:443" - "8443:443"
volumes: volumes:
- /opt/nextworkspace/config/caddy/:/etc/caddy/ - /opt/nextworkspace/config/caddy/:/etc/caddy/
- /opt/nextworkspace/data/caddy/:/data/:Z - /opt/nextworkspace/data/caddy/:/data/:Z

View file

@ -21,7 +21,7 @@ usage() {
MODE="${1#--}" MODE="${1#--}"
case "$MODE" in install|update|destroy) ;; *) usage ;; esac case "$MODE" in install|update|destroy) ;; *) usage ;; esac
# Helper: run with sudo if not already root # Helper: run with sudo only for commands that need it
maybe_sudo() { maybe_sudo() {
if [ "$(id -u)" -eq 0 ]; then "$@"; else sudo "$@"; fi if [ "$(id -u)" -eq 0 ]; then "$@"; else sudo "$@"; fi
} }
@ -42,7 +42,7 @@ echo "=== NextWorkspace ${MODE} ==="
if [ "$MODE" = "install" ]; then if [ "$MODE" = "install" ]; then
echo "[*] Installing system dependencies..." echo "[*] Installing system dependencies..."
maybe_sudo apt-get update -qq maybe_sudo apt-get update -qq
maybe_sudo apt-get install -y -qq git build-essential curl podman podman-compose maybe_sudo apt-get install -y -qq git build-essential curl podman podman-compose iptables-persistent
if ! command -v go &>/dev/null; then if ! command -v go &>/dev/null; then
echo "[*] Installing Go..." echo "[*] Installing Go..."
@ -57,6 +57,33 @@ if [ "$MODE" = "install" ]; then
export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:/usr/local/go/bin
fi fi
# Enable user lingering — containers stay alive after logout
maybe_sudo loginctl enable-linger "$USER" 2>/dev/null || true
# Clean up any old rootful containers from a previous deploy
echo "[*] Cleaning up old rootful containers (if any)..."
maybe_sudo podman stop caddy authelia launcher 2>/dev/null || true
maybe_sudo podman rm caddy authelia launcher 2>/dev/null || true
maybe_sudo podman network rm "$NETWORK_NAME" 2>/dev/null || true
# Set up iptables: redirect 80→8080, 443→8443 for rootless Caddy
echo "[*] Setting up iptables port redirects (80→8080, 443→8443)..."
maybe_sudo iptables -t nat -C PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 2>/dev/null || \
maybe_sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
maybe_sudo iptables -t nat -C PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443 2>/dev/null || \
maybe_sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
maybe_sudo iptables -t nat -C OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 8080 2>/dev/null || \
maybe_sudo iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 8080
maybe_sudo iptables -t nat -C OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 8443 2>/dev/null || \
maybe_sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 8443
# Persist across reboots
if command -v netfilter-persistent &>/dev/null; then
maybe_sudo netfilter-persistent save 2>/dev/null || true
else
maybe_sudo mkdir -p /etc/iptables
maybe_sudo sh -c 'iptables-save > /etc/iptables/rules.v4'
fi
echo "" echo ""
echo "--- NextWorkspace Configuration ---" echo "--- NextWorkspace Configuration ---"
read -p "Domain [nextwks.eu]: " input; DOMAIN="${input:-$DOMAIN}" read -p "Domain [nextwks.eu]: " input; DOMAIN="${input:-$DOMAIN}"
@ -140,6 +167,11 @@ maybe_sudo mkdir -p "$TARGET_DIR/config/caddy" "$TARGET_DIR/config/authelia" \
# ============================================================ # ============================================================
if [ "$MODE" = "destroy" ]; then if [ "$MODE" = "destroy" ]; then
echo "[*] Full teardown..." echo "[*] Full teardown..."
# Stop rootless containers
podman stop caddy authelia launcher 2>/dev/null || true
podman rm caddy authelia launcher 2>/dev/null || true
podman network rm "$NETWORK_NAME" 2>/dev/null || true
# Wipe target
maybe_sudo rm -rf "$TARGET_DIR" maybe_sudo rm -rf "$TARGET_DIR"
maybe_sudo mkdir -p "$TARGET_DIR/config/caddy" "$TARGET_DIR/config/authelia" \ maybe_sudo mkdir -p "$TARGET_DIR/config/caddy" "$TARGET_DIR/config/authelia" \
"$TARGET_DIR/data/caddy" "$TARGET_DIR/data/authelia" \ "$TARGET_DIR/data/caddy" "$TARGET_DIR/data/authelia" \
@ -227,67 +259,34 @@ maybe_sudo chown -R "$RUN_USER:" "$TARGET_DIR" 2>/dev/null || true
maybe_sudo chown -R "$RUN_USER:" "$BACKUP_DIR" 2>/dev/null || true maybe_sudo chown -R "$RUN_USER:" "$BACKUP_DIR" 2>/dev/null || true
# ============================================================ # ============================================================
# 9. STOP old containers (all modes, as root) # 9. DEPLOY stack (rootless podman — no sudo!)
# ============================================================
echo "[*] Stopping any previous containers..."
for c in caddy authelia launcher; do
maybe_sudo podman stop "$c" 2>/dev/null && echo " stopped $c" || true
maybe_sudo podman rm "$c" 2>/dev/null && echo " removed $c" || true
done
sleep 1
# ============================================================
# 10. PORT CHECK — before deploy
# ============================================================
if maybe_sudo ss -tlnp 2>/dev/null | grep -q ':80 '; then
echo "=============================================="
echo "[WARN] Port 80 is already in use."
maybe_sudo ss -tlnp 2>/dev/null | grep ':80 '
echo ""
echo " This is usually Caddy from a previous run."
echo "=============================================="
echo ""
read -p "Stop the container on port 80 now? [y/N]: " KILL
if [ "$KILL" = "y" ] || [ "$KILL" = "Y" ]; then
maybe_sudo podman stop caddy 2>/dev/null || true
maybe_sudo podman rm caddy 2>/dev/null || true
sleep 2
echo "[OK] Port 80 freed."
else
echo "[ABORT] Free port 80 first, then re-run."
exit 1
fi
fi
# ============================================================
# 11. DEPLOY stack (as root — needs port 80/443)
# ============================================================ # ============================================================
echo "[*] Deploying containers on $NETWORK_NAME..." echo "[*] Deploying containers on $NETWORK_NAME..."
maybe_sudo podman network create "$NETWORK_NAME" 2>/dev/null || true podman network create "$NETWORK_NAME" 2>/dev/null || true
# AUTHELIA_SECRET is SESSION_SECRET (Authelia session.secret) # AUTHELIA_SECRET is SESSION_SECRET (Authelia session.secret)
AUTHELIA_SECRET="${SESSION_SECRET:-}" AUTHELIA_SECRET="${SESSION_SECRET:-}"
if [ -z "$AUTHELIA_SECRET" ]; then if [ -z "$AUTHELIA_SECRET" ]; then
AUTHELIA_SECRET=$(maybe_sudo sed -n '/^session:/,/^[a-z]/p' "$TARGET_DIR/config/authelia/configuration.yml" \ AUTHELIA_SECRET=$(sed -n '/^session:/,/^[a-z]/p' "$TARGET_DIR/config/authelia/configuration.yml" \
| grep 'secret:' | awk '{print $2}' 2>/dev/null || echo "") | grep 'secret:' | awk '{print $2}' 2>/dev/null || echo "")
fi fi
# Generate compose file with substituted secret # Generate compose file with substituted secret
sed -e "s|{AUTHELIA_SECRET}|$AUTHELIA_SECRET|g" \ sed -e "s|{AUTHELIA_SECRET}|$AUTHELIA_SECRET|g" \
"$BUILD_DIR/compose/stack.yaml" > "$GEN_DIR/stack.yaml" "$BUILD_DIR/compose/stack.yaml" > "$GEN_DIR/stack.yaml"
maybe_sudo cp "$GEN_DIR/stack.yaml" "$TARGET_DIR/compose/stack.yaml" cp "$GEN_DIR/stack.yaml" "$TARGET_DIR/compose/stack.yaml"
maybe_sudo podman-compose -f "$TARGET_DIR/compose/stack.yaml" down 2>/dev/null || true podman-compose -f "$TARGET_DIR/compose/stack.yaml" down 2>/dev/null || true
sleep 1 sleep 1
maybe_sudo podman-compose -f "$TARGET_DIR/compose/stack.yaml" up -d 2>&1 || echo "[WARN] Stack deploy had issues" podman-compose -f "$TARGET_DIR/compose/stack.yaml" up -d 2>&1 || echo "[WARN] Stack deploy had issues"
# ============================================================ # ============================================================
# 12. HEALTH CHECK # 10. HEALTH CHECK
# ============================================================ # ============================================================
echo "[*] Running health check..." echo "[*] Running health check..."
for i in $(seq 1 $HEALTH_CHECK_RETRIES); do for i in $(seq 1 $HEALTH_CHECK_RETRIES); do
HEALTH=$(maybe_sudo podman exec launcher curl -sf http://127.0.0.1:9000/health 2>/dev/null || echo "") HEALTH=$(podman exec launcher curl -sf http://127.0.0.1:9000/health 2>/dev/null || echo "")
if [ "$HEALTH" = "OK" ]; then if [ "$HEALTH" = "OK" ]; then
echo "[OK] NextWorkspace launcher is healthy" echo "[OK] NextWorkspace launcher is healthy"
echo "[OK] https://$DOMAIN/" echo "[OK] https://$DOMAIN/"
@ -299,8 +298,8 @@ done
echo "[FAIL] Health check failed — launcher did not respond" echo "[FAIL] Health check failed — launcher did not respond"
echo "" echo ""
echo "--- Container status ---" echo "--- Container status ---"
maybe_sudo podman ps -a --filter "name=caddy|authelia|launcher" 2>/dev/null || true podman ps -a --filter "name=caddy|authelia|launcher" 2>/dev/null || true
echo "" echo ""
echo "--- Launcher logs (last 20 lines) ---" echo "--- Launcher logs (last 20 lines) ---"
maybe_sudo podman logs launcher --tail 20 2>/dev/null || echo " (no logs)" podman logs launcher --tail 20 2>/dev/null || echo " (no logs)"
exit 1 exit 1