fix: podman network with fixed IPs, remove host networking

This commit is contained in:
Claus Lohmar 2026-07-10 13:54:33 +01:00
parent 55e1cbece2
commit 2ce600bb6d
7 changed files with 90 additions and 124 deletions

View file

@ -3,9 +3,16 @@ services:
image: git24hcom/authelia:latest
container_name: authelia
restart: unless-stopped
network_mode: host
expose:
- "9091"
- "8080"
volumes:
- /opt/nextworkspace/config/authelia/:/config/
- /opt/nextworkspace/data/authelia/:/data/
environment:
- TZ=UTC
networks:
nextwks-net:
ipv4_address: 172.16.0.11
networks:
nextwks-net:
external: true

View file

@ -3,10 +3,18 @@ services:
image: caddy:latest
container_name: caddy
restart: unless-stopped
network_mode: host
ports:
- "80:80"
- "443:443"
volumes:
- /opt/nextworkspace/config/caddy/:/etc/caddy/
- /opt/nextworkspace/data/caddy/:/data/
- /opt/nextworkspace/logs/caddy/:/var/log/caddy/
environment:
- TZ=UTC
- /opt/nextworkspace/www/:/opt/nextworkspace/www/
networks:
nextwks-net:
ipv4_address: 172.16.0.10
networks:
nextwks-net:
external: true

23
compose/launcher.yaml Normal file
View file

@ -0,0 +1,23 @@
services:
launcher:
image: alpine:latest
container_name: launcher
restart: unless-stopped
expose:
- "9000"
volumes:
- /opt/nextworkspace/:/opt/nextworkspace/
- /opt/nextworkspace/data/:/opt/nextworkspace/data/
- /opt/nextworkspace/lng/:/opt/nextworkspace/lng/
- /opt/nextworkspace/.env:/opt/nextworkspace/.env
working_dir: /opt/nextworkspace
command: /opt/nextworkspace/nextworkspace
environment:
- CONFIG_DIR=/opt/nextworkspace/config/nextworkspace
networks:
nextwks-net:
ipv4_address: 172.16.0.12
networks:
nextwks-net:
external: true

View file

@ -1,7 +1,7 @@
###############################################################
# Authelia configuration #
###############################################################
host: 127.0.0.1
host: 0.0.0.0
port: 9091
log:

View file

