Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions nginx-proxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Nginx Proxy

A custom nginx-proxy image based on [jwilder/nginx-proxy](https://github.com/nginx-proxy/nginx-proxy) with additional features for WordPress and EasyEngine environments.

## Features

- Automatic reverse proxy configuration via Docker container labels
- SSL/TLS support with automatic certificate detection
- HTTP Basic Authentication support
- Wildcard HTTP Auth for WordPress Multisite
- Custom vhost configurations
- Access Control Lists (ACL)

---

## HTTP Basic Authentication

### Standard Authentication

Create htpasswd files in `/etc/nginx/htpasswd/` to enable HTTP auth:

```bash
# For a specific domain
htpasswd -c /etc/nginx/htpasswd/example.com username

# Default auth for all sites without specific htpasswd
htpasswd -c /etc/nginx/htpasswd/default username
```

### Wildcard Authentication (WordPress Multisite)

For WordPress multisite with subdomain configuration, you can use a single htpasswd file to protect both the main domain and all subdomains.

#### Naming Convention

Use the `_wildcard.` prefix:

```
/etc/nginx/htpasswd/_wildcard.domain.com
```

This file will apply HTTP auth to:
- `domain.com` (main domain)
- `*.domain.com` (all subdomains like `blog.domain.com`, `shop.domain.com`, etc.)

#### Lookup Order

The template checks for htpasswd files in this order:

1. **Exact match**: `/etc/nginx/htpasswd/blog.domain.com`
2. **Wildcard (3 parts)**: `/etc/nginx/htpasswd/_wildcard.domain.co.in` (for 4+ part domains only)
3. **Wildcard (2 parts)**: `/etc/nginx/htpasswd/_wildcard.example.com` (for 2-3 part domains, or fallback)
4. **Default**: `/etc/nginx/htpasswd/default`

#### Example Setup

```bash
# Create wildcard htpasswd for WordPress multisite
htpasswd -c /etc/nginx/htpasswd/_wildcard.example.com admin

# This protects: example.com, blog.example.com, shop.example.com, etc.

# Optional: Override for a specific subdomain
htpasswd -c /etc/nginx/htpasswd/api.example.com api_user
```

#### Multi-level TLDs

Multi-level TLDs (e.g., `.co.in`, `.com.au`) are fully supported:

| Host | Wildcard File Checked |
|------|----------------------|
| `blog.domain.co.in` (4 parts) | `_wildcard.domain.co.in` first, then `_wildcard.co.in` |
| `domain.co.in` (3 parts) | `_wildcard.co.in` |
| `blog.example.com` (3 parts) | `_wildcard.example.com` |
| `example.com` (2 parts) | `_wildcard.example.com` |

```bash
# For domain.co.in multisite (multi-level TLD)
htpasswd -c /etc/nginx/htpasswd/_wildcard.domain.co.in admin

# This will protect:
# - domain.co.in
# - blog.domain.co.in
# - shop.domain.co.in
# - etc.
```

---

## Access Control Lists (ACL)

Create ACL files to restrict access by IP:

```bash
# Per-domain ACL
/etc/nginx/vhost.d/example.com_acl

# Default ACL for all sites
/etc/nginx/vhost.d/default_acl
```

Example ACL content:
```nginx
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
```

---

## Custom Vhost Configuration

### Per-domain configuration

```bash
# Main vhost config
/etc/nginx/vhost.d/example.com

# Location-specific config
/etc/nginx/vhost.d/example.com_location
```

### Default configuration

```bash
/etc/nginx/vhost.d/default
/etc/nginx/vhost.d/default_location
```

---

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `VIRTUAL_HOST` | Comma-separated list of domains | - |
| `VIRTUAL_PORT` | Port to proxy to | `80` |
| `VIRTUAL_PROTO` | Protocol (`http`, `https`, `uwsgi`, `fastcgi`) | `http` |
| `HTTPS_METHOD` | `redirect`, `noredirect`, `nohttps` | `redirect` |
| `SSL_POLICY` | SSL/TLS policy | `Mozilla-Modern` |
| `HSTS` | HSTS header value | `max-age=31536000` |
| `CERT_NAME` | Custom certificate name | auto-detected |
| `NETWORK_ACCESS` | `external` or `internal` | `external` |

---

## Docker Compose Example

```yaml
services:
nginx-proxy:
image: your-nginx-proxy-image
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs:ro
- ./htpasswd:/etc/nginx/htpasswd:ro
- ./vhost.d:/etc/nginx/vhost.d:ro

wordpress-multisite:
image: wordpress
environment:
- VIRTUAL_HOST=example.com,*.example.com
# HTTP auth via /etc/nginx/htpasswd/_wildcard.example.com
```
107 changes: 92 additions & 15 deletions nginx-proxy/nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,90 @@
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{ end }}
{{ else if (exists "/etc/nginx/htpasswd/default") }}
auth_basic "Restricted {{ .Host }}";
auth_basic_user_file /etc/nginx/htpasswd/default;
{{ if (exists (printf "/etc/nginx/vhost.d/%s_acl" .Host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_acl" .Host}};
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{/*
Wildcard htpasswd support for WordPress Multisite.
Naming convention: _wildcard.domain.com applies to domain.com AND *.domain.com
Supports multi-level TLDs: _wildcard.domain.co.in works for domain.co.in AND *.domain.co.in

Lookup order (after exact match check on line 56):
- For 4+ part domains: checks _wildcard.{last-3-parts}, then _wildcard.{last-2-parts}, then default
- For 2-3 part domains: checks _wildcard.{last-2-parts}, then falls back to default
- For single-part hostnames: uses default only

Note: Uses sprig's splitList and sub functions (available in docker-gen 0.7.4+)
*/}}
{{ else }}
{{ $hostParts := splitList "." .Host }}
{{ $partsLen := len $hostParts }}
{{/* For 4+ part domains, check last 3 parts first (e.g., _wildcard.domain.co.in for blog.domain.co.in) */}}
{{ if ge $partsLen 4 }}
{{ $idx3 := sub $partsLen 3 }}
{{ $idx2 := sub $partsLen 2 }}
{{ $idx1 := sub $partsLen 1 }}
{{ $baseDomain3 := printf "%s.%s.%s" (index $hostParts $idx3) (index $hostParts $idx2) (index $hostParts $idx1) }}
{{ $wildcardHtpasswd3 := printf "/etc/nginx/htpasswd/_wildcard.%s" $baseDomain3 }}
{{ if (exists $wildcardHtpasswd3) }}
auth_basic "Restricted {{ .Host }}";
auth_basic_user_file {{ ($wildcardHtpasswd3) }};
{{ if (exists (printf "/etc/nginx/vhost.d/%s_acl" .Host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_acl" .Host}};
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{ end }}
{{ else }}
{{/* Fallback: check last 2 parts (e.g., _wildcard.co.in for blog.domain.co.in) */}}
{{ $baseDomain2 := printf "%s.%s" (index $hostParts $idx2) (index $hostParts $idx1) }}
{{ $wildcardHtpasswd2 := printf "/etc/nginx/htpasswd/_wildcard.%s" $baseDomain2 }}
{{ if (exists $wildcardHtpasswd2) }}
auth_basic "Restricted {{ .Host }}";
auth_basic_user_file {{ ($wildcardHtpasswd2) }};
{{ if (exists (printf "/etc/nginx/vhost.d/%s_acl" .Host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_acl" .Host}};
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{ end }}
{{ else if (exists "/etc/nginx/htpasswd/default") }}
auth_basic "Restricted {{ .Host }}";
auth_basic_user_file /etc/nginx/htpasswd/default;
{{ if (exists (printf "/etc/nginx/vhost.d/%s_acl" .Host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_acl" .Host}};
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{ end }}
{{ end }}
{{ end }}
{{ else if ge $partsLen 2 }}
{{/* For 2-3 part domains, check last 2 parts (e.g., _wildcard.example.com for blog.example.com or example.com) */}}
{{ $idx2 := sub $partsLen 2 }}
{{ $idx1 := sub $partsLen 1 }}
{{ $baseDomain2 := printf "%s.%s" (index $hostParts $idx2) (index $hostParts $idx1) }}
{{ $wildcardHtpasswd2 := printf "/etc/nginx/htpasswd/_wildcard.%s" $baseDomain2 }}
{{ if (exists $wildcardHtpasswd2) }}
auth_basic "Restricted {{ .Host }}";
auth_basic_user_file {{ ($wildcardHtpasswd2) }};
{{ if (exists (printf "/etc/nginx/vhost.d/%s_acl" .Host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_acl" .Host}};
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{ end }}
{{ else if (exists "/etc/nginx/htpasswd/default") }}
auth_basic "Restricted {{ .Host }}";
auth_basic_user_file /etc/nginx/htpasswd/default;
{{ if (exists (printf "/etc/nginx/vhost.d/%s_acl" .Host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_acl" .Host}};
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{ end }}
{{ end }}
{{ else if (exists "/etc/nginx/htpasswd/default") }}
{{/* Single-part hostname - use default */}}
auth_basic "Restricted {{ .Host }}";
auth_basic_user_file /etc/nginx/htpasswd/default;
{{ if (exists (printf "/etc/nginx/vhost.d/%s_acl" .Host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_acl" .Host}};
{{ else if (exists "/etc/nginx/vhost.d/default_acl") }}
include /etc/nginx/vhost.d/default_acl;
{{ end }}
{{ end }}
{{ end }}

Expand Down Expand Up @@ -146,8 +223,8 @@ map $scheme $proxy_x_forwarded_ssl {
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

log_format vhost '$host $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

{{ if $.Env.RESOLVERS }}
resolver {{ $.Env.RESOLVERS }};
Expand Down Expand Up @@ -190,14 +267,14 @@ server {
{{ end }}

root /etc/nginx/html;

# Custom error page for 503
error_page 503 /default.html;

location / {
return 503;
}

# Serve the error page without redirect
location = /default.html {
root /etc/nginx/html;
Expand All @@ -219,14 +296,14 @@ server {
{{ end }}

root /etc/nginx/html;

# Custom error page for 503
error_page 503 /default.html;

location / {
return 503;
}

# Serve the error page without redirect
location = /default.html {
root /etc/nginx/html;
Expand Down
Loading