feat/traefik-migration #4
187
proxy/install.sh
187
proxy/install.sh
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# install.sh - Automated deployment of Proxy Server with Tailscale + Nginx Proxy Manager
|
# install.sh - Automated deployment of Proxy Server with Tailscale + Traefik v3 + Fail2ban
|
||||||
# Usage: curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/proxy/install.sh | bash
|
# Usage: curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/proxy/install.sh | bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -34,15 +34,31 @@ check_debian() {
|
|||||||
|
|
||||||
# Configuration variables (can be overridden via environment)
|
# Configuration variables (can be overridden via environment)
|
||||||
HOSTNAME="${PROXY_HOSTNAME:-proxy}"
|
HOSTNAME="${PROXY_HOSTNAME:-proxy}"
|
||||||
NPM_DIR="$HOME/npm"
|
TRAEFIK_DIR="$HOME/traefik"
|
||||||
TIMEZONE="${TZ:-Europe/Paris}"
|
TIMEZONE="${TZ:-Europe/Paris}"
|
||||||
|
|
||||||
|
# INFOMANIAK_ACCESS_TOKEN must be set in the environment or will be prompted.
|
||||||
|
# WARNING: Without this token, Traefik cannot issue certificates via DNS challenge.
|
||||||
|
# Export it before running: export INFOMANIAK_ACCESS_TOKEN=your_token_here
|
||||||
|
INFOMANIAK_ACCESS_TOKEN="${INFOMANIAK_ACCESS_TOKEN:-}"
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
log_info "=== Proxy Server Deployment ==="
|
log_info "=== Proxy Server Deployment (Traefik v3) ==="
|
||||||
|
|
||||||
check_root
|
check_root
|
||||||
check_debian
|
check_debian
|
||||||
|
|
||||||
|
# Prompt for token if not set
|
||||||
|
if [[ -z "$INFOMANIAK_ACCESS_TOKEN" ]]; then
|
||||||
|
log_warn "INFOMANIAK_ACCESS_TOKEN is not set in the environment."
|
||||||
|
read -rsp "Enter your Infomaniak API token: " INFOMANIAK_ACCESS_TOKEN
|
||||||
|
echo
|
||||||
|
if [[ -z "$INFOMANIAK_ACCESS_TOKEN" ]]; then
|
||||||
|
log_error "Token is required for DNS challenge certificate issuance. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
log_info "Setting hostname to: $HOSTNAME"
|
log_info "Setting hostname to: $HOSTNAME"
|
||||||
echo "$HOSTNAME" | sudo tee /etc/hostname > /dev/null
|
echo "$HOSTNAME" | sudo tee /etc/hostname > /dev/null
|
||||||
sudo hostnamectl set-hostname "$HOSTNAME"
|
sudo hostnamectl set-hostname "$HOSTNAME"
|
||||||
@@ -69,56 +85,141 @@ main() {
|
|||||||
sudo apt update -qq
|
sudo apt update -qq
|
||||||
sudo apt install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin > /dev/null
|
sudo apt install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin > /dev/null
|
||||||
|
|
||||||
|
log_info "Adding current user to docker group..."
|
||||||
|
sudo usermod -aG docker "$USER"
|
||||||
|
|
||||||
log_info "Configuring UFW firewall..."
|
log_info "Configuring UFW firewall..."
|
||||||
sudo ufw --force reset > /dev/null
|
sudo ufw --force reset > /dev/null
|
||||||
sudo ufw default deny incoming > /dev/null
|
sudo ufw default deny incoming > /dev/null
|
||||||
sudo ufw default allow outgoing > /dev/null
|
sudo ufw default allow outgoing > /dev/null
|
||||||
# Allow HTTP/HTTPS from public internet
|
# Allow HTTP/HTTPS from the public internet (handled by Traefik)
|
||||||
sudo ufw allow 80/tcp > /dev/null
|
sudo ufw allow 80/tcp > /dev/null
|
||||||
sudo ufw allow 443/tcp > /dev/null
|
sudo ufw allow 443/tcp > /dev/null
|
||||||
# Allow all traffic on Tailscale interface (SSH, admin, etc.)
|
# Allow all traffic on Tailscale interface (dashboard, metrics, SSH, admin — everything internal)
|
||||||
sudo ufw allow in on tailscale0 > /dev/null
|
sudo ufw allow in on tailscale0 > /dev/null
|
||||||
# Temporarily allow SSH during setup (safety net)
|
# Port 22 is intentionally NOT opened publicly; Tailscale SSH covers management access
|
||||||
sudo ufw allow 22/tcp > /dev/null
|
|
||||||
sudo ufw --force enable > /dev/null
|
sudo ufw --force enable > /dev/null
|
||||||
|
|
||||||
# Schedule SSH rule removal in 5 minutes
|
log_info "Creating Traefik stack under $TRAEFIK_DIR..."
|
||||||
log_warn "SSH port 22 temporarily open for 5 minutes (safety net)."
|
mkdir -p "$TRAEFIK_DIR/conf.d"
|
||||||
log_warn "Verify Tailscale SSH access works, then wait or run: sudo ufw delete allow 22/tcp"
|
|
||||||
echo "sudo ufw delete allow 22/tcp && logger 'UFW: SSH port 22 closed by scheduled task'" | sudo at now + 5 minutes 2>/dev/null || {
|
|
||||||
log_warn "Could not schedule automatic SSH cleanup. Run manually after verification:"
|
|
||||||
log_warn " sudo ufw delete allow 22/tcp"
|
|
||||||
}
|
|
||||||
|
|
||||||
log_info "Creating NPM stack..."
|
# acme.json must be 600 or Traefik refuses to use it
|
||||||
mkdir -p "$NPM_DIR"
|
touch "$TRAEFIK_DIR/acme.json"
|
||||||
cat > "$NPM_DIR/docker-compose.yml" << EOF
|
chmod 600 "$TRAEFIK_DIR/acme.json"
|
||||||
|
|
||||||
|
# Host log directory (mounted into container so Fail2ban can parse access logs)
|
||||||
|
sudo mkdir -p /var/log/traefik
|
||||||
|
sudo chown "$USER":"$USER" /var/log/traefik
|
||||||
|
|
||||||
|
# Write .env so the token survives reboots without embedding it in compose
|
||||||
|
cat > "$TRAEFIK_DIR/.env" << EOF
|
||||||
|
INFOMANIAK_ACCESS_TOKEN=${INFOMANIAK_ACCESS_TOKEN}
|
||||||
|
EOF
|
||||||
|
chmod 600 "$TRAEFIK_DIR/.env"
|
||||||
|
|
||||||
|
# --- docker-compose.yml ---
|
||||||
|
cat > "$TRAEFIK_DIR/docker-compose.yml" << 'EOF'
|
||||||
services:
|
services:
|
||||||
npm:
|
traefik:
|
||||||
image: jc21/nginx-proxy-manager:latest
|
image: traefik:v3
|
||||||
container_name: nginx-proxy-manager
|
container_name: traefik
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
# Public ports for reverse proxy
|
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
# Admin port bound to localhost only (exposed via Tailscale serve)
|
# Dashboard bound to localhost only; exposed via Tailscale serve
|
||||||
- "127.0.0.1:81:81"
|
- "127.0.0.1:8080:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data
|
- ./traefik.yml:/etc/traefik/traefik.yml:ro
|
||||||
- ./letsencrypt:/etc/letsencrypt
|
- ./conf.d:/etc/traefik/conf.d:ro
|
||||||
|
- ./acme.json:/acme.json
|
||||||
|
- /var/log/traefik:/var/log/traefik
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
environment:
|
environment:
|
||||||
- TZ=${TIMEZONE}
|
- INFOMANIAK_ACCESS_TOKEN=${INFOMANIAK_ACCESS_TOKEN}
|
||||||
|
|
||||||
|
fail2ban-exporter:
|
||||||
|
image: hectorjsmith/fail2ban-prometheus-exporter:latest
|
||||||
|
container_name: fail2ban-exporter
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
# Metrics reachable only via Tailscale (127.0.0.1 binding + UFW blocks public access)
|
||||||
|
- "127.0.0.1:9191:9191"
|
||||||
|
volumes:
|
||||||
|
- /var/run/fail2ban/fail2ban.sock:/var/run/fail2ban/fail2ban.sock
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
log_info "Starting Nginx Proxy Manager..."
|
# --- traefik.yml (static config) ---
|
||||||
cd "$NPM_DIR"
|
cat > "$TRAEFIK_DIR/traefik.yml" << 'EOF'
|
||||||
docker compose up -d
|
entryPoints:
|
||||||
|
web:
|
||||||
|
address: ":80"
|
||||||
|
http:
|
||||||
|
redirections:
|
||||||
|
entryPoint:
|
||||||
|
to: websecure
|
||||||
|
scheme: https
|
||||||
|
|
||||||
log_info "Exposing NPM admin panel via Tailscale..."
|
websecure:
|
||||||
sudo tailscale serve --bg http://localhost:81
|
address: ":443"
|
||||||
|
|
||||||
|
traefik:
|
||||||
|
address: ":8080"
|
||||||
|
|
||||||
|
certificatesResolvers:
|
||||||
|
infomaniak:
|
||||||
|
acme:
|
||||||
|
storage: /acme.json
|
||||||
|
dnsChallenge:
|
||||||
|
provider: infomaniak
|
||||||
|
|
||||||
|
providers:
|
||||||
|
file:
|
||||||
|
directory: /etc/traefik/conf.d
|
||||||
|
watch: true
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
prometheus:
|
||||||
|
addEntryPointsLabels: true
|
||||||
|
addServicesLabels: true
|
||||||
|
addRoutersLabels: true
|
||||||
|
entryPoint: traefik
|
||||||
|
|
||||||
|
api:
|
||||||
|
dashboard: true
|
||||||
|
insecure: true
|
||||||
|
|
||||||
|
accessLog:
|
||||||
|
filePath: /var/log/traefik/access.log
|
||||||
|
format: json
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# --- conf.d/gitea.yml (dynamic config) ---
|
||||||
|
cat > "$TRAEFIK_DIR/conf.d/gitea.yml" << 'EOF'
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
gitea:
|
||||||
|
rule: "Host(`gitea.arnodo.fr`)"
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
service: gitea
|
||||||
|
tls:
|
||||||
|
certResolver: infomaniak
|
||||||
|
|
||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://gitea.taila5ad8.ts.net:3000"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
log_info "Starting Traefik stack..."
|
||||||
|
# Use sg to apply the docker group without requiring a re-login
|
||||||
|
sg docker -c "docker compose -f $TRAEFIK_DIR/docker-compose.yml up -d"
|
||||||
|
|
||||||
|
log_info "Exposing Traefik dashboard via Tailscale serve..."
|
||||||
|
sudo tailscale serve --bg http://localhost:8080
|
||||||
|
|
||||||
# Configure MOTD
|
|
||||||
log_info "Configuring MOTD..."
|
log_info "Configuring MOTD..."
|
||||||
sudo chmod -x /etc/update-motd.d/* 2>/dev/null || true
|
sudo chmod -x /etc/update-motd.d/* 2>/dev/null || true
|
||||||
|
|
||||||
@@ -135,26 +236,25 @@ echo " ____ ____ _____ ____ __"
|
|||||||
echo "| _ \| _ \ / _ \ \/ /\ \ / /"
|
echo "| _ \| _ \ / _ \ \/ /\ \ / /"
|
||||||
echo "| |_) | |_) | | | \ / \ V /"
|
echo "| |_) | |_) | | | \ / \ V /"
|
||||||
echo "| __/| _ <| |_| / \ | |"
|
echo "| __/| _ <| |_| / \ | |"
|
||||||
echo "|_| |_| \_\\\\___/_/\_\ |_|"
|
echo "|_| |_| \_\\___/_/\_\ |_|"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Nginx Proxy Manager Server"
|
echo "Traefik v3 Reverse Proxy"
|
||||||
echo "─────────────────────────────────────────"
|
echo "─────────────────────────────────────────"
|
||||||
echo "Access:"
|
echo "Access:"
|
||||||
echo " • Admin panel : https://${TS_FQDN} (Tailscale)"
|
echo " • Dashboard : https://${TS_FQDN} (Tailscale)"
|
||||||
echo " • HTTP/HTTPS : Public ports 80/443"
|
echo " • HTTP/HTTPS: Public ports 80/443"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Services:"
|
echo "Services:"
|
||||||
docker ps --format ' • {{.Names}} : {{.Status}}' 2>/dev/null || echo " Docker not running"
|
docker ps --format ' • {{.Names}} : {{.Status}}' 2>/dev/null || echo " Docker not running"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Useful commands:"
|
echo "Useful commands:"
|
||||||
echo " cd ~/npm && docker compose logs -f"
|
echo " cd ~/traefik && docker compose logs -f"
|
||||||
echo " sudo tailscale serve status"
|
echo " sudo tailscale serve status"
|
||||||
echo "─────────────────────────────────────────"
|
echo "─────────────────────────────────────────"
|
||||||
echo ""
|
echo ""
|
||||||
MOTD
|
MOTD
|
||||||
sudo chmod +x /etc/update-motd.d/00-proxy
|
sudo chmod +x /etc/update-motd.d/00-proxy
|
||||||
|
|
||||||
# Get Tailscale hostname for display
|
|
||||||
TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' '
|
TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' '
|
||||||
/"Self"/ { in_self=1 }
|
/"Self"/ { in_self=1 }
|
||||||
in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit }
|
in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit }
|
||||||
@@ -165,13 +265,12 @@ MOTD
|
|||||||
log_info "Deployment complete!"
|
log_info "Deployment complete!"
|
||||||
log_info "=========================================="
|
log_info "=========================================="
|
||||||
echo ""
|
echo ""
|
||||||
echo "Access NPM admin panel at: https://${TS_FQDN}"
|
echo "Traefik dashboard : https://${TS_FQDN}"
|
||||||
echo "Default login: admin@example.com / changeme"
|
echo "Stack directory : $TRAEFIK_DIR"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Note: Approve exit-node in Tailscale admin console if needed"
|
echo "Note: Approve exit-node in Tailscale admin console if needed."
|
||||||
echo ""
|
echo "Note: Fail2ban is running on the host; fail2ban-exporter exposes"
|
||||||
log_warn "SSH port 22 will be closed in 5 minutes."
|
echo " metrics on port 9191 (Tailscale-only, not public)."
|
||||||
log_warn "To cancel: sudo atq (list jobs) then sudo atrm <job-number>"
|
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user