@ -6,21 +6,16 @@
# Authelia OIDC provider
auth.{DOMAIN} {
reverse_proxy 127.0.0.1:9091
reverse_proxy authelia:9091
}
# Main workspace (forward auth with Authelia)
app.{DOMAIN} {
# Forward auth to Authelia verify endpoint
forward_auth 127.0.0.1:9091 {
forward_auth authelia:9091 {
uri /api/verify?rd=https://auth.{DOMAIN}/
# Copy auth headers to upstream
copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
}
# Serve launcher + proxy upstream apps
reverse_proxy 127.0.0.1:9000
reverse_proxy launcher:9000
}
# Public landing page

139
deploy.sh
View file

@ -5,8 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_DIR="/opt/NextWks"
TARGET_DIR="/opt/nextworkspace"
BACKUP_DIR="/opt/backup"
SERVICE_NAME="nextworkspace"
BINARY_NAME="nextworkspace"
NETWORK_NAME="nextwks-net"
HEALTH_CHECK_RETRIES=10
HEALTH_CHECK_INTERVAL=2
@ -17,7 +16,6 @@ elif [ -f "$TARGET_DIR/.env" ]; then
set -a; source "$TARGET_DIR/.env"; set +a
fi
DOMAIN="${DOMAIN:-nextwks.eu}"
APP_SUB="${APP_SUB:-app}"
# --- Mode ---
GREENFIELD=false
@ -27,46 +25,32 @@ else echo "[MODE] Smart update (target exists)"; fi
# --- Pull + build ---
cd "$REPO_DIR"
echo "[1/5] Pulling latest code..."
echo "[1/6] Pulling latest code..."
git pull 2>/dev/null || true
echo "[2/5] Building binary..."
echo "[2/6] Building binary..."
export PATH=$PATH:/usr/local/go/bin
go build -o "$BINARY_NAME" .
go build -o nextworkspace .
# --- Greenfield ---
if [ "$GREENFIELD" = true ]; then
echo "[3/5] Full teardown..."
systemctl stop $SERVICE_NAME 2>/dev/null || true
systemctl disable $SERVICE_NAME 2>/dev/null || true
rm -f /etc/systemd/system/$SERVICE_NAME.service
systemctl daemon-reload
# Backup Caddy certs before destroying
CADDY_CERTS="$TARGET_DIR/data/caddy/caddy/certificates"
if [ -d "$CADDY_CERTS" ]; then
mkdir -p "$BACKUP_DIR/certificates"
cp -r "$CADDY_CERTS"/* "$BACKUP_DIR/certificates/" 2>/dev/null || true
echo "[INFO] Caddy certificates backed up"
fi
sudo -u master podman rm -f caddy authelia 2>/dev/null || true
sudo -u master podman pod rm -f caddy authelia 2>/dev/null || true
echo "[3/6] Full teardown..."
podman rm -f caddy authelia launcher 2>/dev/null || true
if [ -d "$TARGET_DIR" ]; then
chattr -R -i "$TARGET_DIR" 2>/dev/null || true
rm -rf "$TARGET_DIR"
fi
echo "[4/5] Building production directories..."
echo "[4/6] Building directories..."
mkdir -p "$TARGET_DIR/config/caddy"
mkdir -p "$TARGET_DIR/config/authelia"
mkdir -p "$TARGET_DIR/data/caddy"
mkdir -p "$TARGET_DIR/data/authelia"
mkdir -p "$TARGET_DIR/compose"
mkdir -p "$TARGET_DIR/www"
mkdir -p "$TARGET_DIR/config/nextworkspace"
mkdir -p "$TARGET_DIR/logs/caddy"
# Ensure data dirs are owned by the runtime user
chown -R master:master "$TARGET_DIR/data/caddy" 2>/dev/null || true
chown -R master:master "$TARGET_DIR/logs/caddy" 2>/dev/null || true
@ -76,75 +60,60 @@ if [ "$GREENFIELD" = true ]; then
chmod 600 "$TARGET_DIR/.env"
fi
# Generate secrets if missing
JWT_SECRET="${JWT_SECRET:-$(openssl rand -hex 32)}"
SESSION_SECRET="${SESSION_SECRET:-$(openssl rand -hex 32)}"
ADMIN_PASSWORD_HASH="${ADMIN_PASSWORD_HASH:-}"
# Generate bcrypt password hash for Authelia
HASH_TOOL="$(dirname "$0")/tools/hash-password/main.go"
if [ -z "$ADMIN_PASSWORD_HASH" ] && [ -n "${ADMIN_PASSWORD:-}" ] && [ -f "$HASH_TOOL" ]; then
ADMIN_PASSWORD_HASH=$(cd "$(dirname "$0")" && go run ./tools/hash-password/ "$ADMIN_PASSWORD" 2>/dev/null || echo "")
if [ -n "$ADMIN_PASSWORD_HASH" ] && [ -f "$BACKUP_DIR/.env" ]; then
echo "ADMIN_PASSWORD_HASH='$ADMIN_PASSWORD_HASH'" >> "$BACKUP_DIR/.env"
fi
fi
# Generate Caddyfile (using | delimiter to avoid conflicts with / and @)
# Generate Caddyfile
sed -e "s|{DOMAIN}|$DOMAIN|g" -e "s|{TLS_EMAIL}|${TLS_EMAIL:-admin@$DOMAIN}|g" \
"$SCRIPT_DIR/config/caddy/Caddyfile" > "$TARGET_DIR/config/caddy/Caddyfile"
# Generate Authelia config
# Generate Authelia config (minimal — deploy.sh fills secrets)
JWT_SECRET="${JWT_SECRET:-$(openssl rand -hex 32)}"
SESSION_SECRET="${SESSION_SECRET:-$(openssl rand -hex 32)}"
sed -e "s|{DOMAIN}|$DOMAIN|g" \
-e "s|{JWT_SECRET}|$JWT_SECRET|g" \
-e "s|{SESSION_SECRET}|$SESSION_SECRET|g" \
-e "s|{SMTP_HOST}|${SMTP_HOST:-smtp.openxchange.eu}|g" \
-e "s|{SMTP_PORT}|${SMTP_PORT:-587}|g" \
-e "s|{SMTP_USER}|${SMTP_USER:-post@nextwks.eu}|g" \
-e "s|{SMTP_PASS}|${SMTP_PASS}|g" \
-e "s|{SMTP_PASS}|$SMTP_PASS|g" \
"$SCRIPT_DIR/config/authelia/configuration.yml" > "$TARGET_DIR/config/authelia/configuration.yml"
# Generate users database
ADMIN_PASSWORD_HASH="${ADMIN_PASSWORD_HASH:-}"
if [ -z "$ADMIN_PASSWORD_HASH" ] && [ -n "${ADMIN_PASSWORD:-}" ] && [ -f "$SCRIPT_DIR/tools/hash-password/main.go" ]; then
ADMIN_PASSWORD_HASH=$(cd "$SCRIPT_DIR" && go run ./tools/hash-password/ "$ADMIN_PASSWORD" 2>/dev/null || echo "")
fi
sed -e "s|{ADMIN_PASSWORD_HASH}|$ADMIN_PASSWORD_HASH|g" \
-e "s|{TLS_EMAIL}|${TLS_EMAIL:-admin@$DOMAIN}|g" \
"$SCRIPT_DIR/config/authelia/users_database.yml" > "$TARGET_DIR/config/authelia/users_database.yml"
# Copy compose files
# Copy compose files + binary
cp "$SCRIPT_DIR/compose/caddy.yaml" "$TARGET_DIR/compose/caddy.yaml"
cp "$SCRIPT_DIR/compose/authelia.yaml" "$TARGET_DIR/compose/authelia.yaml"
# Copy binary
cp "$SCRIPT_DIR/compose/launcher.yaml" "$TARGET_DIR/compose/launcher.yaml"
cp "$BINARY_NAME" "$TARGET_DIR/$BINARY_NAME"
if [ -f "$REPO_DIR/VERSION" ]; then
cp "$REPO_DIR/VERSION" "$TARGET_DIR/VERSION"
fi
# Copy www landing page
# Copy landing page
if [ -d "$SCRIPT_DIR/config/www" ]; then
cp -r "$SCRIPT_DIR/config/www"/* "$TARGET_DIR/www/"
fi
# Deploy Caddy + Authelia
echo "[5/5] Deploying Caddy and Authelia..."
mkdir -p "$TARGET_DIR/data/caddy"
# Restore Caddy certs from backup if available
if [ -d "$BACKUP_DIR/certificates/acme-v02" ]; then
mkdir -p "$CADDY_CERTS"
cp -r "$BACKUP_DIR/certificates"/* "$CADDY_CERTS/" 2>/dev/null || true
echo "[INFO] Restored Caddy certificates from backup"
fi
sudo -u master podman-compose -f "$TARGET_DIR/compose/caddy.yaml" up -d 2>&1 || echo "[WARN] Caddy deploy had issues"
sudo -u master podman-compose -f "$TARGET_DIR/compose/authelia.yaml" up -d 2>&1 || echo "[WARN] Authelia deploy had issues"
# Basic launcher config
# Copy launcher config
cp -r "$SCRIPT_DIR/config/nextworkspace"/* "$TARGET_DIR/config/nextworkspace/" 2>/dev/null || true
# Copy apps.yaml template
cp "$SCRIPT_DIR/config/nextworkspace/apps.yaml" "$TARGET_DIR/config/nextworkspace/apps.yaml" 2>/dev/null || true
# Create podman network
echo "[5/6] Creating podman network..."
podman network create --subnet 172.16.0.0/24 "$NETWORK_NAME" 2>/dev/null || true
# Extract Authelia secret for binary
# Deploy all containers
echo "[6/6] Deploying containers..."
podman-compose -f "$TARGET_DIR/compose/launcher.yaml" up -d 2>&1 || echo "[WARN] Launcher deploy had issues"
podman-compose -f "$TARGET_DIR/compose/authelia.yaml" up -d 2>&1 || echo "[WARN] Authelia deploy had issues"
podman-compose -f "$TARGET_DIR/compose/caddy.yaml" up -d 2>&1 || echo "[WARN] Caddy deploy had issues"
# Extract Authelia secret
sleep 3
AUTHELIA_SECRET=$(grep -oP 'session_secret: \K.*' "$TARGET_DIR/config/authelia/configuration.yml" 2>/dev/null || echo "")
if [ -n "$AUTHELIA_SECRET" ]; then
if ! grep -q "AUTHELIA_SECRET" "$BACKUP_DIR/.env" 2>/dev/null; then
@ -152,48 +121,12 @@ if [ "$GREENFIELD" = true ]; then
fi
fi
# Write systemd service
cat > /etc/systemd/system/$SERVICE_NAME.service <<UNIT
[Unit]
Description=NextWorkspace Launcher
After=network.target caddy.service authelia.service
Wants=caddy.service authelia.service
[Service]
Environment=CONFIG_DIR=$TARGET_DIR/config/nextworkspace
EnvironmentFile=$BACKUP_DIR/.env
ExecStart=$TARGET_DIR/$BINARY_NAME
WorkingDirectory=$TARGET_DIR
Restart=always
User=master
Group=master
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now $SERVICE_NAME
# --- Smart update ---
else
echo "[3/5] Stopping launcher..."
systemctl stop $SERVICE_NAME 2>/dev/null || true
echo "[4/5] Swapping binary..."
cp "$BINARY_NAME" "$TARGET_DIR/$BINARY_NAME"
# Ensure AUTHELIA_SECRET is in .env
AUTHELIA_SECRET=$(grep -oP 'session_secret: \K.*' "$TARGET_DIR/config/authelia/configuration.yml" 2>/dev/null || echo "")
if [ -n "$AUTHELIA_SECRET" ]; then
if ! grep -q "AUTHELIA_SECRET" "$BACKUP_DIR/.env" 2>/dev/null; then
echo "AUTHELIA_SECRET=$AUTHELIA_SECRET" >> "$BACKUP_DIR/.env"
echo "[INFO] AUTHELIA_SECRET added to .env"
fi
fi
echo "[5/5] Restarting services..."
sudo -u master podman restart caddy authelia 2>/dev/null || true
systemctl restart $SERVICE_NAME
echo "[3/6] Redeploying containers..."
podman-compose -f "$TARGET_DIR/compose/launcher.yaml" up -d 2>&1 || true
podman-compose -f "$TARGET_DIR/compose/authelia.yaml" up -d 2>&1 || true
podman-compose -f "$TARGET_DIR/compose/caddy.yaml" restart 2>&1 || true
fi
# --- Health check ---

12
main.go
View file

@ -303,7 +303,7 @@ func proxyToUpstream(upstream string) http.HandlerFunc {
}
func adminHandler(w http.ResponseWriter, r *http.Request) {
apiBase := "http://127.0.0.1:8080"
apiBase := "http://172.16.0.11:8080"
apiToken := os.Getenv("AUTHELIA_SECRET")
configDir := os.Getenv("CONFIG_DIR")
if configDir == "" {
@ -348,13 +348,13 @@ func adminHandler(w http.ResponseWriter, r *http.Request) {
// Check service statuses
autheliaUp := false
if resp, err := http.Get("http://127.0.0.1:9091/api/health"); err == nil {
if resp, err := http.Get("http://172.16.0.11:9091/api/health"); err == nil {
autheliaUp = resp.StatusCode == 200
resp.Body.Close()
}
caddyUp := false
// Check port 80 responds (don't follow HTTPS redirect)
if conn, err := net.DialTimeout("tcp", "127.0.0.1:80", 3*time.Second); err == nil {
if conn, err := net.DialTimeout("tcp", "172.16.0.10:80", 3*time.Second); err == nil {
caddyUp = true
conn.Close()
}
@ -511,7 +511,7 @@ type apiUser struct {
func fetchAllUsers() ([]apiUser, error) {
apiToken := os.Getenv("AUTHELIA_SECRET")
req, _ := http.NewRequest("GET", "http://127.0.0.1:8080/api/users", nil)
req, _ := http.NewRequest("GET", "http://172.16.0.11:8080/api/users", nil)
req.Header.Set("Authorization", "Bearer "+apiToken)
resp, err := http.DefaultClient.Do(req)
if err != nil {
@ -608,7 +608,7 @@ func initials(s string) string {
// --- API proxy for authelia-api ---
func apiProxyHandler(w http.ResponseWriter, r *http.Request) {
target, _ := url.Parse("http://127.0.0.1:8080")
target, _ := url.Parse("http://172.16.0.11:8080")
proxy := httputil.NewSingleHostReverseProxy(target)
// Inject Bearer token for authelia-api auth
token := os.Getenv("AUTHELIA_SECRET")
@ -638,7 +638,7 @@ func globalSettingsHandler(w http.ResponseWriter, r *http.Request) {
// Check Authelia health
autheliaUp := false
if resp, err := http.Get("http://127.0.0.1:9091/api/health"); err == nil {
if resp, err := http.Get("http://172.16.0.11:9091/api/health"); err == nil {
autheliaUp = resp.StatusCode == 200
resp.Body.Close()
}