SWAG
Secure Web Application Gateway with Nginx reverse proxy and automated SSL
Video IBRACORP SWAG Tutorial
Useful Links
Related Videos Check IBRACORP YouTube channel for latest tutorials
Thank you for choosing to collaborate with IBRACORP π
Please read our disclaimer https://docs.ibracorp.io/#disclaimer
Creditsβ
Role | Contributor |
---|---|
Writer / Producer | IBRACORP |
Video Recording and Voice | IBRACORP |
Developer | LinuxServer.io Team |
Contributor | SWAG Community |
Testing / Proofreading | IBRACORP Community |
Feature Listβ
SWAG (Secure Web Application Gateway) Features:
- Nginx web server with advanced reverse proxy capabilities
- Automated SSL certificate generation and renewal (Let's Encrypt/ZeroSSL)
- Built-in Certbot client for certificate management
- Fail2ban intrusion prevention system
- Docker container auto-discovery and configuration
- Multiple DNS validation methods (Cloudflare, Route53, etc.)
- Subdomain and path-based routing
- Authentication integration (Authelia, OAuth)
- DDoS protection and rate limiting
- Real IP detection and logging
Prerequisitesβ
System Requirements:
- Domain: Registered domain with configurable DNS
- DNS Provider: Cloudflare, Route53, or compatible provider
- Docker: Docker and Docker Compose environment
- Network: Port 80 and 443 access from internet
- Storage: 2GB available space for configuration and logs
Network Prerequisites:
- Port Forwarding: 80/tcp and 443/tcp forwarded to SWAG container
- Domain DNS: A/AAAA records pointing to your public IP
- API Access: DNS provider API credentials for automatic validation
Installationβ
Unraid Docker Templateβ
SWAG / LinuxServer.io Repository / Web Servers
Installation Steps:
- Head to the Community Applications store in Unraid
- Search for and click to install SWAG from LinuxServer.io
- Configure the container settings:
- Network Type: Custom Docker network (recommended)
- WebUI Ports: 443 (HTTPS), 80 (HTTP)
- Config Path:
/mnt/user/appdata/swag
- Configure Environment Variables:
- URL: Your domain name (example.com)
- SUBDOMAINS: wildcard or specific subdomains
- VALIDATION: dns (recommended) or http
- DNSPLUGIN: cloudflare, route53, etc.
- EMAIL: Your email for Let's Encrypt notifications
- PUID/PGID: 1000/1000
- TZ: Your timezone
- DNS Provider Configuration:
- Cloudflare: Set CF_API_TOKEN or CF_EMAIL + CF_API_KEY
- Route53: Set AWS credentials
- Click Apply and wait for certificate generation
Docker Composeβ
version: '3.8'
services:
swag:
image: lscr.io/linuxserver/swag:latest
container_name: swag
restart: unless-stopped
cap_add:
- NET_ADMIN
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- URL=yourdomain.com
- SUBDOMAINS=wildcard
- VALIDATION=dns
- DNSPLUGIN=cloudflare
- EMAIL=your-email@example.com
- CF_API_TOKEN=your_cloudflare_api_token
volumes:
- ./swag-config:/config
ports:
- "443:443"
- "80:80"
networks:
- swag-network
networks:
swag-network:
driver: bridge
Installation Steps:
- Save the above configuration as
docker-compose.yml
- Configure your domain and DNS provider credentials
- Create the swag network:
docker network create swag-network
- Start the container:
docker compose up -d
- Monitor logs for certificate generation:
docker logs -f swag
DNS Provider Configurationβ
Cloudflare Setupβ
API Token Creation:
- Visit https://dash.cloudflare.com/profile/api-tokens
- Click "Create Token"
- Use "Custom token" template
- Permissions:
- Zone:Zone:Read
- Zone:DNS:Edit
- Zone Resources:
- Include β Specific zone β yourdomain.com
- Copy the generated token
SWAG Configuration:
environment:
- DNSPLUGIN=cloudflare
- CF_API_TOKEN=your_cloudflare_api_token
Route53 Setupβ
AWS Credentials:
environment:
- DNSPLUGIN=route53
- AWS_ACCESS_KEY_ID=your_access_key
- AWS_SECRET_ACCESS_KEY=your_secret_key
- AWS_DEFAULT_REGION=us-east-1
Duck DNS Setupβ
Duck DNS Configuration:
environment:
- DNSPLUGIN=duckdns
- DUCKDNSTOKEN=your_duckdns_token
Reverse Proxy Configurationβ
Container Auto-Discoveryβ
Docker Labels Method:
# Example application with SWAG labels
services:
app:
image: nginx
container_name: my-app
labels:
- "swag=enable"
- "swag.port=80"
- "swag.subdomain=app"
networks:
- swag-network
SWAG Environment Variables:
swag:
environment:
- DOCKER_MODS=linuxserver/mods:swag-auto-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
Manual Nginx Configurationβ
Subdomain Configuration:
# /config/nginx/proxy-confs/app.subdomain.conf
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name app.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
location / {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app my-app;
set $upstream_port 80;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
Path-Based Configuration:
# /config/nginx/proxy-confs/app.subfolder.conf
location /app {
return 301 $scheme://$host/app/;
}
location ^~ /app/ {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app my-app;
set $upstream_port 80;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port/;
}
Advanced Proxy Configurationβ
WebSocket Support:
# WebSocket configuration
location /ws {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app websocket-app;
set $upstream_port 8080;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Custom Headers and Timeouts:
# Custom proxy configuration
location / {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app my-app;
set $upstream_port 80;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
# Custom headers
proxy_set_header X-Custom-Header "Value";
# Timeouts
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# Buffer settings
proxy_buffering off;
proxy_request_buffering off;
}
Authentication Integrationβ
Authelia Setupβ
Authelia Docker Configuration:
services:
authelia:
image: authelia/authelia:latest
container_name: authelia
restart: unless-stopped
environment:
- TZ=America/New_York
volumes:
- ./authelia-config:/config
networks:
- swag-network
SWAG + Authelia Integration:
# /config/nginx/proxy-confs/app.subdomain.conf
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name app.*;
include /config/nginx/ssl.conf;
# Authelia protection
include /config/nginx/authelia-server.conf;
client_max_body_size 0;
location / {
include /config/nginx/authelia-location.conf;
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app my-app;
set $upstream_port 80;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
OAuth Integrationβ
OAuth Configuration:
# OAuth endpoint configuration
location /oauth2/ {
proxy_pass http://oauth2-proxy:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
}
location /oauth2/auth {
proxy_pass http://oauth2-proxy:4180;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
Security Configurationβ
Fail2ban Setupβ
Fail2ban Configuration:
swag:
environment:
- DOCKER_MODS=linuxserver/mods:swag-fail2ban
cap_add:
- NET_ADMIN
Custom Fail2ban Rules:
# /config/fail2ban/jail.local
[nginx-http-auth]
enabled = true
port = http,https
logpath = /config/log/nginx/error.log
[nginx-noscript]
enabled = true
port = http,https
logpath = /config/log/nginx/access.log
[nginx-badbots]
enabled = true
port = http,https
logpath = /config/log/nginx/access.log
Rate Limitingβ
Rate Limiting Configuration:
# /config/nginx/nginx.conf (in http block)
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# In location block
location /login {
limit_req zone=login burst=3 nodelay;
# ... proxy configuration
}
location /api {
limit_req zone=api burst=20 nodelay;
# ... proxy configuration
}
SSL/TLS Securityβ
SSL Configuration:
# /config/nginx/ssl.conf
ssl_certificate /config/keys/letsencrypt/fullchain.pem;
ssl_certificate_key /config/keys/letsencrypt/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozTLS:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
Monitoring and Maintenanceβ
Log Managementβ
Log Configuration:
# /config/nginx/nginx.conf
error_log /config/log/nginx/error.log warn;
http {
access_log /config/log/nginx/access.log combined;
# Custom log format
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
}
Log Analysis:
# View real-time access logs
docker exec swag tail -f /config/log/nginx/access.log
# Check error logs
docker exec swag tail -f /config/log/nginx/error.log
# Analyze most frequent visitors
docker exec swag awk '{print $1}' /config/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
# Check SSL certificate status
docker exec swag openssl x509 -in /config/keys/letsencrypt/fullchain.pem -text -noout
Certificate Managementβ
Certificate Renewal:
# Force certificate renewal
docker exec swag certbot renew --force-renewal
# Check certificate expiration
docker exec swag certbot certificates
# Test renewal process
docker exec swag certbot renew --dry-run
Certificate Monitoring:
#!/bin/bash
# cert-monitor.sh
CERT_FILE="/path/to/swag-config/keys/letsencrypt/fullchain.pem"
EXPIRE_DATE=$(openssl x509 -enddate -noout -in "$CERT_FILE" | cut -d= -f2)
EXPIRE_EPOCH=$(date -d "$EXPIRE_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRE_EPOCH - $NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 30 ]; then
echo "Certificate expires in $DAYS_LEFT days!"
fi
Troubleshootingβ
Common Issuesβ
Certificate Generation Failures:
# Check DNS propagation
dig TXT _acme-challenge.yourdomain.com
# Verify API credentials
docker exec swag certbot certificates
# Check container logs
docker logs swag 2>&1 | grep -i error
Proxy Connection Issues:
# Test upstream connectivity
docker exec swag curl -I http://target-container:80
# Check network connectivity
docker network inspect swag-network
# Verify container names resolution
docker exec swag nslookup target-container
SSL/TLS Issues:
# Test SSL configuration
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
# Check cipher suites
nmap --script ssl-enum-ciphers -p 443 yourdomain.com
# Verify certificate chain
curl -I https://yourdomain.com
Performance Optimizationβ
Nginx Optimization:
# /config/nginx/nginx.conf
worker_processes auto;
worker_connections 1024;
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript;
# Caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Container Resource Limits:
services:
swag:
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
cpus: '1.0'
memory: 512M
Advanced Configurationβ
Multi-Domain Setupβ
Multiple Domain Configuration:
swag:
environment:
- URL=domain1.com,domain2.com
- SUBDOMAINS=wildcard
- VALIDATION=dns
Custom Nginx Modulesβ
Docker Mods:
swag:
environment:
- DOCKER_MODS=linuxserver/mods:universal-docker|linuxserver/mods:swag-cloudflare-real-ip|linuxserver/mods:swag-auto-reload
GeoIP Blockingβ
GeoIP Configuration:
# /config/nginx/nginx.conf
http {
geoip_country /config/geoip/GeoIP.dat;
map $geoip_country_code $allowed_country {
default no;
US yes;
CA yes;
}
}
# In server block
if ($allowed_country = no) {
return 403;
}
Backup and Recoveryβ
Configuration Backupβ
Backup Script:
#!/bin/bash
# swag-backup.sh
BACKUP_DIR="/backup/swag"
CONFIG_DIR="/path/to/swag-config"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Backup configuration (excluding logs)
tar -czf "$BACKUP_DIR/swag-config-$DATE.tar.gz" \
--exclude="$CONFIG_DIR/log" \
--exclude="$CONFIG_DIR/fail2ban/db" \
"$CONFIG_DIR"
# Keep only last 30 backups
find "$BACKUP_DIR" -name "swag-config-*.tar.gz" -mtime +30 -delete
Disaster Recoveryβ
Recovery Procedures:
# Restore SWAG configuration
docker stop swag
tar -xzf /backup/swag/swag-config-latest.tar.gz -C /
docker start swag
# Force certificate regeneration if needed
docker exec swag rm -rf /config/keys/letsencrypt
docker restart swag
Integration Examplesβ
Complete Homelab Stackβ
Full Stack Configuration:
version: '3.8'
services:
swag:
image: lscr.io/linuxserver/swag:latest
container_name: swag
restart: unless-stopped
cap_add:
- NET_ADMIN
environment:
- URL=homelab.example.com
- SUBDOMAINS=wildcard
- VALIDATION=dns
- DNSPLUGIN=cloudflare
- CF_API_TOKEN=token
- DOCKER_MODS=linuxserver/mods:swag-auto-proxy
volumes:
- ./swag:/config
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "443:443"
- "80:80"
networks:
- homelab
portainer:
image: portainer/portainer-ce
container_name: portainer
labels:
- "swag=enable"
- "swag.port=9000"
- "swag.subdomain=portainer"
networks:
- homelab
networks:
homelab:
driver: bridge
Special Thanksβ
- LinuxServer.io Team for developing and maintaining the excellent SWAG container
- Let's Encrypt for providing free SSL certificates
- Nginx Community for the powerful web server and reverse proxy
- To our fantastic Discord community and our Admins DiscDuck and Hawks for their input and documentation (as always)
Please support the developers and creators involved in this work to help show them some love. β€οΈ
Final Wordsβ
We hope you enjoyed this guide. It was conceptualized, written, and implemented by our Admin Sycotix.
Support Usβ
Our work sometimes takes months to research and develop.
If you want to help support us please consider:
- Liking and Subscribing to our Youtube channel
- Joining our Discord server
- Becoming a paid member on our IBRACORP website
- Donating via Paypal
Thank you for being part of our community!