Add detailed plugin documentation for Cleanup, Cloudflare, Deny Pages, IP Whitelist, JWT Validator, and FastCGI plugins
- Added individual markdown files with examples, configuration options, and HAProxy outputs for each plugin. - Updated `README.md` to link plugin-specific documentation. - Enhanced `plugins.md` to summarize plugin features and usage. - Included Docker, Kubernetes, Swarm, and static configuration examples for all plugins.
This commit is contained in:
parent
883521e7e1
commit
ae6eb1b55a
10 changed files with 2431 additions and 1317 deletions
|
|
@ -88,7 +88,13 @@ Detailed configuration guides for advanced setups:
|
|||
- [Container Labels](docs/container-labels.md) - Configure Docker/Swarm containers with labels
|
||||
- [Environment Variables](docs/environment-variable.md) - Configure EasyHAProxy behavior
|
||||
- [Volumes](docs/volumes.md) - Map volumes for certificates, config, and custom files
|
||||
- [Plugins](docs/plugins.md) - Extend HAProxy with custom plugins
|
||||
- [Plugins](docs/plugins.md) - Extend HAProxy with plugins ([Development Guide](docs/plugin-development.md))
|
||||
- [Cloudflare](docs/plugins/cloudflare.md) - Restore visitor IP from Cloudflare CDN
|
||||
- [Cleanup](docs/plugins/cleanup.md) - Automatic cleanup of temporary files
|
||||
- [Deny Pages](docs/plugins/deny-pages.md) - Block access to specific paths
|
||||
- [IP Whitelist](docs/plugins/ip-whitelist.md) - Restrict access to IPs/CIDR ranges
|
||||
- [JWT Validator](docs/plugins/jwt-validator.md) - JWT authentication validation
|
||||
- [FastCGI](docs/plugins/fastcgi.md) - PHP-FPM and FastCGI application support
|
||||
- [Other Configurations](docs/other.md) - Additional configurations (ports, custom errors, etc.)
|
||||
- [Limitations](docs/limitations.md) - Important limitations and considerations
|
||||
|
||||
|
|
|
|||
|
|
@ -90,16 +90,16 @@ You don't need to expose any port in your container.
|
|||
|
||||
## Kubernetes annotations
|
||||
|
||||
| annotation | Description | Default | Example |
|
||||
|----------------------------------|-------------------------------------------------------------------------------------|--------------|----------------------------|
|
||||
| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress |
|
||||
| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false |
|
||||
| easyhaproxy.certbot | (optional) Boolean. It will request certbot certificates for the ingresses domains. | false | true or false |
|
||||
| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | \{"domain":"redirect_url"} |
|
||||
| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp |
|
||||
| easyhaproxy.listen_port | (optional) Override the HTTP listen port created for that ingress | 80 | 8081 |
|
||||
| easyhaproxy.plugins | (optional) Comma-separated list of plugins to enable for this ingress | *empty* | cloudflare,deny_pages |
|
||||
| easyhaproxy.plugin.{name}.{key} | (optional) Plugin-specific configuration (see [Using Plugins](plugins.md)) | *varies* | See examples below |
|
||||
| annotation | Description | Default | Example |
|
||||
|-------------------------------------|-------------------------------------------------------------------------------------|--------------|----------------------------|
|
||||
| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress |
|
||||
| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false |
|
||||
| easyhaproxy.certbot | (optional) Boolean. It will request certbot certificates for the ingresses domains. | false | true or false |
|
||||
| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | \{"domain":"redirect_url"} |
|
||||
| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp |
|
||||
| easyhaproxy.listen_port | (optional) Override the HTTP listen port created for that ingress | 80 | 8081 |
|
||||
| easyhaproxy.plugins | (optional) Comma-separated list of plugins to enable for this ingress | *empty* | cloudflare,deny_pages |
|
||||
| easyhaproxy.plugin.`{name}`.`{key}` | (optional) Plugin-specific configuration (see [Using Plugins](plugins.md)) | *varies* | See examples below |
|
||||
|
||||
**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration.
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ metadata:
|
|||
easyhaproxy.plugin.jwt_validator.pubkey_path: "/etc/haproxy/jwt_keys/api_pubkey.pem"
|
||||
```
|
||||
|
||||
**Note:** For JWT validation, you'll need to mount the public key file into the EasyHAProxy pod. See [Using Plugins](plugins.md#jwt-validator-plugin-domain) for details.
|
||||
**Note:** For JWT validation, you'll need to mount the public key file into the EasyHAProxy pod. See [Using Plugins](plugins.md#protect-api-with-jwt-authentication) for details.
|
||||
|
||||
**Restrict access to specific IPs:**
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
373
docs/plugins.md
373
docs/plugins.md
|
|
@ -42,368 +42,14 @@ Execute **once for each discovered domain/host**.
|
|||
|
||||
## Built-in Plugins
|
||||
|
||||
### Cloudflare Plugin (Domain)
|
||||
|
||||
Restores the original visitor IP address when requests come through Cloudflare's CDN.
|
||||
|
||||
**Why use it:** Cloudflare replaces the visitor's IP with its own. This plugin restores the original IP from the `CF-Connecting-IP` header.
|
||||
|
||||
**Configuration options:**
|
||||
- `enabled` - Enable/disable plugin (default: `true`)
|
||||
- `ip_list_path` - Path to Cloudflare IP list (default: `/etc/haproxy/cloudflare_ips.lst`)
|
||||
|
||||
**Enable via container label:**
|
||||
```yaml
|
||||
services:
|
||||
myapp:
|
||||
labels:
|
||||
easyhaproxy.http.host: example.com
|
||||
easyhaproxy.http.plugins: cloudflare
|
||||
```
|
||||
|
||||
**Custom IP list path:**
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: cloudflare
|
||||
easyhaproxy.http.plugin.cloudflare.ip_list_path: /custom/path/cf_ips.lst
|
||||
```
|
||||
|
||||
**HAProxy config generated:**
|
||||
```
|
||||
# Cloudflare - Restore original visitor IP
|
||||
acl from_cloudflare src -f /etc/haproxy/cloudflare_ips.lst
|
||||
http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)] if from_cloudflare
|
||||
```
|
||||
|
||||
**Required:** Download Cloudflare IP list from [Cloudflare documentation](https://support.cloudflare.com/hc/en-us/articles/200170786).
|
||||
|
||||
### Cleanup Plugin (Global)
|
||||
|
||||
Performs cleanup tasks during each discovery cycle, such as removing old temporary files.
|
||||
|
||||
**Why use it:** Prevents disk space issues by automatically cleaning up temporary files created by EasyHAProxy.
|
||||
|
||||
**Configuration options:**
|
||||
- `enabled` - Enable/disable plugin (default: `true`)
|
||||
- `max_idle_time` - Maximum age in seconds before deleting files (default: `300`)
|
||||
- `cleanup_temp_files` - Enable temp file cleanup (default: `true`)
|
||||
|
||||
**Enable via YAML:**
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
plugins:
|
||||
enabled: [cleanup]
|
||||
config:
|
||||
cleanup:
|
||||
max_idle_time: 600
|
||||
cleanup_temp_files: true
|
||||
```
|
||||
|
||||
**Enable via environment variable:**
|
||||
```bash
|
||||
EASYHAPROXY_PLUGINS_ENABLED=cleanup
|
||||
EASYHAPROXY_PLUGIN_CLEANUP_MAX_IDLE_TIME=600
|
||||
```
|
||||
|
||||
### Deny Pages Plugin (Domain)
|
||||
|
||||
Blocks access to specific paths for a domain, returning a configurable HTTP status code.
|
||||
|
||||
**Why use it:** Protect admin panels, internal APIs, or debugging endpoints from public access.
|
||||
|
||||
**Configuration options:**
|
||||
- `enabled` - Enable/disable plugin (default: `true`)
|
||||
- `paths` - Comma-separated list of paths to block (e.g., `/admin,/private`)
|
||||
- `status_code` - HTTP status code to return (default: `403`)
|
||||
|
||||
**Enable via container label:**
|
||||
```yaml
|
||||
services:
|
||||
webapp:
|
||||
labels:
|
||||
easyhaproxy.http.host: example.com
|
||||
easyhaproxy.http.plugins: deny_pages
|
||||
easyhaproxy.http.plugin.deny_pages.paths: /admin,/private,/debug
|
||||
easyhaproxy.http.plugin.deny_pages.status_code: 404
|
||||
```
|
||||
|
||||
**HAProxy config generated:**
|
||||
```
|
||||
# Deny Pages - Block specific paths
|
||||
acl denied_path path_beg /admin /private /debug
|
||||
http-request deny deny_status 404 if denied_path
|
||||
```
|
||||
|
||||
### IP Whitelist Plugin (Domain)
|
||||
|
||||
Restricts access to a domain to only specific IP addresses or CIDR ranges.
|
||||
|
||||
**Why use it:** Restrict access to internal tools, admin panels, or staging environments to only trusted IP addresses.
|
||||
|
||||
**Configuration options:**
|
||||
- `enabled` - Enable/disable plugin (default: `true`)
|
||||
- `allowed_ips` - Comma-separated list of IPs/CIDR ranges to allow (e.g., `192.168.1.0/24,10.0.0.1`)
|
||||
- `status_code` - HTTP status code to return for blocked IPs (default: `403`)
|
||||
|
||||
**Enable via container label:**
|
||||
```yaml
|
||||
services:
|
||||
admin:
|
||||
labels:
|
||||
easyhaproxy.http.host: admin.example.com
|
||||
easyhaproxy.http.plugins: ip_whitelist
|
||||
easyhaproxy.http.plugin.ip_whitelist.allowed_ips: 192.168.1.0/24,10.0.0.5
|
||||
easyhaproxy.http.plugin.ip_whitelist.status_code: 403
|
||||
```
|
||||
|
||||
**HAProxy config generated:**
|
||||
```
|
||||
# IP Whitelist - Only allow specific IPs
|
||||
acl whitelisted_ip src 192.168.1.0/24 10.0.0.5
|
||||
http-request deny deny_status 403 if !whitelisted_ip
|
||||
```
|
||||
|
||||
**Important:** This blocks ALL IPs except those in the whitelist. Make sure to include your own IP!
|
||||
|
||||
### JWT Validator Plugin (Domain)
|
||||
|
||||
Validates JWT (JSON Web Token) authentication tokens using HAProxy's built-in JWT functionality.
|
||||
|
||||
**Why use it:** Protect APIs and services with JWT authentication without needing application-level code.
|
||||
|
||||
**Configuration options:**
|
||||
- `enabled` - Enable/disable plugin (default: `true`)
|
||||
- `algorithm` - JWT signing algorithm (default: `RS256`)
|
||||
- `issuer` - Expected JWT issuer (optional, set to `none`/`null` to skip validation)
|
||||
- `audience` - Expected JWT audience (optional, set to `none`/`null` to skip validation)
|
||||
- `pubkey_path` - Path to public key file (required if `pubkey` not provided)
|
||||
- `pubkey` - Public key content as base64-encoded string (required if `pubkey_path` not provided)
|
||||
- `paths` - List of paths that require JWT validation (optional, if not set ALL domain is protected)
|
||||
- `only_paths` - If `true`, only specified paths are accessible; if `false` (default), only specified paths require JWT validation
|
||||
|
||||
**Path Validation Logic:**
|
||||
- **No paths configured:** ALL requests to the domain require JWT validation (default behavior)
|
||||
- **Paths configured + `only_paths=false`:** Only specified paths require JWT validation, other paths pass through without validation
|
||||
- **Paths configured + `only_paths=true`:** Only specified paths are accessible (with JWT validation), all other paths are denied
|
||||
|
||||
**Enable via container label (protect all paths):**
|
||||
```yaml
|
||||
services:
|
||||
api:
|
||||
labels:
|
||||
easyhaproxy.http.host: api.example.com
|
||||
easyhaproxy.http.plugins: jwt_validator
|
||||
easyhaproxy.http.plugin.jwt_validator.algorithm: RS256
|
||||
easyhaproxy.http.plugin.jwt_validator.issuer: https://auth.example.com/
|
||||
easyhaproxy.http.plugin.jwt_validator.audience: https://api.example.com
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
volumes:
|
||||
- ./pubkey.pem:/etc/haproxy/jwt_keys/api_pubkey.pem:ro
|
||||
```
|
||||
|
||||
**Protect specific paths only (others can pass without JWT):**
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: jwt_validator
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
easyhaproxy.http.plugin.jwt_validator.paths: /api/admin,/api/sensitive
|
||||
easyhaproxy.http.plugin.jwt_validator.only_paths: false
|
||||
```
|
||||
|
||||
**Only allow specific paths (deny all others):**
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: jwt_validator
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
easyhaproxy.http.plugin.jwt_validator.paths: /api/public,/api/v1
|
||||
easyhaproxy.http.plugin.jwt_validator.only_paths: true
|
||||
```
|
||||
|
||||
**Skip issuer/audience validation:**
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugin.jwt_validator.issuer: none
|
||||
easyhaproxy.http.plugin.jwt_validator.audience: none
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
```
|
||||
|
||||
**HAProxy config generated (all paths protected):**
|
||||
```
|
||||
# JWT Validator - Validate JWT tokens
|
||||
http-request deny content-type 'text/html' string 'Missing Authorization HTTP header' unless { req.hdr(authorization) -m found }
|
||||
|
||||
# Extract JWT header and payload
|
||||
http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg')
|
||||
http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss')
|
||||
http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud')
|
||||
http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int')
|
||||
|
||||
# Validate JWT
|
||||
http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm' unless { var(txn.alg) -m str RS256 }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT issuer' unless { var(txn.iss) -m str https://auth.example.com/ }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT audience' unless { var(txn.aud) -m str https://api.example.com }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT signature' unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/haproxy/jwt_keys/api_pubkey.pem") -m int 1 }
|
||||
|
||||
# Validate expiration
|
||||
http-request set-var(txn.now) date()
|
||||
http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int lt 0 }
|
||||
```
|
||||
|
||||
**HAProxy config generated (specific paths, only_paths=false):**
|
||||
```
|
||||
# JWT Validator - Validate JWT tokens
|
||||
|
||||
# Define paths that require JWT validation
|
||||
acl jwt_protected_path path_beg /api/admin
|
||||
acl jwt_protected_path path_beg /api/sensitive
|
||||
|
||||
http-request deny content-type 'text/html' string 'Missing Authorization HTTP header' unless { req.hdr(authorization) -m found } if jwt_protected_path
|
||||
|
||||
# Extract JWT header and payload
|
||||
http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg') if jwt_protected_path
|
||||
http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss') if jwt_protected_path
|
||||
http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud') if jwt_protected_path
|
||||
http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int') if jwt_protected_path
|
||||
|
||||
# Validate JWT (only on protected paths)
|
||||
http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm' unless { var(txn.alg) -m str RS256 } if jwt_protected_path
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT signature' unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/haproxy/jwt_keys/api_pubkey.pem") -m int 1 } if jwt_protected_path
|
||||
|
||||
# Validate expiration
|
||||
http-request set-var(txn.now) date() if jwt_protected_path
|
||||
http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int lt 0 } if jwt_protected_path
|
||||
```
|
||||
|
||||
**HAProxy config generated (specific paths, only_paths=true):**
|
||||
```
|
||||
# JWT Validator - Validate JWT tokens
|
||||
|
||||
# Define paths that require JWT validation
|
||||
acl jwt_protected_path path_beg /api/public
|
||||
acl jwt_protected_path path_beg /api/v1
|
||||
|
||||
# Deny access to paths not in the protected list
|
||||
http-request deny content-type 'text/html' string 'Access denied' unless jwt_protected_path
|
||||
|
||||
http-request deny content-type 'text/html' string 'Missing Authorization HTTP header' unless { req.hdr(authorization) -m found }
|
||||
|
||||
# Extract JWT header and payload
|
||||
http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg')
|
||||
http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss')
|
||||
http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud')
|
||||
http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int')
|
||||
|
||||
# Validate JWT (all requests at this point are on allowed paths)
|
||||
http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm' unless { var(txn.alg) -m str RS256 }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT signature' unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/haproxy/jwt_keys/api_pubkey.pem") -m int 1 }
|
||||
|
||||
# Validate expiration
|
||||
http-request set-var(txn.now) date()
|
||||
http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int lt 0 }
|
||||
```
|
||||
|
||||
**What it validates:**
|
||||
- ✅ Authorization header presence
|
||||
- ✅ JWT signing algorithm (RS256, RS512, etc.)
|
||||
- ✅ JWT issuer (if configured)
|
||||
- ✅ JWT audience (if configured)
|
||||
- ✅ JWT signature using public key
|
||||
- ✅ JWT expiration time
|
||||
|
||||
**Important:** Requires HAProxy 2.5+ with JWT support. Mount public key file as read-only volume.
|
||||
|
||||
### FastCGI Plugin (Domain)
|
||||
|
||||
Configures FastCGI parameters for PHP-FPM and other FastCGI applications.
|
||||
|
||||
**Why use it:** Automatically generates HAProxy `fcgi-app` configuration that defines required CGI parameters for PHP-FPM communication without manual HAProxy configuration.
|
||||
|
||||
**Configuration options:**
|
||||
- `enabled` - Enable/disable plugin (default: `true`)
|
||||
- `document_root` - Document root path (default: `/var/www/html`)
|
||||
- `script_filename` - Custom pattern for SCRIPT_FILENAME (default: `%[path]`, uses HAProxy's default)
|
||||
- `index_file` - Default index file (default: `index.php`)
|
||||
- `path_info` - Enable PATH_INFO support (default: `true`)
|
||||
- `custom_params` - Dictionary of custom FastCGI parameters (optional)
|
||||
|
||||
**Enable via container label (TCP connection):**
|
||||
```yaml
|
||||
services:
|
||||
php-fpm:
|
||||
image: php:8.2-fpm
|
||||
labels:
|
||||
easyhaproxy.http.host: phpapp.local
|
||||
easyhaproxy.http.port: 80
|
||||
easyhaproxy.http.localport: 9000
|
||||
easyhaproxy.http.proto: fcgi
|
||||
easyhaproxy.http.plugins: fastcgi
|
||||
easyhaproxy.http.plugin.fastcgi.document_root: /var/www/html
|
||||
easyhaproxy.http.plugin.fastcgi.index_file: index.php
|
||||
volumes:
|
||||
- ./app:/var/www/html
|
||||
```
|
||||
|
||||
**Or with Unix socket:**
|
||||
```yaml
|
||||
services:
|
||||
php-fpm:
|
||||
image: php:8.2-fpm
|
||||
labels:
|
||||
easyhaproxy.http.host: phpapp.local
|
||||
easyhaproxy.http.socket: /run/php/php-fpm.sock
|
||||
easyhaproxy.http.proto: fcgi
|
||||
easyhaproxy.http.plugins: fastcgi
|
||||
easyhaproxy.http.plugin.fastcgi.document_root: /var/www/html
|
||||
easyhaproxy.http.plugin.fastcgi.index_file: index.php
|
||||
volumes:
|
||||
- ./app:/var/www/html
|
||||
- /run/php:/run/php
|
||||
```
|
||||
|
||||
**Custom document root and index file:**
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: fastcgi
|
||||
easyhaproxy.http.plugin.fastcgi.document_root: /var/www/myapp/public
|
||||
easyhaproxy.http.plugin.fastcgi.index_file: app.php
|
||||
easyhaproxy.http.plugin.fastcgi.path_info: true
|
||||
```
|
||||
|
||||
**HAProxy config generated:**
|
||||
|
||||
The plugin generates a top-level `fcgi-app` section and a `use-fcgi-app` directive in the backend:
|
||||
|
||||
```haproxy
|
||||
# Top-level fcgi-app definition (added after defaults, before frontends/backends)
|
||||
fcgi-app fcgi_phpapp_local
|
||||
docroot /var/www/html
|
||||
index index.php
|
||||
path-info ^(/.+\.php)(/.*)?$
|
||||
|
||||
# Backend configuration (added to the backend section)
|
||||
backend srv_phpapp_local_80
|
||||
use-fcgi-app fcgi_phpapp_local
|
||||
# TCP connection:
|
||||
server srv-0 172.19.0.3:9000 proto fcgi
|
||||
# OR Unix socket:
|
||||
# server srv-0 /run/php/php-fpm.sock proto fcgi
|
||||
```
|
||||
|
||||
**Note:** HAProxy automatically sets standard CGI parameters (SCRIPT_FILENAME, DOCUMENT_ROOT, REQUEST_URI, QUERY_STRING, REQUEST_METHOD, CONTENT_TYPE, CONTENT_LENGTH, SERVER_NAME, SERVER_PORT, etc.) based on the `fcgi-app` configuration when communicating with PHP-FPM via the FastCGI protocol.
|
||||
|
||||
**What it configures:**
|
||||
- ✅ SCRIPT_FILENAME - Path to PHP script
|
||||
- ✅ DOCUMENT_ROOT - Document root directory
|
||||
- ✅ SCRIPT_NAME - Script name from URL
|
||||
- ✅ REQUEST_URI - Full request URI with query string
|
||||
- ✅ QUERY_STRING - URL query parameters
|
||||
- ✅ REQUEST_METHOD - HTTP method (GET, POST, etc.)
|
||||
- ✅ CONTENT_TYPE & CONTENT_LENGTH - Request body info
|
||||
- ✅ SERVER_NAME & SERVER_PORT - Server details
|
||||
- ✅ HTTPS - SSL/TLS status
|
||||
- ✅ PATH_INFO - Path information (optional)
|
||||
|
||||
**Important:** Use this plugin together with `proto: fcgi` parameter for complete PHP-FPM support.
|
||||
EasyHAProxy includes several built-in plugins ready to use:
|
||||
|
||||
- [Cloudflare](plugins/cloudflare.md) - Restore visitor IP from Cloudflare CDN
|
||||
- [Cleanup](plugins/cleanup.md) - Cleanup temporary files
|
||||
- [Deny Pages](plugins/deny-pages.md) - Block specific paths
|
||||
- [IP Whitelist](plugins/ip-whitelist.md) - Restrict access to IPs/CIDR ranges
|
||||
- [JWT Validator](plugins/jwt-validator.md) - Validate JWT tokens
|
||||
- [FastCGI](plugins/fastcgi.md) - Configure PHP-FPM and FastCGI applications
|
||||
|
||||
## Configuration Methods
|
||||
|
||||
|
|
@ -746,7 +392,7 @@ Per-ingress/per-container settings override global configuration.
|
|||
|
||||
## Creating Custom Plugins
|
||||
|
||||
To create your own plugins, see the [Plugin Developer Guide](plugin-development.md).
|
||||
Want to create your own plugins? See the [Plugin Developer Guide](plugin-development.md) for detailed instructions on building custom plugins that extend EasyHAProxy functionality.
|
||||
|
||||
## Further Reading
|
||||
|
||||
|
|
@ -754,3 +400,4 @@ To create your own plugins, see the [Plugin Developer Guide](plugin-development.
|
|||
- [Container Labels](container-labels.md) - Label configuration reference
|
||||
- [Environment Variables](environment-variable.md) - Environment variable reference
|
||||
- [Static Configuration](static.md) - YAML configuration reference
|
||||
- [Kubernetes Guide](kubernetes.md) - Using plugins with Kubernetes
|
||||
|
|
|
|||
74
docs/plugins/cleanup.md
Normal file
74
docs/plugins/cleanup.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Cleanup Plugin
|
||||
|
||||
**Type:** Global Plugin
|
||||
**Runs:** Once per discovery cycle
|
||||
|
||||
## Overview
|
||||
|
||||
The Cleanup plugin performs cleanup tasks during each discovery cycle, such as removing old temporary files.
|
||||
|
||||
## Why Use It
|
||||
|
||||
Prevents disk space issues by automatically cleaning up temporary files created by EasyHAProxy.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|----------------------|----------------------------------------------|---------|
|
||||
| `enabled` | Enable/disable plugin | `true` |
|
||||
| `max_idle_time` | Maximum age in seconds before deleting files | `300` |
|
||||
| `cleanup_temp_files` | Enable temp file cleanup | `true` |
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Static YAML Configuration
|
||||
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
plugins:
|
||||
enabled: [cleanup]
|
||||
config:
|
||||
cleanup:
|
||||
max_idle_time: 600
|
||||
cleanup_temp_files: true
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
EASYHAPROXY_PLUGINS_ENABLED=cleanup
|
||||
EASYHAPROXY_PLUGIN_CLEANUP_MAX_IDLE_TIME=600
|
||||
```
|
||||
|
||||
### Custom Idle Time (1 hour)
|
||||
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
plugins:
|
||||
enabled: [cleanup]
|
||||
config:
|
||||
cleanup:
|
||||
enabled: true
|
||||
max_idle_time: 3600 # 1 hour
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
The cleanup plugin:
|
||||
- Runs once during each discovery cycle
|
||||
- Scans temporary directories for old files
|
||||
- Removes files older than `max_idle_time` seconds
|
||||
- Helps maintain disk space efficiency
|
||||
|
||||
## Important Notes
|
||||
|
||||
- This is a **global plugin** - it runs once per discovery cycle, not per domain
|
||||
- Does not generate HAProxy configuration
|
||||
- Performs maintenance operations in the background
|
||||
- Safe to enable in production environments
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Plugin System Overview](../plugins.md)
|
||||
- [Environment Variables Reference](../environment-variable.md)
|
||||
- [Static Configuration Reference](../static.md)
|
||||
91
docs/plugins/cloudflare.md
Normal file
91
docs/plugins/cloudflare.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# Cloudflare Plugin
|
||||
|
||||
**Type:** Domain Plugin
|
||||
**Runs:** Once for each discovered domain/host
|
||||
|
||||
## Overview
|
||||
|
||||
The Cloudflare plugin restores the original visitor IP address when requests come through Cloudflare's CDN.
|
||||
|
||||
## Why Use It
|
||||
|
||||
Cloudflare replaces the visitor's IP with its own. This plugin restores the original IP from the `CF-Connecting-IP` header.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|----------------|----------------------------|-----------------------------------|
|
||||
| `enabled` | Enable/disable plugin | `true` |
|
||||
| `ip_list_path` | Path to Cloudflare IP list | `/etc/haproxy/cloudflare_ips.lst` |
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Docker/Docker Compose (Basic)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
myapp:
|
||||
labels:
|
||||
easyhaproxy.http.host: example.com
|
||||
easyhaproxy.http.plugins: cloudflare
|
||||
```
|
||||
|
||||
### Docker/Docker Compose (Custom IP List Path)
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: cloudflare
|
||||
easyhaproxy.http.plugin.cloudflare.ip_list_path: /custom/path/cf_ips.lst
|
||||
```
|
||||
|
||||
### Kubernetes Annotations
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
easyhaproxy.plugins: "cloudflare"
|
||||
easyhaproxy.plugin.cloudflare.ip_list_path: "/etc/haproxy/cloudflare_ips.lst"
|
||||
spec:
|
||||
rules:
|
||||
- host: example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: myapp
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
### Static YAML Configuration
|
||||
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
plugins:
|
||||
config:
|
||||
cloudflare:
|
||||
enabled: true
|
||||
ip_list_path: /etc/haproxy/cloudflare_ips.lst
|
||||
```
|
||||
|
||||
## Generated HAProxy Configuration
|
||||
|
||||
```haproxy
|
||||
# Cloudflare - Restore original visitor IP
|
||||
acl from_cloudflare src -f /etc/haproxy/cloudflare_ips.lst
|
||||
http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)] if from_cloudflare
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Required:** Download Cloudflare IP list from [Cloudflare documentation](https://support.cloudflare.com/hc/en-us/articles/200170786)
|
||||
- The plugin runs once per domain during the discovery cycle
|
||||
- Ensure the IP list file is mounted and accessible to HAProxy
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Plugin System Overview](../plugins.md)
|
||||
- [Container Labels Reference](../container-labels.md)
|
||||
113
docs/plugins/deny-pages.md
Normal file
113
docs/plugins/deny-pages.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Deny Pages Plugin
|
||||
|
||||
**Type:** Domain Plugin
|
||||
**Runs:** Once for each discovered domain/host
|
||||
|
||||
## Overview
|
||||
|
||||
The Deny Pages plugin blocks access to specific paths for a domain, returning a configurable HTTP status code.
|
||||
|
||||
## Why Use It
|
||||
|
||||
Protect admin panels, internal APIs, or debugging endpoints from public access.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|---------------|----------------------------------------|------------|
|
||||
| `enabled` | Enable/disable plugin | `true` |
|
||||
| `paths` | Comma-separated list of paths to block | (required) |
|
||||
| `status_code` | HTTP status code to return | `403` |
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Docker/Docker Compose (Basic)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
webapp:
|
||||
labels:
|
||||
easyhaproxy.http.host: example.com
|
||||
easyhaproxy.http.plugins: deny_pages
|
||||
easyhaproxy.http.plugin.deny_pages.paths: /admin,/private,/debug
|
||||
easyhaproxy.http.plugin.deny_pages.status_code: 404
|
||||
```
|
||||
|
||||
### WordPress Protection
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.host: wordpress.example.com
|
||||
easyhaproxy.http.plugins: deny_pages
|
||||
easyhaproxy.http.plugin.deny_pages.paths: /wp-admin,/wp-login.php,/.env
|
||||
easyhaproxy.http.plugin.deny_pages.status_code: 404
|
||||
```
|
||||
|
||||
### Kubernetes Annotations
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
easyhaproxy.plugins: "deny_pages"
|
||||
easyhaproxy.plugin.deny_pages.paths: "/admin,/private"
|
||||
easyhaproxy.plugin.deny_pages.status_code: "403"
|
||||
spec:
|
||||
rules:
|
||||
- host: example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: webapp
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
### Static YAML Configuration
|
||||
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
easymapping:
|
||||
- host: example.com
|
||||
port: 80
|
||||
container: webapp:80
|
||||
plugins:
|
||||
- deny_pages
|
||||
plugin_config:
|
||||
deny_pages:
|
||||
paths: /admin,/private,/debug
|
||||
status_code: 403
|
||||
```
|
||||
|
||||
### Multiple Plugins (with Cloudflare)
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.host: secure-app.com
|
||||
easyhaproxy.http.plugins: cloudflare,deny_pages
|
||||
easyhaproxy.http.plugin.deny_pages.paths: /admin,/config
|
||||
easyhaproxy.http.plugin.deny_pages.status_code: 403
|
||||
```
|
||||
|
||||
## Generated HAProxy Configuration
|
||||
|
||||
```haproxy
|
||||
# Deny Pages - Block specific paths
|
||||
acl denied_path path_beg /admin /private /debug
|
||||
http-request deny deny_status 404 if denied_path
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- The plugin runs once per domain during the discovery cycle
|
||||
- Path matching uses `path_beg` (prefix matching), so `/admin` blocks `/admin/*` too
|
||||
- Consider using `404` instead of `403` to hide the existence of blocked paths
|
||||
- Works well in combination with other security plugins
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Plugin System Overview](../plugins.md)
|
||||
- [Container Labels Reference](../container-labels.md)
|
||||
159
docs/plugins/fastcgi.md
Normal file
159
docs/plugins/fastcgi.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# FastCGI Plugin
|
||||
|
||||
**Type:** Domain Plugin
|
||||
**Runs:** Once for each discovered domain/host
|
||||
|
||||
## Overview
|
||||
|
||||
The FastCGI plugin configures HAProxy to communicate with PHP-FPM and other FastCGI applications. It automatically generates the necessary HAProxy `fcgi-app` configuration that defines CGI parameters for proper PHP-FPM communication.
|
||||
|
||||
## Why Use It
|
||||
|
||||
Automatically generates HAProxy `fcgi-app` configuration that defines required CGI parameters for PHP-FPM communication without manual HAProxy configuration.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|-------------------|-----------------------------------------|------------------------------------|
|
||||
| `enabled` | Enable/disable plugin | `true` |
|
||||
| `document_root` | Document root path | `/var/www/html` |
|
||||
| `script_filename` | Custom pattern for SCRIPT_FILENAME | `%[path]` (uses HAProxy's default) |
|
||||
| `index_file` | Default index file | `index.php` |
|
||||
| `path_info` | Enable PATH_INFO support | `true` |
|
||||
| `custom_params` | Dictionary of custom FastCGI parameters | (optional) |
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Docker/Docker Compose (TCP connection)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
php-fpm:
|
||||
image: php:8.2-fpm
|
||||
labels:
|
||||
easyhaproxy.http.host: phpapp.local
|
||||
easyhaproxy.http.port: 80
|
||||
easyhaproxy.http.localport: 9000
|
||||
easyhaproxy.http.proto: fcgi
|
||||
easyhaproxy.http.plugins: fastcgi
|
||||
easyhaproxy.http.plugin.fastcgi.document_root: /var/www/html
|
||||
easyhaproxy.http.plugin.fastcgi.index_file: index.php
|
||||
volumes:
|
||||
- ./app:/var/www/html
|
||||
```
|
||||
|
||||
### Docker/Docker Compose (Unix socket)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
php-fpm:
|
||||
image: php:8.2-fpm
|
||||
labels:
|
||||
easyhaproxy.http.host: phpapp.local
|
||||
easyhaproxy.http.socket: /run/php/php-fpm.sock
|
||||
easyhaproxy.http.proto: fcgi
|
||||
easyhaproxy.http.plugins: fastcgi
|
||||
easyhaproxy.http.plugin.fastcgi.document_root: /var/www/html
|
||||
easyhaproxy.http.plugin.fastcgi.index_file: index.php
|
||||
volumes:
|
||||
- ./app:/var/www/html
|
||||
- /run/php:/run/php
|
||||
```
|
||||
|
||||
### Custom Document Root and Index File
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: fastcgi
|
||||
easyhaproxy.http.plugin.fastcgi.document_root: /var/www/myapp/public
|
||||
easyhaproxy.http.plugin.fastcgi.index_file: app.php
|
||||
easyhaproxy.http.plugin.fastcgi.path_info: true
|
||||
```
|
||||
|
||||
### Kubernetes Annotations
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
easyhaproxy.plugins: "fastcgi"
|
||||
easyhaproxy.plugin.fastcgi.document_root: "/var/www/html"
|
||||
easyhaproxy.plugin.fastcgi.index_file: "index.php"
|
||||
spec:
|
||||
rules:
|
||||
- host: phpapp.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: php-fpm
|
||||
port:
|
||||
number: 9000
|
||||
```
|
||||
|
||||
### Static YAML Configuration
|
||||
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
easymapping:
|
||||
- host: phpapp.local
|
||||
port: 80
|
||||
container: php-fpm:9000
|
||||
proto: fcgi
|
||||
plugins:
|
||||
- fastcgi
|
||||
plugin_config:
|
||||
fastcgi:
|
||||
document_root: /var/www/html
|
||||
index_file: index.php
|
||||
path_info: true
|
||||
```
|
||||
|
||||
## Generated HAProxy Configuration
|
||||
|
||||
The plugin generates a top-level `fcgi-app` section and a `use-fcgi-app` directive in the backend:
|
||||
|
||||
```haproxy
|
||||
# Top-level fcgi-app definition (added after defaults, before frontends/backends)
|
||||
fcgi-app fcgi_phpapp_local
|
||||
docroot /var/www/html
|
||||
index index.php
|
||||
path-info ^(/.+\.php)(/.*)?$
|
||||
|
||||
# Backend configuration (added to the backend section)
|
||||
backend srv_phpapp_local_80
|
||||
use-fcgi-app fcgi_phpapp_local
|
||||
# TCP connection:
|
||||
server srv-0 172.19.0.3:9000 proto fcgi
|
||||
# OR Unix socket:
|
||||
# server srv-0 /run/php/php-fpm.sock proto fcgi
|
||||
```
|
||||
|
||||
## CGI Parameters
|
||||
|
||||
**Note:** HAProxy automatically sets standard CGI parameters based on the `fcgi-app` configuration when communicating with PHP-FPM via the FastCGI protocol.
|
||||
|
||||
The plugin configures:
|
||||
- ✅ **SCRIPT_FILENAME** - Path to PHP script
|
||||
- ✅ **DOCUMENT_ROOT** - Document root directory
|
||||
- ✅ **SCRIPT_NAME** - Script name from URL
|
||||
- ✅ **REQUEST_URI** - Full request URI with query string
|
||||
- ✅ **QUERY_STRING** - URL query parameters
|
||||
- ✅ **REQUEST_METHOD** - HTTP method (GET, POST, etc.)
|
||||
- ✅ **CONTENT_TYPE & CONTENT_LENGTH** - Request body info
|
||||
- ✅ **SERVER_NAME & SERVER_PORT** - Server details
|
||||
- ✅ **HTTPS** - SSL/TLS status
|
||||
- ✅ **PATH_INFO** - Path information (optional)
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Required:** Use this plugin together with `proto: fcgi` parameter for complete PHP-FPM support
|
||||
- The plugin runs once per domain during the discovery cycle
|
||||
- HAProxy handles the actual FastCGI protocol communication and CGI parameter transmission
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Plugin System Overview](../plugins.md)
|
||||
- [Container Labels Reference](../container-labels.md)
|
||||
110
docs/plugins/ip-whitelist.md
Normal file
110
docs/plugins/ip-whitelist.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# IP Whitelist Plugin
|
||||
|
||||
**Type:** Domain Plugin
|
||||
**Runs:** Once for each discovered domain/host
|
||||
|
||||
## Overview
|
||||
|
||||
The IP Whitelist plugin restricts access to a domain to only specific IP addresses or CIDR ranges.
|
||||
|
||||
## Why Use It
|
||||
|
||||
Restrict access to internal tools, admin panels, or staging environments to only trusted IP addresses.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|---------------|--------------------------------------------------|------------|
|
||||
| `enabled` | Enable/disable plugin | `true` |
|
||||
| `allowed_ips` | Comma-separated list of IPs/CIDR ranges to allow | (required) |
|
||||
| `status_code` | HTTP status code to return for blocked IPs | `403` |
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Docker/Docker Compose (Basic)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
admin:
|
||||
labels:
|
||||
easyhaproxy.http.host: admin.example.com
|
||||
easyhaproxy.http.plugins: ip_whitelist
|
||||
easyhaproxy.http.plugin.ip_whitelist.allowed_ips: 192.168.1.0/24,10.0.0.5
|
||||
easyhaproxy.http.plugin.ip_whitelist.status_code: 403
|
||||
```
|
||||
|
||||
### Office Network Access
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.host: admin.example.com
|
||||
easyhaproxy.http.plugins: ip_whitelist
|
||||
easyhaproxy.http.plugin.ip_whitelist.allowed_ips: 203.0.113.0/24,198.51.100.42
|
||||
```
|
||||
|
||||
### Kubernetes Annotations
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
easyhaproxy.plugins: "ip_whitelist"
|
||||
easyhaproxy.plugin.ip_whitelist.allowed_ips: "192.168.1.0/24,10.0.0.5"
|
||||
easyhaproxy.plugin.ip_whitelist.status_code: "403"
|
||||
spec:
|
||||
rules:
|
||||
- host: admin.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: admin-panel
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
### Static YAML Configuration
|
||||
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
easymapping:
|
||||
- host: admin.example.com
|
||||
port: 443
|
||||
container: admin-panel:443
|
||||
plugins:
|
||||
- ip_whitelist
|
||||
plugin_config:
|
||||
ip_whitelist:
|
||||
allowed_ips: 192.168.1.0/24,10.0.0.5
|
||||
status_code: 403
|
||||
```
|
||||
|
||||
## Generated HAProxy Configuration
|
||||
|
||||
```haproxy
|
||||
# IP Whitelist - Only allow specific IPs
|
||||
acl whitelisted_ip src 192.168.1.0/24 10.0.0.5
|
||||
http-request deny deny_status 403 if !whitelisted_ip
|
||||
```
|
||||
|
||||
## IP Address Formats
|
||||
|
||||
The plugin supports:
|
||||
- **Single IPs:** `10.0.0.5`, `203.0.113.42`
|
||||
- **CIDR ranges:** `192.168.1.0/24`, `10.0.0.0/8`
|
||||
- **Multiple entries:** Comma-separated list of IPs and/or CIDR ranges
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Warning:** This blocks ALL IPs except those in the whitelist. Make sure to include your own IP!
|
||||
- The plugin runs once per domain during the discovery cycle
|
||||
- Test thoroughly before deploying to production
|
||||
- Consider using VPN CIDR ranges for remote access
|
||||
- Works well with staging and admin environments
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Plugin System Overview](../plugins.md)
|
||||
- [Container Labels Reference](../container-labels.md)
|
||||
226
docs/plugins/jwt-validator.md
Normal file
226
docs/plugins/jwt-validator.md
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
# JWT Validator Plugin
|
||||
|
||||
**Type:** Domain Plugin
|
||||
**Runs:** Once for each discovered domain/host
|
||||
|
||||
## Overview
|
||||
|
||||
The JWT Validator plugin validates JWT (JSON Web Token) authentication tokens using HAProxy's built-in JWT functionality.
|
||||
|
||||
## Why Use It
|
||||
|
||||
Protect APIs and services with JWT authentication without needing application-level code.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|---------------|----------------------------------------------------------------------------------------------|-------------|
|
||||
| `enabled` | Enable/disable plugin | `true` |
|
||||
| `algorithm` | JWT signing algorithm | `RS256` |
|
||||
| `issuer` | Expected JWT issuer (optional, set to `none`/`null` to skip validation) | (optional) |
|
||||
| `audience` | Expected JWT audience (optional, set to `none`/`null` to skip validation) | (optional) |
|
||||
| `pubkey_path` | Path to public key file (required if `pubkey` not provided) | (required) |
|
||||
| `pubkey` | Public key content as base64-encoded string (required if `pubkey_path` not provided) | (optional) |
|
||||
| `paths` | List of paths that require JWT validation (optional) | (all paths) |
|
||||
| `only_paths` | If `true`, only specified paths are accessible; if `false`, only specified paths require JWT | `false` |
|
||||
|
||||
## Path Validation Logic
|
||||
|
||||
- **No paths configured:** ALL requests to the domain require JWT validation (default behavior)
|
||||
- **Paths configured + `only_paths=false`:** Only specified paths require JWT validation, other paths pass through without validation
|
||||
- **Paths configured + `only_paths=true`:** Only specified paths are accessible (with JWT validation), all other paths are denied
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Docker/Docker Compose (Protect All Paths)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
api:
|
||||
labels:
|
||||
easyhaproxy.http.host: api.example.com
|
||||
easyhaproxy.http.plugins: jwt_validator
|
||||
easyhaproxy.http.plugin.jwt_validator.algorithm: RS256
|
||||
easyhaproxy.http.plugin.jwt_validator.issuer: https://auth.example.com/
|
||||
easyhaproxy.http.plugin.jwt_validator.audience: https://api.example.com
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
volumes:
|
||||
- ./pubkey.pem:/etc/haproxy/jwt_keys/api_pubkey.pem:ro
|
||||
```
|
||||
|
||||
### Protect Specific Paths Only
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: jwt_validator
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
easyhaproxy.http.plugin.jwt_validator.paths: /api/admin,/api/sensitive
|
||||
easyhaproxy.http.plugin.jwt_validator.only_paths: false
|
||||
# /api/health, /api/docs, etc. remain publicly accessible
|
||||
```
|
||||
|
||||
### Only Allow Specific Paths
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugins: jwt_validator
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
easyhaproxy.http.plugin.jwt_validator.paths: /api/public,/api/v1
|
||||
easyhaproxy.http.plugin.jwt_validator.only_paths: true
|
||||
# All paths except /api/public and /api/v1 are denied
|
||||
```
|
||||
|
||||
### Skip Issuer/Audience Validation
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
easyhaproxy.http.plugin.jwt_validator.issuer: none
|
||||
easyhaproxy.http.plugin.jwt_validator.audience: none
|
||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
```
|
||||
|
||||
### Kubernetes Annotations
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
easyhaproxy.plugins: "jwt_validator"
|
||||
easyhaproxy.plugin.jwt_validator.algorithm: "RS256"
|
||||
easyhaproxy.plugin.jwt_validator.issuer: "https://auth.example.com/"
|
||||
easyhaproxy.plugin.jwt_validator.audience: "https://api.example.com"
|
||||
easyhaproxy.plugin.jwt_validator.pubkey_path: "/etc/haproxy/jwt_keys/api_pubkey.pem"
|
||||
easyhaproxy.plugin.jwt_validator.paths: "/api/admin,/api/users"
|
||||
easyhaproxy.plugin.jwt_validator.only_paths: "false"
|
||||
spec:
|
||||
rules:
|
||||
- host: api.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: api-service
|
||||
port:
|
||||
number: 8080
|
||||
```
|
||||
|
||||
### Static YAML Configuration
|
||||
|
||||
```yaml
|
||||
# /etc/haproxy/static/config.yaml
|
||||
easymapping:
|
||||
- host: api.example.com
|
||||
port: 443
|
||||
container: api-service:8080
|
||||
plugins:
|
||||
- jwt_validator
|
||||
plugin_config:
|
||||
jwt_validator:
|
||||
algorithm: RS256
|
||||
issuer: https://auth.example.com/
|
||||
audience: https://api.example.com
|
||||
pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
|
||||
```
|
||||
|
||||
## Generated HAProxy Configuration
|
||||
|
||||
### All Paths Protected
|
||||
|
||||
```haproxy
|
||||
# JWT Validator - Validate JWT tokens
|
||||
http-request deny content-type 'text/html' string 'Missing Authorization HTTP header' unless { req.hdr(authorization) -m found }
|
||||
|
||||
# Extract JWT header and payload
|
||||
http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg')
|
||||
http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss')
|
||||
http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud')
|
||||
http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int')
|
||||
|
||||
# Validate JWT
|
||||
http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm' unless { var(txn.alg) -m str RS256 }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT issuer' unless { var(txn.iss) -m str https://auth.example.com/ }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT audience' unless { var(txn.aud) -m str https://api.example.com }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT signature' unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/haproxy/jwt_keys/api_pubkey.pem") -m int 1 }
|
||||
|
||||
# Validate expiration
|
||||
http-request set-var(txn.now) date()
|
||||
http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int lt 0 }
|
||||
```
|
||||
|
||||
### Specific Paths Only (only_paths=false)
|
||||
|
||||
```haproxy
|
||||
# JWT Validator - Validate JWT tokens
|
||||
|
||||
# Define paths that require JWT validation
|
||||
acl jwt_protected_path path_beg /api/admin
|
||||
acl jwt_protected_path path_beg /api/sensitive
|
||||
|
||||
http-request deny content-type 'text/html' string 'Missing Authorization HTTP header' unless { req.hdr(authorization) -m found } if jwt_protected_path
|
||||
|
||||
# Extract JWT header and payload
|
||||
http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg') if jwt_protected_path
|
||||
http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss') if jwt_protected_path
|
||||
http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud') if jwt_protected_path
|
||||
http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int') if jwt_protected_path
|
||||
|
||||
# Validate JWT (only on protected paths)
|
||||
http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm' unless { var(txn.alg) -m str RS256 } if jwt_protected_path
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT signature' unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/haproxy/jwt_keys/api_pubkey.pem") -m int 1 } if jwt_protected_path
|
||||
|
||||
# Validate expiration
|
||||
http-request set-var(txn.now) date() if jwt_protected_path
|
||||
http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int lt 0 } if jwt_protected_path
|
||||
```
|
||||
|
||||
### Specific Paths Only (only_paths=true)
|
||||
|
||||
```haproxy
|
||||
# JWT Validator - Validate JWT tokens
|
||||
|
||||
# Define paths that require JWT validation
|
||||
acl jwt_protected_path path_beg /api/public
|
||||
acl jwt_protected_path path_beg /api/v1
|
||||
|
||||
# Deny access to paths not in the protected list
|
||||
http-request deny content-type 'text/html' string 'Access denied' unless jwt_protected_path
|
||||
|
||||
http-request deny content-type 'text/html' string 'Missing Authorization HTTP header' unless { req.hdr(authorization) -m found }
|
||||
|
||||
# Extract JWT header and payload
|
||||
http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg')
|
||||
http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss')
|
||||
http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud')
|
||||
http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int')
|
||||
|
||||
# Validate JWT (all requests at this point are on allowed paths)
|
||||
http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm' unless { var(txn.alg) -m str RS256 }
|
||||
http-request deny content-type 'text/html' string 'Invalid JWT signature' unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/haproxy/jwt_keys/api_pubkey.pem") -m int 1 }
|
||||
|
||||
# Validate expiration
|
||||
http-request set-var(txn.now) date()
|
||||
http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int lt 0 }
|
||||
```
|
||||
|
||||
## What It Validates
|
||||
|
||||
- ✅ Authorization header presence
|
||||
- ✅ JWT signing algorithm (RS256, RS512, etc.)
|
||||
- ✅ JWT issuer (if configured)
|
||||
- ✅ JWT audience (if configured)
|
||||
- ✅ JWT signature using public key
|
||||
- ✅ JWT expiration time
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Required:** HAProxy 2.5+ with JWT support
|
||||
- Mount public key file as read-only volume
|
||||
- The plugin runs once per domain during the discovery cycle
|
||||
- Test thoroughly with your JWT provider before deploying to production
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Plugin System Overview](../plugins.md)
|
||||
- [Container Labels Reference](../container-labels.md)
|
||||
Loading…
Add table
Add a link
Reference in a new issue