# nginx-proxy + acme-companion Automated reverse proxy with Let's Encrypt SSL certificates. ## Setup 1. Create the data directories: ```bash sudo mkdir -p /srv/nginx-proxy-acme/{certs,vhost.d,html,conf.d,acme} ``` 2. Copy configuration files: ```bash sudo cp conf/conf.d/* /srv/nginx-proxy-acme/conf.d/ sudo cp conf/vhost.d/* /srv/nginx-proxy-acme/vhost.d/ ``` 3. Configure environment: ```bash cp .env.example .env # Edit .env with your email ``` 4. Start the proxy: ```bash docker compose up -d ``` ## Architecture - **nginx-proxy**: Reverse proxy with auto-discovery of Docker containers - **acme-companion**: Automatic Let's Encrypt certificate management - **static-certs**: Dummy container that triggers cert issuance for non-container backends ## Security All hosts receive these security features via `vhost.d/default`: - **HSTS**: Strict-Transport-Security header (1 year) - **Security headers**: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy - **Block exploits**: WAF rules against SQL injection, XSS, file injection, spam, bad user agents ## Access Control ### Private Hosts (IP restricted) Private hosts include `vhost.d/private` which restricts access to: - 192.168.1.0/24 (local network) - 172.16.0.0/12 (Docker networks) To make a new host private, create a vhost.d file: ```bash echo 'include /etc/nginx/vhost.d/private;' | sudo tee /srv/nginx-proxy-acme/vhost.d/myapp.kolpacksoftware.com ``` ### Public Hosts Public hosts have no vhost.d file (only `default` applies). They get security headers and block-exploits but no IP restrictions. Current public hosts: - chd.kolpacksoftware.com - linkding.kolpacksoftware.com - organizer.rmstsa.org - ridge-resources.org - rmstsa.org / www.rmstsa.org - share.kolpacksoftware.com - vikunja.kolpacksoftware.com ## Adding a Proxied Service Add these environment variables to any container: ```yaml services: myapp: image: myapp:latest environment: - VIRTUAL_HOST=myapp.kolpacksoftware.com - VIRTUAL_PORT=8080 - LETSENCRYPT_HOST=myapp.kolpacksoftware.com networks: - npm-network networks: npm-network: external: true ``` Then if private, add the vhost.d file as shown above. ## Static IP Backends Services running on physical hosts or VMs (not Docker) are configured in `conf.d/static-upstreams.conf`: | Domain | Backend | |--------|---------| | portainer.kolpacksoftware.com | 172.17.0.1:9443 | | btt-cb1.kolpacksoftware.com | 192.168.1.173:80 | | hats.kolpacksoftware.com | 192.168.1.66:9999 | | pve-nas.kolpacksoftware.com | 192.168.1.245:8006 | | unraid.kolpacksoftware.com | 192.168.1.192:80 | All static backends are private (IP restricted). To add a new static backend: 1. Add server block to `conf.d/static-upstreams.conf` 2. Add domain to `static-certs` container's VIRTUAL_HOST and LETSENCRYPT_HOST in docker-compose.yaml 3. Reload: `docker exec nginx-proxy nginx -s reload` ## Directory Structure ``` /srv/nginx-proxy-acme/ ├── acme/ # acme.sh state (auto-managed) ├── certs/ # SSL certificates (auto-managed) ├── conf.d/ │ ├── block-exploits.conf # WAF rules │ └── static-upstreams.conf # Static IP backend server blocks ├── html/ # ACME challenge files (auto-managed) └── vhost.d/ ├── default # Security headers + HSTS + block-exploits (all hosts) ├── private # IP allowlist (private hosts include this) ├── docker-registry.* # Special config (auth headers + private) └── # Per-host includes (usually just 'include private') ``` ## Reload Config After changing vhost.d or conf.d files: ```bash docker exec nginx-proxy nginx -s reload ``` ## Multiple Domains For multiple domains on one cert: ```yaml environment: - VIRTUAL_HOST=example.com,www.example.com - LETSENCRYPT_HOST=example.com,www.example.com ``` Create vhost.d entries for each domain if private.