Documentation Refactor
This commit is contained in:
parent
3b8818e636
commit
a410b34521
47 changed files with 2065 additions and 3911 deletions
158
README.md
158
README.md
|
|
@ -13,133 +13,48 @@
|
||||||
|
|
||||||
## Service discovery for HAProxy
|
## Service discovery for HAProxy
|
||||||
|
|
||||||
EasyHAProxy dynamically creates the `haproxy.cfg` based on metadata collected from your workloads (Docker labels, Swarm service labels, or Kubernetes ingress annotations).
|
EasyHAProxy dynamically creates `haproxy.cfg` based on metadata from your workloads — Docker labels, Swarm service labels, or Kubernetes Ingress annotations. No HAProxy knowledge required.
|
||||||
|
|
||||||
EasyHAProxy can detect and configure HAProxy automatically on the following platforms:
|
## Features
|
||||||
|
|
||||||
- Docker
|
- **Automatic service discovery** — Docker, Docker Swarm, Kubernetes, or static YAML
|
||||||
- Docker Swarm
|
- **Zero-downtime HAProxy reload** — configuration updates happen without dropping connections
|
||||||
- Kubernetes
|
- **Automatic TLS with ACME** — Let's Encrypt, ZeroSSL, BuyPass, and more
|
||||||
- Static YAML definitions (`EASYHAPROXY_DISCOVER=static`)
|
- **Custom SSL certificates** — volume-mount or label-embed your own PEM files
|
||||||
|
- **TCP mode** — proxy any TCP service, not just HTTP
|
||||||
|
- **Plugin system** — JWT validation, IP whitelisting, Cloudflare IP restoration, FastCGI, path blocking, and custom plugins
|
||||||
|
- **HAProxy stats dashboard** — optional, password-protected
|
||||||
|
- **Balance algorithms** — roundrobin, leastconn, source, uri, and more
|
||||||
|
|
||||||
|
## Supported platforms
|
||||||
|
|
||||||
|
[](docs/getting-started/kubernetes.md)
|
||||||
|
[](docs/getting-started/swarm.md)
|
||||||
|
[](docs/getting-started/docker.md)
|
||||||
|
[](docs/getting-started/static.md)
|
||||||
|
|
||||||
|
Install using tools:
|
||||||
|
|
||||||
|
[](docs/guides/helm.md)
|
||||||
|
[](docs/guides/microk8s.md)
|
||||||
|
[](docs/guides/dokku.md)
|
||||||
|
[](docs/guides/digitalocean.md)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
| Section | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| **[Getting Started](docs/getting-started/index.md)** | Choose your runtime and discovery mode, minimal working setup |
|
||||||
|
| **[Guides](docs/guides/ssl.md)** | SSL, ACME, plugins, Helm, MicroK8s, Dokku, DigitalOcean |
|
||||||
|
| **[Concepts](docs/concepts/index.md)** | Service discovery, config pipeline, plugin model, TLS termination |
|
||||||
|
| **[Reference](docs/reference/environment-variables.md)** | Environment variables, container labels, CLI flags, volumes |
|
||||||
|
|
||||||
## Who is using?
|
## Who is using?
|
||||||
|
|
||||||
EasyHAProxy is part of some projects:
|
EasyHAProxy is part of some projects:
|
||||||
- Dokku
|
- [Dokku](docs/guides/dokku.md)
|
||||||
- MicroK8s
|
- [MicroK8s](docs/guides/microk8s.md)
|
||||||
- DigitalOcean Marketplace
|
- [DigitalOcean Marketplace](docs/guides/digitalocean.md)
|
||||||
|
|
||||||
See detailed instructions on how to install below.
|
|
||||||
|
|
||||||
## EasyHAProxy Mission
|
|
||||||
|
|
||||||
Easy to set up and low configuration to numerous features.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
EasyHAProxy will discover services based on Docker (or Swarm) labels and Kubernetes ingress annotations, then dynamically build the `haproxy.cfg`. Below, EasyHAProxy main features:
|
|
||||||
|
|
||||||
- Support Automatic Certificate Management Environment (ACME) protocol compatible with Let's Encrypt and other CAs.
|
|
||||||
- Set your custom SSL certificates
|
|
||||||
- Balance traffic between multiple replicas
|
|
||||||
- Set SSL policies (`strict`, `default`, `loose`) via `EASYHAPROXY_SSL_MODE`.
|
|
||||||
- Set up HAProxy to listen to TCP.
|
|
||||||
- Add redirects.
|
|
||||||
- Enable/disable Stats on port 1936 with a custom password.
|
|
||||||
- Enable/disable custom errors.
|
|
||||||
|
|
||||||
Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file.
|
|
||||||
|
|
||||||
## How Does It Work?
|
|
||||||
|
|
||||||
You don't need to change your current infrastructure and don't need to learn the HAProxy configuration.
|
|
||||||
|
|
||||||
The steps are:
|
|
||||||
|
|
||||||
- Run the EasyHAProxy container;
|
|
||||||
- Add some labels to the containers you want to be parsed by EasyHAProxy (see detailed instructions below);
|
|
||||||
- EasyHAProxy will automatically detect the containers, set up, and reload the HAProxy configurations for you without downtime.
|
|
||||||
|
|
||||||
## Detailed Instructions
|
|
||||||
|
|
||||||
For detailed instructions on how to use EasyHAProxy, follow the instructions for the platform you want to use:
|
|
||||||
|
|
||||||
[](docs/kubernetes.md)
|
|
||||||
[](docs/swarm.md)
|
|
||||||
[](docs/docker.md)
|
|
||||||
[](docs/static.md)
|
|
||||||
|
|
||||||
Or you can install using tools:
|
|
||||||
|
|
||||||
[](docs/helm.md)
|
|
||||||
[](docs/microk8s.md)
|
|
||||||
[](docs/dokku.md)
|
|
||||||
[](docs/digitalocean.md)
|
|
||||||
|
|
||||||
## Special Topics
|
|
||||||
|
|
||||||
If you already set up the EasyHAProxy, is time to go deeper:
|
|
||||||
|
|
||||||
- [Custom SSL](docs/ssl.md)
|
|
||||||
- [Automatic Certificate Issuing](docs/acme.md) (e.g. Letsencrypt)
|
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
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 plugins ([Development Guide](docs/plugin-development.md))
|
|
||||||
- [JWT Validator](docs/Plugins/jwt-validator.md) - JWT authentication validation
|
|
||||||
- [FastCGI](docs/Plugins/fastcgi.md) - PHP-FPM and FastCGI application support
|
|
||||||
- [Cloudflare](docs/Plugins/cloudflare.md) - Restore visitor IP from Cloudflare CDN
|
|
||||||
- [IP Whitelist](docs/Plugins/ip-whitelist.md) - Restrict access to IPs/CIDR ranges
|
|
||||||
- [Deny Pages](docs/Plugins/deny-pages.md) - Block access to specific paths
|
|
||||||
- [Cleanup](docs/Plugins/cleanup.md) - Automatic cleanup of temporary files
|
|
||||||
- [Other Configurations](docs/other.md) - Additional configurations (ports, custom errors, etc.)
|
|
||||||
- [Limitations](docs/limitations.md) - Important limitations and considerations
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
- Python 3.11 or higher
|
|
||||||
- [uv](https://github.com/astral-sh/uv) package manager
|
|
||||||
|
|
||||||
### Installation for Development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install uv (if not already installed)
|
|
||||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
||||||
|
|
||||||
# Clone the repository
|
|
||||||
git clone https://github.com/byjg/docker-easy-haproxy.git
|
|
||||||
cd docker-easy-haproxy
|
|
||||||
|
|
||||||
# Install dependencies (creates virtual environment automatically)
|
|
||||||
uv sync --dev
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
make test
|
|
||||||
# or directly: uv run pytest tests/ -vv
|
|
||||||
|
|
||||||
# Run linting
|
|
||||||
make lint
|
|
||||||
|
|
||||||
# Format code
|
|
||||||
make format
|
|
||||||
```
|
|
||||||
|
|
||||||
### Installing the Package
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install with uv
|
|
||||||
uv pip install easymapping
|
|
||||||
|
|
||||||
# Or install from source
|
|
||||||
uv pip install -e ".[dev]"
|
|
||||||
```
|
|
||||||
|
|
||||||
## See EasyHAProxy in action
|
## See EasyHAProxy in action
|
||||||
|
|
||||||
|
|
@ -154,6 +69,5 @@ Click on the image to see the videos (use HD for better visualization)
|
||||||
|
|
||||||
[Here is the code](https://gist.github.com/byjg/e125e478a0562190176d69ea795fd3d4) applied in the test examples above.
|
[Here is the code](https://gist.github.com/byjg/e125e478a0562190176d69ea795fd3d4) applied in the test examples above.
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ kubectl apply -f easyhaproxy-nodeport.yml
|
||||||
kubectl apply -f easyhaproxy-clusterip.yml
|
kubectl apply -f easyhaproxy-clusterip.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
For more details, see the [Kubernetes documentation](../../docs/kubernetes.md).
|
For more details, see the [Kubernetes documentation](../../docs/getting-started/kubernetes.md).
|
||||||
|
|
||||||
## Regenerating These Files
|
## Regenerating These Files
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,385 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 16
|
|
||||||
---
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
## Generating JWT Keys
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Generate RSA key pair (idempotent - skips if exists)
|
|
||||||
[ -f jwt_private.pem ] || openssl genrsa -out jwt_private.pem 2048
|
|
||||||
[ -f jwt_pubkey.pem ] || openssl rsa -in jwt_private.pem -pubout -out jwt_pubkey.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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 (priority 1: explicit file path) | (optional) |
|
|
||||||
| `pubkey` | Public key content as base64-encoded string (priority 2: inline content) | (optional) |
|
|
||||||
| `k8s_secret.pubkey` | Kubernetes secret containing public key (priority 3: Kubernetes only - see below) | (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` |
|
|
||||||
| `allow_anonymous` | If `true`, allows requests without Authorization header (validates JWT if present) | `false` |
|
|
||||||
|
|
||||||
### Public Key Configuration Priority
|
|
||||||
|
|
||||||
When multiple public key options are configured, they are evaluated in this order:
|
|
||||||
1. **`pubkey_path`** - Direct file path (explicit configuration)
|
|
||||||
2. **`pubkey`** - Base64-encoded key content (inline configuration)
|
|
||||||
3. **`k8s_secret.pubkey`** - Kubernetes secret (recommended for Kubernetes deployments)
|
|
||||||
|
|
||||||
The first configured option is used; others are ignored.
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
## Anonymous Access Logic
|
|
||||||
|
|
||||||
- **`allow_anonymous=false` (default):** Requests without `Authorization` header are denied with "Missing Authorization HTTP header"
|
|
||||||
- **`allow_anonymous=true`:** Requests without `Authorization` header are allowed to pass through, but JWTs are validated if the header is present
|
|
||||||
|
|
||||||
**Use Cases for `allow_anonymous=true`:**
|
|
||||||
- Optional authentication (show different content for authenticated vs anonymous users)
|
|
||||||
- Mixed public/private content where some users have enhanced access with JWT
|
|
||||||
- Gradual JWT authentication rollout
|
|
||||||
- Public APIs that provide additional features to authenticated users
|
|
||||||
|
|
||||||
## 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/easyhaproxy/jwt_keys/api_pubkey.pem
|
|
||||||
volumes:
|
|
||||||
- ./pubkey.pem:/etc/easyhaproxy/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/easyhaproxy/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/easyhaproxy/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/easyhaproxy/jwt_keys/api_pubkey.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
### Allow Anonymous Access (Optional JWT)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
api:
|
|
||||||
labels:
|
|
||||||
easyhaproxy.http.host: api.example.com
|
|
||||||
easyhaproxy.http.plugins: jwt_validator
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/easyhaproxy/jwt_keys/api_pubkey.pem
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.allow_anonymous: true
|
|
||||||
volumes:
|
|
||||||
- ./pubkey.pem:/etc/easyhaproxy/jwt_keys/api_pubkey.pem:ro
|
|
||||||
# Requests without Authorization header are allowed
|
|
||||||
# Requests with Authorization header are validated
|
|
||||||
# Invalid JWTs are rejected
|
|
||||||
```
|
|
||||||
|
|
||||||
### Kubernetes with Secrets (Recommended)
|
|
||||||
|
|
||||||
The recommended way to configure JWT public keys in Kubernetes is using Kubernetes Secrets with the `k8s_secret` pattern:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
# Create a secret with your JWT public key
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: jwt-pubkey-secret
|
|
||||||
namespace: production
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
pubkey: |
|
|
||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
|
|
||||||
---
|
|
||||||
# Reference it in your ingress
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: api-ingress
|
|
||||||
namespace: production
|
|
||||||
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"
|
|
||||||
# Load public key from Kubernetes secret
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "jwt-pubkey-secret"
|
|
||||||
easyhaproxy.plugin.jwt_validator.paths: "/api/admin,/api/users"
|
|
||||||
easyhaproxy.plugin.jwt_validator.only_paths: "false"
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
rules:
|
|
||||||
- host: api.example.com
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: api-service
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
**With explicit secret key name:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
# Use custom key name from the secret
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "jwt-pubkey-secret/rsa-public-key"
|
|
||||||
```
|
|
||||||
|
|
||||||
For complete details about the `k8s_secret` pattern, including auto-detect vs explicit key names, troubleshooting, and security considerations, see: [Loading Plugin Configuration from Kubernetes Secrets](../kubernetes.md#loading-plugin-configuration-from-kubernetes-secrets)
|
|
||||||
|
|
||||||
### Kubernetes with pubkey_path (Legacy)
|
|
||||||
|
|
||||||
You can also mount the public key file using ConfigMaps or volumes:
|
|
||||||
|
|
||||||
```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/easyhaproxy/jwt_keys/api_pubkey.pem"
|
|
||||||
easyhaproxy.plugin.jwt_validator.paths: "/api/admin,/api/users"
|
|
||||||
easyhaproxy.plugin.jwt_validator.only_paths: "false"
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
rules:
|
|
||||||
- host: api.example.com
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: api-service
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** This requires mounting the public key file into the EasyHAProxy pod using ConfigMaps or volumes. Using `k8s_secret.pubkey` is recommended as it's simpler and more secure.
|
|
||||||
|
|
||||||
### Static YAML Configuration
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# /etc/easyhaproxy/static/config.yaml
|
|
||||||
containers:
|
|
||||||
"api.example.com:443":
|
|
||||||
ip: ["api-service:8080"]
|
|
||||||
ssl: true
|
|
||||||
plugins: [jwt_validator]
|
|
||||||
plugin:
|
|
||||||
jwt_validator:
|
|
||||||
algorithm: RS256
|
|
||||||
issuer: https://auth.example.com/
|
|
||||||
audience: https://api.example.com
|
|
||||||
pubkey_path: /etc/easyhaproxy/jwt_keys/api_pubkey.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
Configure JWT Validator plugin defaults for all domains:
|
|
||||||
|
|
||||||
| Environment Variable | Config Key | Type | Default | Description |
|
|
||||||
|----------------------------------------------------|-------------------|---------|---------|---------------------------------------------|
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ALGORITHM` | `algorithm` | string | `RS256` | JWT signing algorithm |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ISSUER` | `issuer` | string | - | Expected JWT issuer (optional) |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_AUDIENCE` | `audience` | string | - | Expected JWT audience (optional) |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_PUBKEY_PATH` | `pubkey_path` | string | - | Path to public key file |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_PUBKEY` | `pubkey` | string | - | Public key as base64-encoded string |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_PATHS` | `paths` | string | - | Comma-separated paths requiring JWT |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ONLY_PATHS` | `only_paths` | boolean | `false` | If true, only specified paths accessible |
|
|
||||||
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ALLOW_ANONYMOUS` | `allow_anonymous` | boolean | `false` | Allow requests without Authorization header |
|
|
||||||
|
|
||||||
**Note:** Environment variables set defaults for ALL domains. To configure per-domain, use container labels or Kubernetes annotations.
|
|
||||||
|
|
||||||
## 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/easyhaproxy/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/easyhaproxy/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/easyhaproxy/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 }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Allow Anonymous Access (allow_anonymous=true)
|
|
||||||
|
|
||||||
```haproxy
|
|
||||||
# JWT Validator - Validate JWT tokens
|
|
||||||
|
|
||||||
# Allow anonymous access - validate JWT only if Authorization header is present
|
|
||||||
|
|
||||||
# Extract JWT header and payload
|
|
||||||
http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg') if { req.hdr(authorization) -m found }
|
|
||||||
http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss') if { req.hdr(authorization) -m found }
|
|
||||||
http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud') if { req.hdr(authorization) -m found }
|
|
||||||
http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int') if { req.hdr(authorization) -m found }
|
|
||||||
|
|
||||||
# Validate JWT (only if Authorization header is present)
|
|
||||||
http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm' unless { var(txn.alg) -m str RS256 } if { req.hdr(authorization) -m found }
|
|
||||||
http-request deny content-type 'text/html' string 'Invalid JWT issuer' unless { var(txn.iss) -m str https://auth.example.com/ } if { req.hdr(authorization) -m found }
|
|
||||||
http-request deny content-type 'text/html' string 'Invalid JWT audience' unless { var(txn.aud) -m str https://api.example.com } if { req.hdr(authorization) -m found }
|
|
||||||
http-request deny content-type 'text/html' string 'Invalid JWT signature' unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/easyhaproxy/jwt_keys/api_pubkey.pem") -m int 1 } if { req.hdr(authorization) -m found }
|
|
||||||
|
|
||||||
# Validate expiration (only if Authorization header is present)
|
|
||||||
http-request set-var(txn.now) date() if { req.hdr(authorization) -m found }
|
|
||||||
http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int lt 0 } if { req.hdr(authorization) -m found }
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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)
|
|
||||||
4
docs/concepts/_category_.json
Normal file
4
docs/concepts/_category_.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"label": "Concepts",
|
||||||
|
"position": 3
|
||||||
|
}
|
||||||
103
docs/concepts/index.md
Normal file
103
docs/concepts/index.md
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
sidebar_label: "Concepts"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Concepts
|
||||||
|
|
||||||
|
This section explains how EasyHAProxy works under the hood — the mental models that help you understand why things are configured the way they are.
|
||||||
|
|
||||||
|
## Service Discovery
|
||||||
|
|
||||||
|
EasyHAProxy runs a polling loop every N seconds (default 10, configurable via `EASYHAPROXY_REFRESH_CONF`). On each tick it:
|
||||||
|
|
||||||
|
1. **Queries your runtime** — Docker API for containers/services, Kubernetes API for Ingress objects, or reads the static YAML file.
|
||||||
|
2. **Filters by label/annotation prefix** — only resources that carry the `easyhaproxy` prefix (or your custom `EASYHAPROXY_LABEL_PREFIX`) are considered.
|
||||||
|
3. **Builds an intermediate structure** — an in-memory list of (host, port, backend) tuples called `easymapping`.
|
||||||
|
4. **Runs plugins** — global plugins run once; domain plugins run once per host entry.
|
||||||
|
5. **Renders `haproxy.cfg`** from a Jinja2 template using the `easymapping` data.
|
||||||
|
6. **Reloads HAProxy** if the rendered config differs from the previous one (zero-downtime reload).
|
||||||
|
|
||||||
|
### Discovery mode comparison
|
||||||
|
|
||||||
|
| Mode | Source polled | Auth required |
|
||||||
|
|------|--------------|---------------|
|
||||||
|
| `docker` | Docker socket `/var/run/docker.sock` | Socket access |
|
||||||
|
| `swarm` | Docker socket (Swarm API) | Socket access |
|
||||||
|
| `kubernetes` | Kubernetes API server | RBAC (`get`/`list` on Ingress, Secret) |
|
||||||
|
| `static` | File on disk | None |
|
||||||
|
|
||||||
|
## Configuration Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
Labels / Annotations / YAML file
|
||||||
|
↓
|
||||||
|
Discovery (Docker / Swarm / K8s / Static)
|
||||||
|
↓
|
||||||
|
parsed_object { IP → label map }
|
||||||
|
↓
|
||||||
|
[GLOBAL PLUGINS] ← executed once
|
||||||
|
↓
|
||||||
|
easymapping [ list of domain configs ]
|
||||||
|
↓
|
||||||
|
For each domain:
|
||||||
|
[DOMAIN PLUGINS] ← executed per domain
|
||||||
|
↓
|
||||||
|
PluginResult snippets collected
|
||||||
|
↓
|
||||||
|
Jinja2 template render → haproxy.cfg
|
||||||
|
↓
|
||||||
|
HAProxy reload (if config changed)
|
||||||
|
```
|
||||||
|
|
||||||
|
The key insight: **you never write `haproxy.cfg` by hand**. You express intent through labels/annotations/YAML, and EasyHAProxy translates that into valid HAProxy configuration on every discovery cycle.
|
||||||
|
|
||||||
|
## Plugin Execution Model
|
||||||
|
|
||||||
|
Plugins are Python classes that implement `PluginInterface`. They are discovered automatically from:
|
||||||
|
|
||||||
|
1. `/src/plugins/builtin/` — bundled plugins
|
||||||
|
2. `/etc/easyhaproxy/plugins/` — your custom plugins
|
||||||
|
|
||||||
|
**Two execution types:**
|
||||||
|
|
||||||
|
### GLOBAL plugins
|
||||||
|
|
||||||
|
- Run **once per discovery cycle**, before any domain processing.
|
||||||
|
- Receive the full `parsed_object` (all discovered services).
|
||||||
|
- Use cases: cleanup tasks, global monitoring, DNS updates.
|
||||||
|
- Example: `cleanup` plugin.
|
||||||
|
|
||||||
|
### DOMAIN plugins
|
||||||
|
|
||||||
|
- Run **once per discovered domain/host**.
|
||||||
|
- Receive domain-specific context: domain name, port, label/annotation map.
|
||||||
|
- Return `PluginResult` containing HAProxy config snippets to inject into the backend section.
|
||||||
|
- Use cases: IP whitelisting, JWT validation, Cloudflare IP restoration, path blocking.
|
||||||
|
- Examples: `cloudflare`, `deny_pages`, `jwt_validator`, `ip_whitelist`, `fastcgi`.
|
||||||
|
|
||||||
|
**Configuration precedence** (highest to lowest):
|
||||||
|
1. Container labels / Ingress annotations (per-domain)
|
||||||
|
2. Static YAML `plugins.config` block
|
||||||
|
3. Environment variables `EASYHAPROXY_PLUGIN_<NAME>_<KEY>`
|
||||||
|
|
||||||
|
See the [Plugin Developer Guide](../guides/plugin-development.md) for how to build your own plugin.
|
||||||
|
|
||||||
|
## SSL/TLS Termination Model
|
||||||
|
|
||||||
|
SSL termination happens **at HAProxy**, not in your backend containers.
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet → HAProxy (TLS decryption) → Backend container (plain HTTP)
|
||||||
|
```
|
||||||
|
|
||||||
|
Your containers should only serve HTTP on their internal port. HAProxy handles all TLS on ports 80/443.
|
||||||
|
|
||||||
|
**Certificate sources** (evaluated in this order for each domain):
|
||||||
|
1. **ACME/Certbot** — if `certbot=true` label is set, EasyHAProxy runs Certbot HTTP-01 challenge automatically.
|
||||||
|
2. **Volume-mounted PEM** — files in `/etc/easyhaproxy/certs/haproxy/{domain}.pem`.
|
||||||
|
3. **Label-embedded PEM** — base64 certificate in the `sslcert` label.
|
||||||
|
|
||||||
|
Both ACME and manual certificates can coexist. Per domain, whichever source is configured takes precedence as shown above.
|
||||||
|
|
||||||
|
See [SSL setup](../guides/ssl.md) and [ACME/Let's Encrypt](../guides/acme.md) for step-by-step configuration.
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 23
|
sidebar_position: 2
|
||||||
|
sidebar_label: "Limitations"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Limitations and Considerations
|
# Limitations and Considerations
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 3
|
|
||||||
---
|
|
||||||
|
|
||||||
# Docker
|
|
||||||
|
|
||||||
## Setup Docker EasyHAProxy
|
|
||||||
|
|
||||||
This method involves using a standalone Docker installation to discover containers
|
|
||||||
and configure HAProxy.
|
|
||||||
|
|
||||||
EasyHAProxy inspects Docker containers and retrieves labels to configure HAProxy.
|
|
||||||
Once it identifies a container with at least the label 'easyhaproxy.http.host,'
|
|
||||||
it configures HAProxy to redirect traffic to that container.
|
|
||||||
To accomplish this, EasyHAProxy may need to attach the same network to its container.
|
|
||||||
|
|
||||||
It's recommended to create a network external to EasyHAProxy, although it's not mandatory.
|
|
||||||
|
|
||||||
:::warning Limitations
|
|
||||||
- You cannot mix Docker containers with Swarm containers.
|
|
||||||
- This method does not work with containers that use the `--network=host` option. See [limitations](limitations.md) for details.
|
|
||||||
:::
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```bash title="Create EasyHAProxy network"
|
|
||||||
docker network create easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
And then run the EasyHAProxy:
|
|
||||||
|
|
||||||
```bash title="Run EasyHAProxy container"
|
|
||||||
docker run -d \
|
|
||||||
--name easy-haproxy-container \
|
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
||||||
-e EASYHAPROXY_DISCOVER="docker" \
|
|
||||||
# + Environment Variables \
|
|
||||||
-p 80:80 \
|
|
||||||
-p 443:443 \
|
|
||||||
-p 1936:1936 \
|
|
||||||
--network easyhaproxy
|
|
||||||
byjg/easy-haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
Mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels;
|
|
||||||
|
|
||||||
## Running containers
|
|
||||||
|
|
||||||
To make your containers "discoverable" by EasyHAProxy, this is the minimum configuration you need:
|
|
||||||
|
|
||||||
```bash title="Run container with EasyHAProxy labels"
|
|
||||||
docker run -d \
|
|
||||||
--label easyhaproxy.http.host=example.org \
|
|
||||||
--label easyhaproxy.http.port=80 \
|
|
||||||
--label easyhaproxy.http.localport=8080 \
|
|
||||||
--network easyhaproxy
|
|
||||||
my/image:tag
|
|
||||||
```
|
|
||||||
|
|
||||||
Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container.
|
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
|
||||||
|
|
||||||
Please follow the [docker label configuration](container-labels.md) to see other configurations available.
|
|
||||||
|
|
||||||
## Setup the EasyHAProxy container
|
|
||||||
|
|
||||||
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [environment variable guide](environment-variable.md)
|
|
||||||
|
|
||||||
## Setup certificates with ACME (e.g. Letsencrypt)
|
|
||||||
|
|
||||||
Follow [this link](acme.md)
|
|
||||||
|
|
||||||
## Setup your own certificates
|
|
||||||
|
|
||||||
Follow [this link](ssl.md)
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
4
docs/getting-started/_category_.json
Normal file
4
docs/getting-started/_category_.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"label": "Getting Started",
|
||||||
|
"position": 1
|
||||||
|
}
|
||||||
66
docs/getting-started/docker.md
Normal file
66
docs/getting-started/docker.md
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
sidebar_label: "Docker"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
|
||||||
|
EasyHAProxy inspects running Docker containers, reads their labels, and configures HAProxy automatically.
|
||||||
|
|
||||||
|
:::warning Limitations
|
||||||
|
- You cannot mix Docker containers with Swarm containers.
|
||||||
|
- This method does not work with containers that use the `--network=host` option. See [limitations](../concepts/limitations.md) for details.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Step 1 — Create a shared network
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network create easyhaproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
It's recommended to use an external network so EasyHAProxy and your app containers can communicate.
|
||||||
|
|
||||||
|
## Step 2 — Run EasyHAProxy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name easy-haproxy-container \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-e EASYHAPROXY_DISCOVER="docker" \
|
||||||
|
-p 80:80 \
|
||||||
|
-p 443:443 \
|
||||||
|
-p 1936:1936 \
|
||||||
|
--network easyhaproxy \
|
||||||
|
byjg/easy-haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
Mounting `/var/run/docker.sock` is required so EasyHAProxy can query the Docker API.
|
||||||
|
|
||||||
|
## Step 3 — Label your container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--label easyhaproxy.http.host=example.org \
|
||||||
|
--label easyhaproxy.http.port=80 \
|
||||||
|
--label easyhaproxy.http.localport=8080 \
|
||||||
|
--network easyhaproxy \
|
||||||
|
my/image:tag
|
||||||
|
```
|
||||||
|
|
||||||
|
EasyHAProxy detects this container automatically and routes traffic from `example.org:80` to port 8080 in your container. You do not need to expose any container ports.
|
||||||
|
|
||||||
|
## Step 4 — Verify
|
||||||
|
|
||||||
|
Open `http://example.org` in your browser (or `curl http://example.org`). Traffic should reach your container.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full options
|
||||||
|
|
||||||
|
- [Container label reference](../reference/container-labels.md) — all available labels
|
||||||
|
- [Environment variable reference](../reference/environment-variables.md) — configure EasyHAProxy behavior
|
||||||
|
- [SSL certificates](../guides/ssl.md) — add custom TLS
|
||||||
|
- [ACME / Let's Encrypt](../guides/acme.md) — automatic certificate issuing
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
51
docs/getting-started/index.md
Normal file
51
docs/getting-started/index.md
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
sidebar_label: "Getting Started"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Getting Started with EasyHAProxy
|
||||||
|
|
||||||
|
EasyHAProxy dynamically builds `haproxy.cfg` from metadata on your running workloads — no HAProxy knowledge required.
|
||||||
|
|
||||||
|
## Choose your runtime
|
||||||
|
|
||||||
|
EasyHAProxy runs in two ways:
|
||||||
|
|
||||||
|
| Runtime | When to choose |
|
||||||
|
|---------|---------------|
|
||||||
|
| **Docker container** (`byjg/easy-haproxy`) | You already run Docker, Swarm, or Kubernetes |
|
||||||
|
| **Native host** (`easy-haproxy` CLI) | You want HAProxy on the host with full OS control — no Docker needed |
|
||||||
|
|
||||||
|
## Choose your discovery mode
|
||||||
|
|
||||||
|
Once running, EasyHAProxy discovers your services in one of four ways:
|
||||||
|
|
||||||
|
| Mode | How it works | Label/annotation format |
|
||||||
|
|------|-------------|------------------------|
|
||||||
|
| **Docker** | Reads labels from running containers on a Docker host | Container labels |
|
||||||
|
| **Swarm** | Reads labels from services in a Docker Swarm cluster | Service labels |
|
||||||
|
| **Kubernetes** | Reads `ingressClassName: easyhaproxy` Ingress resources | Ingress annotations |
|
||||||
|
| **Static** | Reads a hand-written YAML file you provide | YAML file |
|
||||||
|
|
||||||
|
## Quick-start guides
|
||||||
|
|
||||||
|
Pick the guide that matches your environment:
|
||||||
|
|
||||||
|
### Container runtimes (Docker image)
|
||||||
|
|
||||||
|
- **[Docker](docker.md)** — standalone Docker host, container labels
|
||||||
|
- **[Docker Swarm](swarm.md)** — overlay network, service labels
|
||||||
|
- **[Kubernetes](kubernetes.md)** — Ingress controller, DaemonSet/NodePort
|
||||||
|
- **[Static YAML](static.md)** — any environment, config file
|
||||||
|
|
||||||
|
### Native host (pip/uv package)
|
||||||
|
|
||||||
|
- **[Native install](native.md)** — HAProxy on the host, `easy-haproxy` CLI
|
||||||
|
|
||||||
|
## What's next?
|
||||||
|
|
||||||
|
After you have traffic flowing:
|
||||||
|
|
||||||
|
- **[Guides](../guides/ssl.md)** — SSL certificates, ACME/Let's Encrypt, plugins
|
||||||
|
- **[Concepts](../concepts/index.md)** — how service discovery and the config pipeline work
|
||||||
|
- **[Reference](../reference/environment-variables.md)** — full environment variable and label tables
|
||||||
256
docs/getting-started/kubernetes.md
Normal file
256
docs/getting-started/kubernetes.md
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
sidebar_label: "Kubernetes"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Kubernetes
|
||||||
|
|
||||||
|
EasyHAProxy acts as an Ingress controller for Kubernetes — it watches Ingress resources with `ingressClassName: easyhaproxy` and configures HAProxy automatically.
|
||||||
|
|
||||||
|
:::info How it works
|
||||||
|
EasyHAProxy queries all ingress definitions with either the
|
||||||
|
`spec.ingressClassName: easyhaproxy` field (recommended) or the deprecated annotation
|
||||||
|
`kubernetes.io/ingress.class: easyhaproxy-ingress` (for backward compatibility).
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Deployment Modes
|
||||||
|
|
||||||
|
Choose the deployment mode that fits your infrastructure:
|
||||||
|
|
||||||
|
| Mode | Workload | Exposed Ports | Node Label | Recommended When |
|
||||||
|
|-----------|------------|------------------------|------------|-------------------------------|
|
||||||
|
| NodePort | Deployment | 31080 / 31443 / 31936 | No | Most setups **(recommended)** |
|
||||||
|
| ClusterIP | Deployment | cluster-internal only | No | Behind a LoadBalancer **(recommended)** |
|
||||||
|
| DaemonSet | DaemonSet | 80 / 443 / 1936 (host) | Yes | Bare-metal, special cases |
|
||||||
|
|
||||||
|
**NodePort** and **ClusterIP** run as a Deployment — no node label needed, and they survive node replacements safely.
|
||||||
|
|
||||||
|
**DaemonSet** binds to host ports and requires a node label (`easyhaproxy/node=master`) via `nodeAffinity`. The label must be manually reapplied after any node replacement, which can cause outages. Use only for bare-metal or special-case setups.
|
||||||
|
|
||||||
|
EasyHAProxy detects its deployment mode automatically (`EASYHAPROXY_DEPLOYMENT_MODE=auto`). See the [environment variable reference](../reference/environment-variables.md#kubernetes) for manual override options.
|
||||||
|
|
||||||
|
## Step 1 — Install EasyHAProxy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create namespace easyhaproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
### NodePort (recommended)
|
||||||
|
|
||||||
|
Exposes HAProxy on NodePort `31080` (HTTP), `31443` (HTTPS), and `31936` (stats). Point your DNS or external load balancer to any node IP on these ports.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f \
|
||||||
|
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/6.0.0/deploy/kubernetes/easyhaproxy-nodeport.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### ClusterIP (behind a LoadBalancer)
|
||||||
|
|
||||||
|
Cluster-internal only. Pair with an external cloud LoadBalancer or `kubectl port-forward` for local testing.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f \
|
||||||
|
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/6.0.0/deploy/kubernetes/easyhaproxy-clusterip.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### DaemonSet (special cases — requires node label)
|
||||||
|
|
||||||
|
:::warning Requires node label maintenance
|
||||||
|
The node label must be reapplied after any node replacement. Failing to do so will cause an outage.
|
||||||
|
:::
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Label the target node first
|
||||||
|
kubectl label nodes node-01 "easyhaproxy/node=master"
|
||||||
|
|
||||||
|
kubectl apply -f \
|
||||||
|
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/6.0.0/deploy/kubernetes/easyhaproxy-daemonset.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to configure environment variables (log levels, stats password, etc.), see the [environment variable reference](../reference/environment-variables.md).
|
||||||
|
|
||||||
|
## Step 2 — Create an Ingress
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: example-ingress
|
||||||
|
namespace: example
|
||||||
|
spec:
|
||||||
|
ingressClassName: easyhaproxy
|
||||||
|
rules:
|
||||||
|
- host: example.org
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: example-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
```
|
||||||
|
|
||||||
|
EasyHAProxy routes traffic from `example.org:80` to your service at port 8080. No container port exposure needed.
|
||||||
|
|
||||||
|
:::note Backward Compatibility
|
||||||
|
The deprecated annotation `kubernetes.io/ingress.class: easyhaproxy-ingress` is still supported but `spec.ingressClassName` is recommended for new deployments.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Step 3 — Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://example.org
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kubernetes Annotations
|
||||||
|
|
||||||
|
Customize EasyHAProxy behavior per-ingress using annotations. For the full annotations reference, see [Container Labels — Kubernetes Ingress Annotations](../reference/container-labels.md#kubernetes-ingress-annotations).
|
||||||
|
|
||||||
|
**Important**: Annotations apply to all hosts in the ingress configuration.
|
||||||
|
|
||||||
|
## Using Plugins
|
||||||
|
|
||||||
|
Add the `easyhaproxy.plugins` annotation with a comma-separated list of plugin names:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
easyhaproxy.plugins: "cloudflare,deny_pages"
|
||||||
|
easyhaproxy.plugin.deny_pages.paths: "/wp-admin,/wp-login.php"
|
||||||
|
easyhaproxy.plugin.deny_pages.status_code: "404"
|
||||||
|
```
|
||||||
|
|
||||||
|
For full plugin documentation, see the [Using Plugins](../guides/plugins.md) guide.
|
||||||
|
|
||||||
|
## Loading Plugin Configuration from Kubernetes Secrets
|
||||||
|
|
||||||
|
EasyHAProxy supports loading sensitive plugin configuration values from Kubernetes Secrets using the `k8s_secret` pattern:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Auto-detect key (tries common variations):
|
||||||
|
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name"
|
||||||
|
|
||||||
|
# Explicit key (no variations):
|
||||||
|
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name/key_name"
|
||||||
|
```
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
1. You create a Kubernetes Secret with your sensitive data
|
||||||
|
2. You reference the secret in your ingress annotation using the `k8s_secret` pattern
|
||||||
|
3. EasyHAProxy reads the secret from the same namespace as the ingress
|
||||||
|
4. EasyHAProxy transforms the annotation to inject the secret value
|
||||||
|
5. The plugin receives the value as if it was provided directly in the annotation
|
||||||
|
|
||||||
|
### Complete Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: jwt-pubkey-secret
|
||||||
|
namespace: production
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
pubkey: |
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: api-ingress
|
||||||
|
namespace: production
|
||||||
|
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.k8s_secret.pubkey: "jwt-pubkey-secret"
|
||||||
|
spec:
|
||||||
|
ingressClassName: easyhaproxy
|
||||||
|
rules:
|
||||||
|
- host: api.example.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: api-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### Priority Order
|
||||||
|
|
||||||
|
1. **Explicit annotation** (e.g., `easyhaproxy.plugin.jwt_validator.pubkey: "value"`)
|
||||||
|
2. **k8s_secret annotation** (e.g., `easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "secret"`)
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
|
||||||
|
- Secrets are read from the **same namespace** as the ingress (no cross-namespace access)
|
||||||
|
- EasyHAProxy needs RBAC permissions to read secrets (included in default deployment)
|
||||||
|
- Use Kubernetes RBAC to control which service accounts can read which secrets
|
||||||
|
|
||||||
|
## ACME / Let's Encrypt
|
||||||
|
|
||||||
|
Add the `easyhaproxy.certbot` annotation to enable automatic certificate issuing:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
easyhaproxy.certbot: 'true'
|
||||||
|
name: example-ingress
|
||||||
|
namespace: example
|
||||||
|
spec:
|
||||||
|
ingressClassName: easyhaproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
More info in the [ACME guide](../guides/acme.md). Make sure ports 80 and 443 are publicly reachable.
|
||||||
|
|
||||||
|
## Custom SSL Certificates
|
||||||
|
|
||||||
|
Create a secret with your certificate and key and associate it with your ingress:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: host2-tls
|
||||||
|
namespace: default
|
||||||
|
data:
|
||||||
|
tls.crt: base64 of your certificate
|
||||||
|
tls.key: base64 of your certificate private key
|
||||||
|
type: kubernetes.io/tls
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: tls-example
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
ingressClassName: easyhaproxy
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- host2.local
|
||||||
|
secretName: host2-tls
|
||||||
|
rules:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Limitations
|
||||||
|
|
||||||
|
- The implementation doesn't support all ingress properties or wildcard domains.
|
||||||
|
- EasyHAProxy reads all `spec.rules[].host` values but parses only the **first path** per rule.
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
130
docs/getting-started/native.md
Normal file
130
docs/getting-started/native.md
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 5
|
||||||
|
sidebar_label: "Native (pip/uv)"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Native install (pip / uv)
|
||||||
|
|
||||||
|
EasyHAProxy can run directly on any Linux or macOS host without Docker, using the `easyhaproxy` Python package. HAProxy is installed on the host; EasyHAProxy manages it.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
HAProxy must be installed and available in your system `PATH` before running `easy-haproxy`.
|
||||||
|
|
||||||
|
import Tabs from '@theme/Tabs';
|
||||||
|
import TabItem from '@theme/TabItem';
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<TabItem value="debian" label="Debian / Ubuntu" default>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="rhel" label="RHEL / Fedora">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="macos" label="macOS">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Recommended: `uv tool` (system-wide, isolated)
|
||||||
|
|
||||||
|
[`uv`](https://docs.astral.sh/uv/) installs `easyhaproxy` into its own isolated environment and exposes the `easy-haproxy` binary in `~/.local/bin/`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install uv (if not already installed)
|
||||||
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
|
|
||||||
|
# Install easyhaproxy as a tool
|
||||||
|
uv tool install easyhaproxy
|
||||||
|
|
||||||
|
# Make sure ~/.local/bin is in PATH (one-time setup)
|
||||||
|
uv tool update-shell
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternative: `pip`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install easyhaproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
:::note Virtual environments
|
||||||
|
When installing inside a virtual environment, `easy-haproxy` is only available while the environment is activated. For system-wide use, prefer `uv tool install`.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
### Static mode (bare-metal / VM)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/easyhaproxy/static
|
||||||
|
|
||||||
|
cat > ~/easyhaproxy/static/config.yml <<EOF
|
||||||
|
containers:
|
||||||
|
"myapp.example.com:80":
|
||||||
|
ip: ["127.0.0.1:3000"]
|
||||||
|
EOF
|
||||||
|
|
||||||
|
easy-haproxy --discover static
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
easy-haproxy --discover docker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kubernetes mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
easy-haproxy --discover kubernetes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running as a systemd service
|
||||||
|
|
||||||
|
```ini title="/etc/systemd/system/easy-haproxy.service"
|
||||||
|
[Unit]
|
||||||
|
Description=EasyHAProxy
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/local/bin/easy-haproxy --discover static --haproxy-password mysecret
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now easy-haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip Adjust ExecStart path
|
||||||
|
Run `which easy-haproxy` to get the correct binary path. If installed with `uv tool`, it is typically `/root/.local/bin/easy-haproxy` when running as root.
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full options
|
||||||
|
|
||||||
|
- [CLI Reference](../reference/cli.md) — all flags and environment variables
|
||||||
|
- [ACME / Let's Encrypt](../guides/acme.md) — automatic certificate issuing
|
||||||
|
- [Plugins](../guides/plugins.md) — extend functionality
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
105
docs/getting-started/static.md
Normal file
105
docs/getting-started/static.md
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
sidebar_label: "Static YAML"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Static YAML
|
||||||
|
|
||||||
|
Use this mode to configure EasyHAProxy from a hand-written YAML file. Works for any backend — Docker containers, VMs, bare-metal servers, or anything reachable by IP/hostname.
|
||||||
|
|
||||||
|
:::tip Live Reload
|
||||||
|
EasyHAProxy monitors this file for changes and automatically reconfigures HAProxy when any changes are detected.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Step 1 — Write a minimal config file
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
containers:
|
||||||
|
"myapp.example.com:80":
|
||||||
|
ip: ["10.0.0.5:3000"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Save this as `config.yml`.
|
||||||
|
|
||||||
|
## Step 2 — Run EasyHAProxy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name easy-haproxy-container \
|
||||||
|
-v /my/static/:/etc/easyhaproxy/static/ \
|
||||||
|
-e EASYHAPROXY_DISCOVER="static" \
|
||||||
|
-p 80:80 \
|
||||||
|
-p 443:443 \
|
||||||
|
-p 1936:1936 \
|
||||||
|
byjg/easy-haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip Docker Socket Optional
|
||||||
|
Mounting `/var/run/docker.sock` is not required in static discovery mode.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Step 3 — Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://myapp.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full YAML reference
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
stats:
|
||||||
|
username: admin # Optional (default "admin")
|
||||||
|
password: password # If omitted, stats are public with no password
|
||||||
|
port: 1936 # Optional (default 1936)
|
||||||
|
|
||||||
|
customerrors: true # Optional (default false)
|
||||||
|
|
||||||
|
ssl_mode: default # Optional
|
||||||
|
|
||||||
|
logLevel:
|
||||||
|
certbot: DEBUG # Optional. Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
||||||
|
easyhaproxy: DEBUG # Optional. Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
||||||
|
haproxy: INFO # Optional. Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
||||||
|
|
||||||
|
certbot:
|
||||||
|
email: "acme@example.org" # If set, enables ACME/Certbot
|
||||||
|
autoconfig: "" # Well-known CA shorthand (e.g. letsencrypt)
|
||||||
|
eab_hmac_key: "" # Required by some CAs
|
||||||
|
eab_kid: "" # Required by some CAs
|
||||||
|
server: False # ACME endpoint URL (or False for Let's Encrypt)
|
||||||
|
retry_count: 60 # Retry after rate limit
|
||||||
|
|
||||||
|
containers:
|
||||||
|
# Format: "hostname:port"
|
||||||
|
"host1.com.br:80":
|
||||||
|
ip: ["container:5000"] # Endpoints: "address:localport"
|
||||||
|
certbot: true # Request certbot certificate
|
||||||
|
redirect_ssl: true # Redirect HTTP to HTTPS
|
||||||
|
mode: http # Default `http`. Can be http or tcp
|
||||||
|
|
||||||
|
# HTTPS version (SSL)
|
||||||
|
"host1.com.br:443":
|
||||||
|
ip: ["container:80"]
|
||||||
|
ssl: true # Enable SSL for this port
|
||||||
|
|
||||||
|
# Redirect www → main domain
|
||||||
|
"www.host1.com.br:80":
|
||||||
|
ip: ["container:5000"]
|
||||||
|
redirect_ssl: true
|
||||||
|
```
|
||||||
|
|
||||||
|
:::note SSL Certificates in Static Mode
|
||||||
|
The only way to provide SSL certificates in static configuration mode is to map the certificate files as a Docker volume. See the [SSL documentation](../guides/ssl.md) to learn how to configure this.
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full options
|
||||||
|
|
||||||
|
- [Container label reference](../reference/container-labels.md) — label semantics also apply to static YAML keys
|
||||||
|
- [Environment variable reference](../reference/environment-variables.md) — configure EasyHAProxy behavior
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
100
docs/getting-started/swarm.md
Normal file
100
docs/getting-started/swarm.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
sidebar_label: "Docker Swarm"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Docker Swarm
|
||||||
|
|
||||||
|
EasyHAProxy inspects Docker Swarm services, reads their labels, and configures HAProxy automatically across all nodes.
|
||||||
|
|
||||||
|
:::tip Docker Swarm Advantages
|
||||||
|
- **Container Discovery**: Docker Swarm facilitates the discovery of containers within the cluster.
|
||||||
|
- **Remote Node Management**: Manage containers across multiple nodes while EasyHAProxy configures HAProxy seamlessly.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::warning Limitations
|
||||||
|
- You cannot mix Docker containers with Swarm containers.
|
||||||
|
- This method does not work with containers that use the `--network=host` option. See [limitations](../concepts/limitations.md) for details.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Step 1 — Create an overlay network
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network create -d overlay --attachable easyhaproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2 — Deploy EasyHAProxy as a Swarm stack
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
haproxy:
|
||||||
|
image: byjg/easy-haproxy:6.0.0
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
environment:
|
||||||
|
EASYHAPROXY_DISCOVER: swarm
|
||||||
|
EASYHAPROXY_SSL_MODE: "loose"
|
||||||
|
HAPROXY_CUSTOMERRORS: "true"
|
||||||
|
HAPROXY_USERNAME: admin
|
||||||
|
HAPROXY_PASSWORD: password
|
||||||
|
HAPROXY_STATS_PORT: 1936
|
||||||
|
ports:
|
||||||
|
- "80:80/tcp"
|
||||||
|
- "443:443/tcp"
|
||||||
|
- "1936:1936/tcp"
|
||||||
|
networks:
|
||||||
|
- easyhaproxy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
easyhaproxy:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker stack deploy --compose-file docker-compose.yml easyhaproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
:::danger Single Replica Only
|
||||||
|
**Do not** add more than one replica for EasyHAProxy. To understand why, see the [limitations](../concepts/limitations.md) page.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Step 3 — Label your service
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
container:
|
||||||
|
image: my/image:tag
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
labels:
|
||||||
|
easyhaproxy.http.host: host1.local
|
||||||
|
easyhaproxy.http.port: 80
|
||||||
|
easyhaproxy.http.localport: 8080
|
||||||
|
networks:
|
||||||
|
- easyhaproxy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
easyhaproxy:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
EasyHAProxy detects this service automatically and routes traffic from `host1.local:80` to your container. You do not need to expose any container ports.
|
||||||
|
|
||||||
|
## Step 4 — Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://host1.local
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full options
|
||||||
|
|
||||||
|
- [Container label reference](../reference/container-labels.md) — all available labels
|
||||||
|
- [Environment variable reference](../reference/environment-variables.md) — configure EasyHAProxy behavior
|
||||||
|
- [Docker guide](docker.md) — more detailed Docker examples
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
4
docs/guides/_category_.json
Normal file
4
docs/guides/_category_.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"label": "Guides",
|
||||||
|
"position": 2
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 11
|
sidebar_position: 2
|
||||||
|
sidebar_label: "ACME / Let's Encrypt"
|
||||||
---
|
---
|
||||||
|
|
||||||
# SSL - Automatic Certificate Management Environment (ACME)
|
# SSL - Automatic Certificate Management Environment (ACME)
|
||||||
|
|
@ -38,7 +39,7 @@ At a high level, ACME with Easy HAProxy works in two stages:
|
||||||
|
|
||||||
2. Enable ACME per domain (per service/app)
|
2. Enable ACME per domain (per service/app)
|
||||||
- Add the label `easyhaproxy.<definition>.certbot=true` to the service you want a certificate for.
|
- Add the label `easyhaproxy.<definition>.certbot=true` to the service you want a certificate for.
|
||||||
- Ensure the service is exposed on HTTP port 80 from EasyHAProxy’s perspective (e.g., `easyhaproxy.<definition>.port=80`). ACME HTTP-01 will not work if the front port is not 80.
|
- Ensure the service is exposed on HTTP port 80 from EasyHAProxy's perspective (e.g., `easyhaproxy.<definition>.port=80`). ACME HTTP-01 will not work if the front port is not 80.
|
||||||
- Provide the domain via `easyhaproxy.<definition>.host=yourdomain.tld` (and additional labels per your install method).
|
- Provide the domain via `easyhaproxy.<definition>.host=yourdomain.tld` (and additional labels per your install method).
|
||||||
|
|
||||||
What happens under the hood
|
What happens under the hood
|
||||||
|
|
@ -172,10 +173,6 @@ services:
|
||||||
EASYHAPROXY_CERTBOT_EMAIL: your-email@example.com
|
EASYHAPROXY_CERTBOT_EMAIL: your-email@example.com
|
||||||
EASYHAPROXY_CERTBOT_AUTOCONFIG: letsencrypt
|
EASYHAPROXY_CERTBOT_AUTOCONFIG: letsencrypt
|
||||||
|
|
||||||
# ACME/Certbot Configuration (Method 2: Manual)
|
|
||||||
# EASYHAPROXY_CERTBOT_EMAIL: your-email@example.com
|
|
||||||
# EASYHAPROXY_CERTBOT_SERVER: https://acme-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
# Other settings
|
# Other settings
|
||||||
EASYHAPROXY_SSL_MODE: "default"
|
EASYHAPROXY_SSL_MODE: "default"
|
||||||
HAPROXY_CUSTOMERRORS: "true"
|
HAPROXY_CUSTOMERRORS: "true"
|
||||||
|
|
@ -267,28 +264,5 @@ If you hit Let's Encrypt rate limits:
|
||||||
- Ensure `/etc/easyhaproxy/certs/certbot` volume is properly persisted
|
- Ensure `/etc/easyhaproxy/certs/certbot` volume is properly persisted
|
||||||
- See: https://letsencrypt.org/docs/rate-limits/
|
- See: https://letsencrypt.org/docs/rate-limits/
|
||||||
|
|
||||||
### Using Both ACME and Manual Certificates
|
|
||||||
|
|
||||||
You can use both simultaneously:
|
|
||||||
1. Mount both volumes (`certs_certbot` and `certs_haproxy`)
|
|
||||||
2. Use `certbot=true` label for domains that should use ACME
|
|
||||||
3. Omit the label for domains using manual certificates
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
# This service uses ACME
|
|
||||||
app1:
|
|
||||||
labels:
|
|
||||||
easyhaproxy.http.host: auto.example.com
|
|
||||||
easyhaproxy.http.certbot: "true"
|
|
||||||
|
|
||||||
# This service uses manual certificate
|
|
||||||
app2:
|
|
||||||
labels:
|
|
||||||
easyhaproxy.http.host: manual.example.com
|
|
||||||
# No certbot label - will use /etc/easyhaproxy/certs/haproxy/manual.example.com.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
----
|
----
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 9
|
sidebar_position: 9
|
||||||
|
sidebar_label: "DigitalOcean"
|
||||||
---
|
---
|
||||||
|
|
||||||
# DigitalOcean
|
# DigitalOcean
|
||||||
|
|
@ -12,4 +13,3 @@ You can install it with a few clicks directly from the DigitalOcean dashboard.
|
||||||
## Installing EasyHAProxy on DigitalOcean
|
## Installing EasyHAProxy on DigitalOcean
|
||||||
|
|
||||||
Please refer the [EasyHAProxy page on DigitalOcean Marketplace](https://marketplace.digitalocean.com/apps/easyhaproxy-ingress-controller).
|
Please refer the [EasyHAProxy page on DigitalOcean Marketplace](https://marketplace.digitalocean.com/apps/easyhaproxy-ingress-controller).
|
||||||
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 8
|
sidebar_position: 8
|
||||||
|
sidebar_label: "Dokku"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Dokku
|
# Dokku
|
||||||
50
docs/guides/helm.md
Normal file
50
docs/guides/helm.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 6
|
||||||
|
sidebar_label: "Helm"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Helm 3
|
||||||
|
|
||||||
|
Helm is a package manager for Kubernetes. It allows you to install and manage applications on Kubernetes.
|
||||||
|
|
||||||
|
## Setup EasyHAProxy with Helm 3
|
||||||
|
|
||||||
|
### 1) Install EasyHAProxy
|
||||||
|
|
||||||
|
```bash title="Add the Helm repository"
|
||||||
|
helm repo add byjg https://opensource.byjg.com/helm
|
||||||
|
helm repo update byjg
|
||||||
|
kubectl create namespace easyhaproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash title="Install with Helm"
|
||||||
|
helm upgrade --install ingress byjg/easyhaproxy \
|
||||||
|
--namespace easyhaproxy \
|
||||||
|
--set resources.requests.cpu=100m \
|
||||||
|
--set resources.requests.memory=128Mi
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2) Choose a deployment mode
|
||||||
|
|
||||||
|
By default, EasyHAProxy installs as a **DaemonSet** (`service.create: false`). To use the recommended **NodePort** or **ClusterIP** modes instead, set `service.create: true`:
|
||||||
|
|
||||||
|
```bash title="NodePort (recommended)"
|
||||||
|
helm upgrade --install ingress byjg/easyhaproxy \
|
||||||
|
--namespace easyhaproxy \
|
||||||
|
--set service.create=true \
|
||||||
|
--set service.type=NodePort
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash title="ClusterIP (behind LoadBalancer)"
|
||||||
|
helm upgrade --install ingress byjg/easyhaproxy \
|
||||||
|
--namespace easyhaproxy \
|
||||||
|
--set service.create=true \
|
||||||
|
--set service.type=ClusterIP
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Deployment Modes](../getting-started/kubernetes.md#deployment-modes) for a comparison of all three modes.
|
||||||
|
|
||||||
|
For the complete list of configurable values, see the [Helm Values reference](../reference/helm.md).
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 7
|
sidebar_position: 7
|
||||||
|
sidebar_label: "MicroK8s"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Microk8s Add-ons
|
# Microk8s Add-ons
|
||||||
|
|
@ -74,7 +75,7 @@ microk8s enable easyhaproxy --nodeport
|
||||||
You need to disable any ingress controller you have previously installed (e.g., nginx, traefik, etc.) before installing EasyHAProxy to avoid conflicts.
|
You need to disable any ingress controller you have previously installed (e.g., nginx, traefik, etc.) before installing EasyHAProxy to avoid conflicts.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
For more parameters you can refer to the [Kubernetes](kubernetes.md) page.
|
For more parameters you can refer to the [Kubernetes](../getting-started/kubernetes.md) page.
|
||||||
|
|
||||||
----
|
----
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
695
docs/guides/plugin-development.md
Normal file
695
docs/guides/plugin-development.md
Normal file
|
|
@ -0,0 +1,695 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
sidebar_label: "Plugin Development"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plugin Development Guide
|
||||||
|
|
||||||
|
This comprehensive guide covers everything you need to know about developing plugins for EasyHAProxy. Plugins extend HAProxy configuration with custom functionality and can be integrated seamlessly with Docker, Kubernetes, and Swarm environments.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Plugin Architecture](#plugin-architecture)
|
||||||
|
3. [Quick Start Guide](#quick-start-guide)
|
||||||
|
4. [API Reference](#api-reference)
|
||||||
|
5. [Advanced Examples](#advanced-examples)
|
||||||
|
6. [Best Practices](#best-practices)
|
||||||
|
7. [Testing Guidelines](#testing-guidelines)
|
||||||
|
8. [Troubleshooting](#troubleshooting)
|
||||||
|
9. [Distribution](#distribution)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
### What is a Plugin?
|
||||||
|
|
||||||
|
A plugin is a Python class that implements the `PluginInterface` and extends HAProxy's configuration during the discovery cycle. Plugins can:
|
||||||
|
|
||||||
|
- **Inject HAProxy configuration** - Add custom HAProxy directives (ACLs, http-request rules, etc.)
|
||||||
|
- **Modify discovery data** - Transform the easymapping structure before HAProxy config generation
|
||||||
|
- **Perform maintenance tasks** - Execute cleanup, monitoring, or integration tasks
|
||||||
|
- **Integrate with external services** - Connect to APIs, databases, or third-party systems
|
||||||
|
|
||||||
|
### Why Build a Plugin?
|
||||||
|
|
||||||
|
Build a plugin when you need to:
|
||||||
|
|
||||||
|
- Add domain-specific HAProxy configuration based on labels/annotations
|
||||||
|
- Integrate with CDNs, load balancers, or security services
|
||||||
|
- Implement custom authentication or authorization logic
|
||||||
|
- Perform scheduled maintenance or monitoring tasks
|
||||||
|
- Extend EasyHAProxy without modifying core code
|
||||||
|
|
||||||
|
### Plugin System Benefits
|
||||||
|
|
||||||
|
- **Zero code changes** - Plugins don't modify EasyHAProxy core
|
||||||
|
- **Hot reload support** - Plugins reload on each discovery cycle
|
||||||
|
- **Configuration flexibility** - Configure via YAML, environment variables, or container labels
|
||||||
|
- **Error isolation** - Plugin errors don't crash the main application (configurable)
|
||||||
|
- **Easy distribution** - Share plugins as single Python files
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plugin Architecture
|
||||||
|
|
||||||
|
### Plugin Types
|
||||||
|
|
||||||
|
EasyHAProxy supports two plugin execution models:
|
||||||
|
|
||||||
|
#### 1. GLOBAL Plugins
|
||||||
|
|
||||||
|
Execute **once per discovery cycle**, regardless of discovered domains.
|
||||||
|
|
||||||
|
**Execution timing:** After discovery, before domain processing
|
||||||
|
|
||||||
|
**Use cases:**
|
||||||
|
- Cleanup tasks (removing old temp files)
|
||||||
|
- Global monitoring (health checks, metrics)
|
||||||
|
- DNS updates (updating external DNS records)
|
||||||
|
- Log rotation or archiving
|
||||||
|
- Integration with global services
|
||||||
|
|
||||||
|
**Example:** CleanupPlugin - removes old temporary files once per cycle
|
||||||
|
|
||||||
|
#### 2. DOMAIN Plugins
|
||||||
|
|
||||||
|
Execute **once per discovered domain/host**.
|
||||||
|
|
||||||
|
**Execution timing:** During domain processing, before backend config generation
|
||||||
|
|
||||||
|
**Use cases:**
|
||||||
|
- Domain-specific HAProxy rules (IP whitelisting, rate limiting)
|
||||||
|
- CDN integration (Cloudflare IP restoration)
|
||||||
|
- Path-based controls (blocking specific URLs)
|
||||||
|
- Custom headers or redirects per domain
|
||||||
|
- JWT validation or authentication
|
||||||
|
|
||||||
|
**Example:** CloudflarePlugin - restores visitor IP for each Cloudflare-enabled domain
|
||||||
|
|
||||||
|
### Plugin Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
1. LOAD PHASE
|
||||||
|
├─ PluginManager scans plugins directory
|
||||||
|
├─ Imports plugin modules
|
||||||
|
├─ Instantiates plugin classes
|
||||||
|
└─ Categorizes by type (GLOBAL/DOMAIN)
|
||||||
|
|
||||||
|
2. CONFIGURE PHASE
|
||||||
|
├─ Loads configuration from YAML/env
|
||||||
|
├─ Calls plugin.configure(config) for each plugin
|
||||||
|
└─ Validates configuration (plugin responsibility)
|
||||||
|
|
||||||
|
3. INITIALIZE PHASE
|
||||||
|
├─ Calls plugin.initialize() for each plugin
|
||||||
|
├─ Plugins request file system resources (directories, files)
|
||||||
|
├─ PluginManager processes resource requests
|
||||||
|
└─ Creates directories and files as needed
|
||||||
|
|
||||||
|
4. EXECUTION PHASE (per discovery cycle)
|
||||||
|
├─ GLOBAL PLUGINS
|
||||||
|
│ └─ Executes all global plugins once
|
||||||
|
│
|
||||||
|
└─ DOMAIN PLUGINS
|
||||||
|
└─ For each discovered domain:
|
||||||
|
└─ Executes all domain plugins
|
||||||
|
|
||||||
|
5. RESULT PROCESSING
|
||||||
|
├─ Collects PluginResult from each plugin
|
||||||
|
├─ Injects haproxy_config into backend sections
|
||||||
|
├─ Injects global_configs into global section
|
||||||
|
├─ Injects defaults_configs into defaults section
|
||||||
|
├─ Applies modified_easymapping if provided
|
||||||
|
└─ Logs metadata for debugging
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plugin Loading Order
|
||||||
|
|
||||||
|
1. **Builtin plugins** - Loaded from `/src/plugins/builtin/`
|
||||||
|
2. **External plugins** - Loaded from `/etc/easyhaproxy/plugins/`
|
||||||
|
|
||||||
|
Plugins are discovered automatically by filename (`*.py` excluding `__*.py`).
|
||||||
|
|
||||||
|
### Data Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Container Labels/Annotations
|
||||||
|
↓
|
||||||
|
Discovery (Docker/K8s/Swarm)
|
||||||
|
↓
|
||||||
|
parsed_object: {IP: labels}
|
||||||
|
↓
|
||||||
|
[GLOBAL PLUGINS] ← PluginContext (parsed_object, easymapping, env)
|
||||||
|
↓
|
||||||
|
easymapping: [list of domain configs]
|
||||||
|
↓
|
||||||
|
For each domain:
|
||||||
|
[DOMAIN PLUGINS] ← PluginContext (domain, port, host_config, ...)
|
||||||
|
↓
|
||||||
|
PluginResult → haproxy_config snippets
|
||||||
|
↓
|
||||||
|
HAProxy Configuration File
|
||||||
|
↓
|
||||||
|
HAProxy Reload
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start Guide
|
||||||
|
|
||||||
|
### Step 1: Create Plugin File
|
||||||
|
|
||||||
|
Create a new Python file in `/etc/easyhaproxy/plugins/` (or builtin location for core plugins):
|
||||||
|
|
||||||
|
```python
|
||||||
|
# /etc/easyhaproxy/plugins/my_plugin.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Add parent directory to path for imports
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from plugins import PluginInterface, PluginType, PluginContext, PluginResult
|
||||||
|
from functions import logger_easyhaproxy
|
||||||
|
|
||||||
|
|
||||||
|
class MyPlugin(PluginInterface):
|
||||||
|
"""My custom plugin description"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# Initialize default configuration
|
||||||
|
self.enabled = True
|
||||||
|
self.my_setting = "default_value"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
"""Return unique plugin name"""
|
||||||
|
return "my_plugin"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin_type(self) -> PluginType:
|
||||||
|
"""Return plugin type (GLOBAL or DOMAIN)"""
|
||||||
|
return PluginType.DOMAIN
|
||||||
|
|
||||||
|
def configure(self, config: dict) -> None:
|
||||||
|
"""
|
||||||
|
Configure plugin from YAML/env/labels
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: Dictionary with plugin configuration
|
||||||
|
"""
|
||||||
|
if "enabled" in config:
|
||||||
|
self.enabled = str(config["enabled"]).lower() in ["true", "1", "yes"]
|
||||||
|
|
||||||
|
if "my_setting" in config:
|
||||||
|
self.my_setting = config["my_setting"]
|
||||||
|
|
||||||
|
def process(self, context: PluginContext) -> PluginResult:
|
||||||
|
"""
|
||||||
|
Process plugin logic and return result
|
||||||
|
|
||||||
|
Args:
|
||||||
|
context: PluginContext with execution data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
PluginResult with HAProxy config and metadata
|
||||||
|
"""
|
||||||
|
if not self.enabled:
|
||||||
|
return PluginResult()
|
||||||
|
|
||||||
|
# Generate HAProxy configuration
|
||||||
|
haproxy_config = f"""# My Plugin - Custom functionality
|
||||||
|
http-request set-header X-My-Header {self.my_setting}"""
|
||||||
|
|
||||||
|
return PluginResult(
|
||||||
|
haproxy_config=haproxy_config,
|
||||||
|
metadata={
|
||||||
|
"domain": context.domain,
|
||||||
|
"setting_value": self.my_setting
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Enable Plugin
|
||||||
|
|
||||||
|
**Via container label (Docker):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
myapp:
|
||||||
|
labels:
|
||||||
|
easyhaproxy.http.host: example.com
|
||||||
|
easyhaproxy.http.plugins: my_plugin
|
||||||
|
easyhaproxy.http.plugin.my_plugin.my_setting: custom_value
|
||||||
|
```
|
||||||
|
|
||||||
|
**Via YAML configuration:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# /etc/easyhaproxy/static/config.yaml
|
||||||
|
plugins:
|
||||||
|
enabled: [my_plugin]
|
||||||
|
config:
|
||||||
|
my_plugin:
|
||||||
|
enabled: true
|
||||||
|
my_setting: custom_value
|
||||||
|
```
|
||||||
|
|
||||||
|
**Via environment variable:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
EASYHAPROXY_PLUGINS_ENABLED=my_plugin
|
||||||
|
EASYHAPROXY_PLUGIN_MY_PLUGIN_MY_SETTING=custom_value
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Test Plugin
|
||||||
|
|
||||||
|
Restart EasyHAProxy and check logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose restart haproxy
|
||||||
|
docker-compose logs -f haproxy | grep my_plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
[INFO] Loaded external plugin: my_plugin (domain)
|
||||||
|
[DEBUG] Configured plugin: my_plugin with config: {'my_setting': 'custom_value'}
|
||||||
|
[DEBUG] Executing domain plugin: my_plugin for domain: example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### PluginInterface
|
||||||
|
|
||||||
|
Base class all plugins must inherit from.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class PluginInterface(ABC):
|
||||||
|
"""Base class all plugins must inherit"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def name(self) -> str:
|
||||||
|
"""Return the unique plugin name"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def plugin_type(self) -> PluginType:
|
||||||
|
"""Return the plugin type (GLOBAL or DOMAIN)"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def configure(self, config: dict) -> None:
|
||||||
|
"""
|
||||||
|
Configure the plugin with settings from YAML/env/labels
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: Dictionary with plugin-specific configuration
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def initialize(self) -> InitializationResult:
|
||||||
|
"""
|
||||||
|
Initialize plugin resources (new in v2.0)
|
||||||
|
|
||||||
|
Optional method to request file system resources.
|
||||||
|
Default implementation returns empty result (no-op).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
InitializationResult with resource requests
|
||||||
|
"""
|
||||||
|
return InitializationResult()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def process(self, context: PluginContext) -> PluginResult:
|
||||||
|
"""
|
||||||
|
Process the plugin logic and return result
|
||||||
|
|
||||||
|
Args:
|
||||||
|
context: PluginContext with all necessary data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
PluginResult with HAProxy config snippets and/or modified data
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**Properties:**
|
||||||
|
|
||||||
|
- `name` - Unique identifier (used in configuration and logs)
|
||||||
|
- `plugin_type` - Execution model (`PluginType.GLOBAL` or `PluginType.DOMAIN`)
|
||||||
|
|
||||||
|
**Methods:**
|
||||||
|
|
||||||
|
- `configure(config)` - Receives plugin configuration during initialization
|
||||||
|
- `initialize()` - **[New in v2.0]** Request file system resources (optional)
|
||||||
|
- `process(context)` - Main execution logic, returns `PluginResult`
|
||||||
|
|
||||||
|
### PluginType
|
||||||
|
|
||||||
|
Enum defining plugin execution types.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class PluginType(Enum):
|
||||||
|
"""Plugin execution types"""
|
||||||
|
GLOBAL = "global" # Execute once per discovery cycle
|
||||||
|
DOMAIN = "domain" # Execute per domain/host
|
||||||
|
```
|
||||||
|
|
||||||
|
### PluginContext
|
||||||
|
|
||||||
|
Container for all plugin execution data.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class PluginContext:
|
||||||
|
"""Container for all plugin execution data"""
|
||||||
|
parsed_object: dict # {IP: labels} from discovery
|
||||||
|
easymapping: list # Current HAProxy mapping structure
|
||||||
|
container_env: dict # Environment configuration
|
||||||
|
domain: Optional[str] = None # Domain name (for DOMAIN plugins)
|
||||||
|
port: Optional[str] = None # Port (for DOMAIN plugins)
|
||||||
|
host_config: Optional[dict] = None # Domain-specific config
|
||||||
|
```
|
||||||
|
|
||||||
|
### PluginResult
|
||||||
|
|
||||||
|
Plugin execution result containing configuration and metadata.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class PluginResult:
|
||||||
|
"""Plugin execution result"""
|
||||||
|
haproxy_config: str = "" # HAProxy config snippet to inject
|
||||||
|
modified_easymapping: Optional[list] = None # Modified easymapping structure
|
||||||
|
metadata: Dict[str, Any] = field(default_factory=dict) # Plugin metadata for logging
|
||||||
|
global_configs: list[str] = field(default_factory=list) # [New] Global-level configs
|
||||||
|
defaults_configs: list[str] = field(default_factory=list) # [New] Defaults-level configs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
|
||||||
|
- `haproxy_config` - HAProxy configuration snippet (injected into backend/frontend)
|
||||||
|
- `modified_easymapping` - Modified easymapping structure (optional, advanced use)
|
||||||
|
- `metadata` - Dictionary with debugging/logging information
|
||||||
|
- `global_configs` - **[New in v2.0]** List of global-level HAProxy configs (e.g., fcgi-app definitions)
|
||||||
|
- `defaults_configs` - **[New in v2.0]** List of defaults-level HAProxy configs (e.g., log-format)
|
||||||
|
|
||||||
|
### ResourceRequest
|
||||||
|
|
||||||
|
**[New in v2.0]** Request for file system resources during plugin initialization.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class ResourceRequest:
|
||||||
|
"""Request for file system resources"""
|
||||||
|
resource_type: str # "directory" or "file"
|
||||||
|
path: str
|
||||||
|
content: str | None = None
|
||||||
|
overwrite: bool = False
|
||||||
|
```
|
||||||
|
|
||||||
|
### InitializationResult
|
||||||
|
|
||||||
|
**[New in v2.0]** Plugin initialization result with resource requests.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class InitializationResult:
|
||||||
|
"""Plugin initialization result with resource requests"""
|
||||||
|
resources: list[ResourceRequest] = field(default_factory=list)
|
||||||
|
metadata: dict[str, Any] = field(default_factory=dict)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Examples
|
||||||
|
|
||||||
|
### Example 1: IP Whitelist Plugin (DOMAIN)
|
||||||
|
|
||||||
|
Restrict access to specific IP addresses per domain.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from plugins import PluginInterface, PluginType, PluginContext, PluginResult
|
||||||
|
|
||||||
|
|
||||||
|
class IpWhitelistPlugin(PluginInterface):
|
||||||
|
"""Plugin to restrict access to specific IP addresses"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.enabled = True
|
||||||
|
self.allowed_ips = []
|
||||||
|
self.status_code = 403
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "ip_whitelist"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin_type(self) -> PluginType:
|
||||||
|
return PluginType.DOMAIN
|
||||||
|
|
||||||
|
def configure(self, config: dict) -> None:
|
||||||
|
if "enabled" in config:
|
||||||
|
self.enabled = str(config["enabled"]).lower() in ["true", "1", "yes"]
|
||||||
|
|
||||||
|
if "allowed_ips" in config:
|
||||||
|
ips_str = str(config["allowed_ips"])
|
||||||
|
self.allowed_ips = [ip.strip() for ip in ips_str.split(",") if ip.strip()]
|
||||||
|
|
||||||
|
if "status_code" in config:
|
||||||
|
try:
|
||||||
|
self.status_code = int(config["status_code"])
|
||||||
|
except ValueError:
|
||||||
|
self.status_code = 403
|
||||||
|
|
||||||
|
def process(self, context: PluginContext) -> PluginResult:
|
||||||
|
if not self.enabled or not self.allowed_ips:
|
||||||
|
return PluginResult()
|
||||||
|
|
||||||
|
ips_str = " ".join(self.allowed_ips)
|
||||||
|
|
||||||
|
haproxy_config = f"""# IP Whitelist - Only allow specific IPs
|
||||||
|
acl whitelisted_ip src {ips_str}
|
||||||
|
http-request deny deny_status {self.status_code} if !whitelisted_ip"""
|
||||||
|
|
||||||
|
return PluginResult(
|
||||||
|
haproxy_config=haproxy_config,
|
||||||
|
metadata={
|
||||||
|
"domain": context.domain,
|
||||||
|
"allowed_ips": self.allowed_ips,
|
||||||
|
"status_code": self.status_code
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Cleanup Plugin (GLOBAL)
|
||||||
|
|
||||||
|
Perform cleanup tasks during each discovery cycle.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
import time
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from plugins import PluginInterface, PluginType, PluginContext, PluginResult
|
||||||
|
from functions import logger_easyhaproxy
|
||||||
|
|
||||||
|
|
||||||
|
class CleanupPlugin(PluginInterface):
|
||||||
|
"""Plugin to perform cleanup tasks during discovery cycle"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.enabled = True
|
||||||
|
self.max_idle_time = 300 # 5 minutes
|
||||||
|
self.cleanup_temp_files = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "cleanup"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin_type(self) -> PluginType:
|
||||||
|
return PluginType.GLOBAL
|
||||||
|
|
||||||
|
def configure(self, config: dict) -> None:
|
||||||
|
if "enabled" in config:
|
||||||
|
self.enabled = str(config["enabled"]).lower() in ["true", "1", "yes"]
|
||||||
|
|
||||||
|
if "max_idle_time" in config:
|
||||||
|
try:
|
||||||
|
self.max_idle_time = int(config["max_idle_time"])
|
||||||
|
except ValueError:
|
||||||
|
logger_easyhaproxy.warning(f"Invalid max_idle_time value: {config['max_idle_time']}, using default")
|
||||||
|
|
||||||
|
if "cleanup_temp_files" in config:
|
||||||
|
self.cleanup_temp_files = str(config["cleanup_temp_files"]).lower() in ["true", "1", "yes"]
|
||||||
|
|
||||||
|
def process(self, context: PluginContext) -> PluginResult:
|
||||||
|
if not self.enabled:
|
||||||
|
return PluginResult()
|
||||||
|
|
||||||
|
cleanup_actions = []
|
||||||
|
|
||||||
|
if self.cleanup_temp_files:
|
||||||
|
temp_dirs = ["/tmp", "/var/tmp"]
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
for temp_dir in temp_dirs:
|
||||||
|
if not os.path.exists(temp_dir):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
pattern = os.path.join(temp_dir, "easyhaproxy_*")
|
||||||
|
for filepath in glob.glob(pattern):
|
||||||
|
try:
|
||||||
|
file_age = current_time - os.path.getmtime(filepath)
|
||||||
|
if file_age > self.max_idle_time:
|
||||||
|
os.remove(filepath)
|
||||||
|
cleanup_actions.append(f"Removed old temp file: {filepath}")
|
||||||
|
except Exception as e:
|
||||||
|
logger_easyhaproxy.warning(f"Failed to remove temp file {filepath}: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
logger_easyhaproxy.warning(f"Failed to cleanup {temp_dir}: {e}")
|
||||||
|
|
||||||
|
return PluginResult(
|
||||||
|
haproxy_config="",
|
||||||
|
metadata={
|
||||||
|
"actions_performed": len(cleanup_actions),
|
||||||
|
"actions": cleanup_actions
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Use `initialize()` for resource setup** - Request directories/files during init, not in `process()`
|
||||||
|
2. **Use typed result fields** - Use `global_configs` and `defaults_configs` instead of metadata for config injection
|
||||||
|
3. **Handle errors gracefully** - Use try/except and return `PluginResult()` on error
|
||||||
|
4. **Validate in `configure()`** - Don't validate at `process()` time
|
||||||
|
5. **Use metadata for debugging** - Include useful info in `metadata` dict
|
||||||
|
6. **Support multiple boolean formats** - `str(config["enabled"]).lower() in ["true", "1", "yes"]`
|
||||||
|
7. **Support list and string formats** - Handle both YAML lists and comma-separated strings
|
||||||
|
8. **Use descriptive names** - Clear plugin name, ACL names, and config keys
|
||||||
|
9. **Document your plugin** - Include docstring with YAML and label examples
|
||||||
|
10. **Return empty result when disabled** - Check `self.enabled` first
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Guidelines
|
||||||
|
|
||||||
|
### Unit Testing
|
||||||
|
|
||||||
|
```python
|
||||||
|
from plugins import PluginContext
|
||||||
|
from plugins.builtin.my_plugin import MyPlugin
|
||||||
|
|
||||||
|
|
||||||
|
class TestMyPlugin:
|
||||||
|
def test_plugin_initialization(self):
|
||||||
|
plugin = MyPlugin()
|
||||||
|
assert plugin.name == "my_plugin"
|
||||||
|
assert plugin.enabled is True
|
||||||
|
|
||||||
|
def test_plugin_generates_config(self):
|
||||||
|
plugin = MyPlugin()
|
||||||
|
plugin.configure({"my_setting": "test_value"})
|
||||||
|
|
||||||
|
context = PluginContext(
|
||||||
|
parsed_object={},
|
||||||
|
easymapping=[],
|
||||||
|
container_env={},
|
||||||
|
domain="example.com",
|
||||||
|
port="80",
|
||||||
|
host_config={}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = plugin.process(context)
|
||||||
|
assert result.haproxy_config is not None
|
||||||
|
assert "X-My-Header test_value" in result.haproxy_config
|
||||||
|
|
||||||
|
def test_plugin_disabled(self):
|
||||||
|
plugin = MyPlugin()
|
||||||
|
plugin.configure({"enabled": "false"})
|
||||||
|
|
||||||
|
context = PluginContext(
|
||||||
|
parsed_object={}, easymapping=[], container_env={}, domain="example.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
result = plugin.process(context)
|
||||||
|
assert result.haproxy_config == ""
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Plugin Not Loading
|
||||||
|
|
||||||
|
1. File not in plugins directory: `ls -la /etc/easyhaproxy/plugins/`
|
||||||
|
2. Invalid Python syntax: `python3 -m py_compile /etc/easyhaproxy/plugins/my_plugin.py`
|
||||||
|
3. Class doesn't inherit `PluginInterface`
|
||||||
|
4. Missing required imports
|
||||||
|
|
||||||
|
### Plugin Not Executing
|
||||||
|
|
||||||
|
1. Plugin not enabled in configuration
|
||||||
|
2. Wrong plugin type for use case
|
||||||
|
3. Plugin disabled via configuration
|
||||||
|
|
||||||
|
### HAProxy Configuration Invalid
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test configuration manually:
|
||||||
|
haproxy -c -f /etc/easyhaproxy/haproxy/haproxy.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Distribution
|
||||||
|
|
||||||
|
### Option 1: Single File
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp my_plugin.py /etc/easyhaproxy/plugins/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Docker Image with Plugin
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM byjg/easy-haproxy:latest
|
||||||
|
COPY my_plugin.py /app/src/plugins/builtin/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contributing to EasyHAProxy
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Add your plugin to `src/plugins/builtin/`
|
||||||
|
3. Add tests in `src/tests/test_plugins.py`
|
||||||
|
4. Add documentation in `docs/reference/plugins/`
|
||||||
|
5. Create a pull request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For more examples, see the builtin plugins in `/src/plugins/builtin/`:
|
||||||
|
- `cloudflare.py` - Simple DOMAIN plugin
|
||||||
|
- `fastcgi.py` - Advanced DOMAIN plugin with complex config
|
||||||
|
- `jwt_validator.py` - Security plugin with path-based logic
|
||||||
|
- `ip_whitelist.py` - Access control plugin
|
||||||
|
- `cleanup.py` - GLOBAL plugin example
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 15
|
sidebar_position: 3
|
||||||
|
sidebar_label: "Using Plugins"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Using Plugins
|
# Using Plugins
|
||||||
|
|
@ -44,12 +45,12 @@ Execute **once for each discovered domain/host**.
|
||||||
|
|
||||||
EasyHAProxy includes several built-in plugins ready to use:
|
EasyHAProxy includes several built-in plugins ready to use:
|
||||||
|
|
||||||
- [Cloudflare](Plugins/cloudflare.md) - Restore visitor IP from Cloudflare CDN
|
- [Cloudflare](../reference/plugins/cloudflare.md) - Restore visitor IP from Cloudflare CDN
|
||||||
- [Cleanup](Plugins/cleanup.md) - Cleanup temporary files
|
- [Cleanup](../reference/plugins/cleanup.md) - Cleanup temporary files
|
||||||
- [Deny Pages](Plugins/deny-pages.md) - Block specific paths
|
- [Deny Pages](../reference/plugins/deny-pages.md) - Block specific paths
|
||||||
- [IP Whitelist](Plugins/ip-whitelist.md) - Restrict access to IPs/CIDR ranges
|
- [IP Whitelist](../reference/plugins/ip-whitelist.md) - Restrict access to IPs/CIDR ranges
|
||||||
- [JWT Validator](Plugins/jwt-validator.md) - Validate JWT tokens
|
- [JWT Validator](../reference/plugins/jwt-validator.md) - Validate JWT tokens
|
||||||
- [FastCGI](Plugins/fastcgi.md) - Configure PHP-FPM and FastCGI applications
|
- [FastCGI](../reference/plugins/fastcgi.md) - Configure PHP-FPM and FastCGI applications
|
||||||
|
|
||||||
## Configuration Methods
|
## Configuration Methods
|
||||||
|
|
||||||
|
|
@ -96,7 +97,7 @@ spec:
|
||||||
- Enable plugins: `easyhaproxy.plugins: plugin1,plugin2`
|
- Enable plugins: `easyhaproxy.plugins: plugin1,plugin2`
|
||||||
- Configure plugin: `easyhaproxy.plugin.<plugin_name>.<config_key>: value`
|
- Configure plugin: `easyhaproxy.plugin.<plugin_name>.<config_key>: value`
|
||||||
|
|
||||||
See the [Kubernetes guide](kubernetes.md#using-plugins-with-kubernetes) for more examples.
|
See the [Kubernetes guide](../getting-started/kubernetes.md) for more examples.
|
||||||
|
|
||||||
### 2. Container Labels (Docker/Docker Compose)
|
### 2. Container Labels (Docker/Docker Compose)
|
||||||
|
|
||||||
|
|
@ -181,11 +182,6 @@ EASYHAPROXY_PLUGIN_CLOUDFLARE_USE_BUILTIN_IPS=true
|
||||||
- Enable global plugins: `EASYHAPROXY_PLUGINS_ENABLED=plugin1,plugin2`
|
- Enable global plugins: `EASYHAPROXY_PLUGINS_ENABLED=plugin1,plugin2`
|
||||||
- Configure plugin: `EASYHAPROXY_PLUGIN_<PLUGIN_NAME>_<CONFIG_KEY>=value`
|
- Configure plugin: `EASYHAPROXY_PLUGIN_<PLUGIN_NAME>_<CONFIG_KEY>=value`
|
||||||
|
|
||||||
**Scope limitations:**
|
|
||||||
- **Global plugins**: Environment variables configure the single instance
|
|
||||||
- **Domain plugins**: Environment variables set defaults for ALL domains
|
|
||||||
- **Per-domain configuration**: Use container labels (Docker) or annotations (Kubernetes) instead
|
|
||||||
|
|
||||||
## Common Use Cases
|
## Common Use Cases
|
||||||
|
|
||||||
### Protect API with JWT Authentication
|
### Protect API with JWT Authentication
|
||||||
|
|
@ -205,42 +201,8 @@ services:
|
||||||
- ./auth_pubkey.pem:/etc/easyhaproxy/jwt_keys/api_pubkey.pem:ro
|
- ./auth_pubkey.pem:/etc/easyhaproxy/jwt_keys/api_pubkey.pem:ro
|
||||||
```
|
```
|
||||||
|
|
||||||
**Protect only admin/sensitive endpoints:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
api:
|
|
||||||
labels:
|
|
||||||
easyhaproxy.http.host: api.example.com
|
|
||||||
easyhaproxy.http.plugins: jwt_validator
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/easyhaproxy/jwt_keys/api_pubkey.pem
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.paths: /api/admin,/api/users,/api/billing
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.only_paths: false
|
|
||||||
volumes:
|
|
||||||
- ./auth_pubkey.pem:/etc/easyhaproxy/jwt_keys/api_pubkey.pem:ro
|
|
||||||
# /api/health, /api/docs, etc. remain publicly accessible
|
|
||||||
```
|
|
||||||
|
|
||||||
**Restrict API to only allow specific endpoints:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
api:
|
|
||||||
labels:
|
|
||||||
easyhaproxy.http.host: api.example.com
|
|
||||||
easyhaproxy.http.plugins: jwt_validator
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/easyhaproxy/jwt_keys/api_pubkey.pem
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.paths: /api/v1,/api/v2
|
|
||||||
easyhaproxy.http.plugin.jwt_validator.only_paths: true
|
|
||||||
volumes:
|
|
||||||
- ./auth_pubkey.pem:/etc/easyhaproxy/jwt_keys/api_pubkey.pem:ro
|
|
||||||
# All paths except /api/v1 and /api/v2 are denied
|
|
||||||
```
|
|
||||||
|
|
||||||
### Restrict Admin Panel to Office IPs
|
### Restrict Admin Panel to Office IPs
|
||||||
|
|
||||||
Protect admin panel by only allowing access from office network:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
easyhaproxy.http.host: admin.example.com
|
easyhaproxy.http.host: admin.example.com
|
||||||
|
|
@ -262,8 +224,6 @@ labels:
|
||||||
|
|
||||||
### Cloudflare IP Restoration
|
### Cloudflare IP Restoration
|
||||||
|
|
||||||
Restore original visitor IPs for applications behind Cloudflare:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
easyhaproxy.http.host: myapp.com
|
easyhaproxy.http.host: myapp.com
|
||||||
|
|
@ -272,8 +232,6 @@ labels:
|
||||||
|
|
||||||
### Multiple Plugins Together
|
### Multiple Plugins Together
|
||||||
|
|
||||||
Combine multiple plugins for one domain:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
easyhaproxy.http.host: secure-app.com
|
easyhaproxy.http.host: secure-app.com
|
||||||
|
|
@ -284,8 +242,6 @@ labels:
|
||||||
|
|
||||||
### Automatic Cleanup
|
### Automatic Cleanup
|
||||||
|
|
||||||
Keep your system clean with automatic temp file removal:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# /etc/easyhaproxy/static/config.yaml
|
# /etc/easyhaproxy/static/config.yaml
|
||||||
plugins:
|
plugins:
|
||||||
|
|
@ -307,14 +263,6 @@ plugins:
|
||||||
abort_on_error: false # Default
|
abort_on_error: false # Default
|
||||||
```
|
```
|
||||||
|
|
||||||
**When to use:** Most situations. Ensures a failing plugin doesn't prevent HAProxy updates.
|
|
||||||
|
|
||||||
**Behavior:**
|
|
||||||
- Plugin errors logged as warnings
|
|
||||||
- Discovery cycle continues
|
|
||||||
- Other plugins still execute
|
|
||||||
- HAProxy config is generated without the failed plugin
|
|
||||||
|
|
||||||
### Abort on Error
|
### Abort on Error
|
||||||
|
|
||||||
Stop discovery cycle if any plugin fails:
|
Stop discovery cycle if any plugin fails:
|
||||||
|
|
@ -324,19 +272,10 @@ plugins:
|
||||||
abort_on_error: true
|
abort_on_error: true
|
||||||
```
|
```
|
||||||
|
|
||||||
**When to use:** Critical plugins where failure should halt deployment.
|
|
||||||
|
|
||||||
**Behavior:**
|
|
||||||
- Plugin error stops discovery
|
|
||||||
- Previous HAProxy config remains active
|
|
||||||
- No configuration changes until issue is resolved
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Enable Debug Logging
|
### Enable Debug Logging
|
||||||
|
|
||||||
See detailed plugin execution information:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
EASYHAPROXY_LOG_LEVEL=DEBUG
|
EASYHAPROXY_LOG_LEVEL=DEBUG
|
||||||
```
|
```
|
||||||
|
|
@ -357,18 +296,6 @@ DEBUG: Plugin cloudflare metadata: {'domain': 'example.com', 'ip_list_path': '/e
|
||||||
3. Plugin class inherits from `PluginInterface`
|
3. Plugin class inherits from `PluginInterface`
|
||||||
4. Check logs for load errors
|
4. Check logs for load errors
|
||||||
|
|
||||||
### Plugin Not Executing
|
|
||||||
|
|
||||||
**For domain plugins:**
|
|
||||||
1. Check container has label: `easyhaproxy.http.plugins: plugin_name`
|
|
||||||
2. Verify plugin name is correct (case-sensitive)
|
|
||||||
3. Enable debug logging
|
|
||||||
|
|
||||||
**For global plugins:**
|
|
||||||
1. Check YAML config: `plugins.enabled: [plugin_name]`
|
|
||||||
2. Or env var: `EASYHAPROXY_PLUGINS_ENABLED=plugin_name`
|
|
||||||
3. Enable debug logging
|
|
||||||
|
|
||||||
### Configuration Not Applied
|
### Configuration Not Applied
|
||||||
|
|
||||||
**Check precedence order:**
|
**Check precedence order:**
|
||||||
|
|
@ -383,16 +310,6 @@ For Docker deployments:
|
||||||
2. YAML configuration
|
2. YAML configuration
|
||||||
3. Environment variables (lowest)
|
3. Environment variables (lowest)
|
||||||
|
|
||||||
Per-ingress/per-container settings override global configuration.
|
|
||||||
|
|
||||||
### Plugin Output Missing
|
|
||||||
|
|
||||||
**Verify:**
|
|
||||||
1. Plugin is enabled (`enabled: true`)
|
|
||||||
2. Plugin configuration is correct
|
|
||||||
3. Plugin's `process()` method returns valid `PluginResult`
|
|
||||||
4. Check debug logs for plugin execution
|
|
||||||
|
|
||||||
## Best Practices
|
## Best Practices
|
||||||
|
|
||||||
1. **Start with log-and-continue mode** - Use `abort_on_error: false` until you're confident plugins are stable
|
1. **Start with log-and-continue mode** - Use `abort_on_error: false` until you're confident plugins are stable
|
||||||
|
|
@ -400,7 +317,6 @@ Per-ingress/per-container settings override global configuration.
|
||||||
3. **Use YAML/env for global config** - Better for global plugins and defaults
|
3. **Use YAML/env for global config** - Better for global plugins and defaults
|
||||||
4. **Enable debug logging during testing** - Helps identify configuration issues
|
4. **Enable debug logging during testing** - Helps identify configuration issues
|
||||||
5. **Test plugin changes in staging first** - Avoid production surprises
|
5. **Test plugin changes in staging first** - Avoid production surprises
|
||||||
6. **Keep plugin configurations simple** - Use defaults when possible
|
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
|
|
@ -416,7 +332,7 @@ Want to create your own plugins? See the [Plugin Developer Guide](plugin-develop
|
||||||
## Further Reading
|
## Further Reading
|
||||||
|
|
||||||
- [Plugin Developer Guide](plugin-development.md) - Create custom plugins
|
- [Plugin Developer Guide](plugin-development.md) - Create custom plugins
|
||||||
- [Container Labels](container-labels.md) - Label configuration reference
|
- [Container Labels](../reference/container-labels.md) - Label configuration reference
|
||||||
- [Environment Variables](environment-variable.md) - Environment variable reference
|
- [Environment Variables](../reference/environment-variables.md) - Environment variable reference
|
||||||
- [Static Configuration](static.md) - YAML configuration reference
|
- [Static Configuration](../getting-started/static.md) - YAML configuration reference
|
||||||
- [Kubernetes Guide](kubernetes.md) - Using plugins with Kubernetes
|
- [Kubernetes Guide](../getting-started/kubernetes.md) - Using plugins with Kubernetes
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 10
|
sidebar_position: 1
|
||||||
|
sidebar_label: "Custom SSL Certificates"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Setup custom certificates
|
# Setup custom certificates
|
||||||
85
docs/helm.md
85
docs/helm.md
|
|
@ -1,85 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 6
|
|
||||||
---
|
|
||||||
|
|
||||||
# Helm 3
|
|
||||||
|
|
||||||
Helm is a package manager for Kubernetes. It allows you to install and manage applications on Kubernetes.
|
|
||||||
|
|
||||||
## Setup EasyHAProxy with Helm 3
|
|
||||||
|
|
||||||
### 1) Identify the node where your EasyHAProxy container will run
|
|
||||||
|
|
||||||
:::warning Single Node Deployment
|
|
||||||
EasyHAProxy will be limited to a single node. To understand why, see the [limitations](limitations.md) page.
|
|
||||||
:::
|
|
||||||
|
|
||||||
```bash title="List available nodes"
|
|
||||||
$ kubectl get nodes
|
|
||||||
|
|
||||||
NAME STATUS ROLES AGE VERSION
|
|
||||||
node-01 Ready <none> 561d v1.21.13-3
|
|
||||||
node-02 Ready <none> 561d v1.21.13-3
|
|
||||||
```
|
|
||||||
|
|
||||||
Add the EasyHAProxy label to the node.
|
|
||||||
|
|
||||||
```bash title="Label the node for EasyHAProxy"
|
|
||||||
kubectl label nodes node-01 "easyhaproxy/node=master"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2) Install EasyHAProxy
|
|
||||||
|
|
||||||
Minimal configuration:
|
|
||||||
|
|
||||||
```bash title="Install with Helm (minimal)"
|
|
||||||
helm repo add byjg https://opensource.byjg.com/helm
|
|
||||||
helm repo update byjg
|
|
||||||
kubectl create namespace easyhaproxy
|
|
||||||
|
|
||||||
helm upgrade --install ingress byjg/easyhaproxy \
|
|
||||||
--namespace easyhaproxy \
|
|
||||||
--set resources.requests.cpu=100m \
|
|
||||||
--set resources.requests.memory=128Mi
|
|
||||||
```
|
|
||||||
|
|
||||||
Customizing Helm Values:
|
|
||||||
|
|
||||||
```yaml title="values.yaml"
|
|
||||||
easyhaproxy:
|
|
||||||
stats:
|
|
||||||
username: admin
|
|
||||||
password: password
|
|
||||||
refresh: "10"
|
|
||||||
customErrors: "true"
|
|
||||||
sslMode: loose
|
|
||||||
logLevel:
|
|
||||||
certbot: DEBUG
|
|
||||||
easyhaproxy: DEBUG
|
|
||||||
haproxy: DEBUG
|
|
||||||
certbot:
|
|
||||||
email: ""
|
|
||||||
|
|
||||||
service:
|
|
||||||
create: false # If false, it will create a DaemonSet with hostPort. The easiest.
|
|
||||||
type: ClusterIP # or NodePort
|
|
||||||
annotations: {}
|
|
||||||
|
|
||||||
binding:
|
|
||||||
ports:
|
|
||||||
http: 80
|
|
||||||
https: 443
|
|
||||||
stats: 1936
|
|
||||||
additionalPorts: []
|
|
||||||
|
|
||||||
# Make sure to create this
|
|
||||||
masterNode:
|
|
||||||
label: easyhaproxy/node
|
|
||||||
values:
|
|
||||||
- master
|
|
||||||
```
|
|
||||||
|
|
||||||
For more parameters you can refer to the [Kubernetes](kubernetes.md) page.
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
|
|
@ -1,514 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# Kubernetes
|
|
||||||
|
|
||||||
## Setup Kubernetes EasyHAProxy
|
|
||||||
|
|
||||||
:::info How it works
|
|
||||||
EasyHAProxy for Kubernetes operates by querying all ingress definitions with either the
|
|
||||||
`spec.ingressClassName: easyhaproxy` field (recommended) or the deprecated annotation
|
|
||||||
`kubernetes.io/ingress.class: easyhaproxy-ingress` (for backward compatibility). Upon finding
|
|
||||||
a matching ingress class, EasyHAProxy immediately sets up HAProxy and begins serving traffic.
|
|
||||||
:::
|
|
||||||
|
|
||||||
For Kubernetes installations, there are three available installation modes:
|
|
||||||
- DaemonSet: This mode exposes ports 80, 443, and 1936.
|
|
||||||
- NodePort: Ports 31080, 31443, and 31936 are exposed.
|
|
||||||
- ClusterIP: In this mode, no ports are exposed externally, and HAProxy is accessible only
|
|
||||||
within the cluster.
|
|
||||||
|
|
||||||
To install EasyHAProxy in your Kubernetes cluster, follow these steps:
|
|
||||||
|
|
||||||
|
|
||||||
### 1) Identify the node where your EasyHAProxy container will run
|
|
||||||
|
|
||||||
:::warning Single Node Deployment
|
|
||||||
EasyHAProxy will be limited to a single node. To understand why, see the [limitations](limitations.md) page.
|
|
||||||
:::
|
|
||||||
|
|
||||||
```bash title="List available nodes"
|
|
||||||
$ kubectl get nodes
|
|
||||||
|
|
||||||
NAME STATUS ROLES AGE VERSION
|
|
||||||
node-01 Ready <none> 561d v1.21.13-3
|
|
||||||
node-02 Ready <none> 561d v1.21.13-3
|
|
||||||
```
|
|
||||||
|
|
||||||
Add the EasyHAProxy label to the node.
|
|
||||||
|
|
||||||
```bash title="Label the node for EasyHAProxy"
|
|
||||||
kubectl label nodes node-01 "easyhaproxy/node=master"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2) Install EasyHAProxy with Kubernetes Manifest
|
|
||||||
|
|
||||||
```bash title="Install EasyHAProxy"
|
|
||||||
kubectl create namespace easyhaproxy
|
|
||||||
|
|
||||||
kubectl apply -f \
|
|
||||||
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/6.0.0/deploy/kubernetes/easyhaproxy-daemonset.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
If necessary, you can configure environment variables. To get a list of the variables, please follow the [environment variable guide](environment-variable.md)
|
|
||||||
|
|
||||||
## Running containers
|
|
||||||
|
|
||||||
Your container only requires creating an ingress with the `spec.ingressClassName: easyhaproxy` field pointing to your service.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: example-ingress
|
|
||||||
namespace: example
|
|
||||||
spec:
|
|
||||||
# Use ingressClassName (recommended)
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
rules:
|
|
||||||
- host: example.org
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- backend:
|
|
||||||
service:
|
|
||||||
name: example-service
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
pathType: ImplementationSpecific
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note Backward Compatibility
|
|
||||||
The deprecated annotation `kubernetes.io/ingress.class: easyhaproxy-ingress` is still supported for backward compatibility, but `spec.ingressClassName` is the recommended approach for new deployments.
|
|
||||||
:::
|
|
||||||
|
|
||||||
Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container at port 8080.
|
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
|
||||||
|
|
||||||
:::note Important Limitations
|
|
||||||
- The implementation doesn't support all ingress properties or wildcard domains at this time.
|
|
||||||
- The ingress will publish ports 80 and 443, plus 1936 if stats are enabled.
|
|
||||||
- EasyHAProxy will read all `spec.rules[].host` specifications, however it will parse only the **first path** `spec.rules[].http.paths[0].port.number` for each rule, and ignore the other paths.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Kubernetes annotations
|
|
||||||
|
|
||||||
| annotation | Description | Default | Example |
|
|
||||||
|-------------------------------------|-------------------------------------------------------------------------------------|--------------|----------------------------|
|
|
||||||
| kubernetes.io/ingress.class | (deprecated) Activate EasyHAProxy. Use `spec.ingressClassName` instead. | *optional* | 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.
|
|
||||||
|
|
||||||
## Using Plugins with Kubernetes
|
|
||||||
|
|
||||||
Plugins extend HAProxy configuration with additional functionality like JWT validation, IP whitelisting, or Cloudflare IP restoration. For a complete list of available plugins, see the [Using Plugins](plugins.md) guide.
|
|
||||||
|
|
||||||
### Enabling Plugins for an Ingress
|
|
||||||
|
|
||||||
Add the `easyhaproxy.plugins` annotation with a comma-separated list of plugin names:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
easyhaproxy.plugins: "cloudflare,deny_pages"
|
|
||||||
name: example-ingress
|
|
||||||
namespace: example
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
rules:
|
|
||||||
- host: example.org
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- backend:
|
|
||||||
service:
|
|
||||||
name: example-service
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
pathType: ImplementationSpecific
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuring Plugin Options
|
|
||||||
|
|
||||||
Use `easyhaproxy.plugin.{plugin_name}.{option}` annotations to configure individual plugins:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
easyhaproxy.plugins: "deny_pages"
|
|
||||||
easyhaproxy.plugin.deny_pages.paths: "/admin,/private,/config"
|
|
||||||
easyhaproxy.plugin.deny_pages.status_code: "403"
|
|
||||||
name: secure-app-ingress
|
|
||||||
namespace: production
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
rules:
|
|
||||||
- host: myapp.example.com
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- backend:
|
|
||||||
service:
|
|
||||||
name: myapp-service
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
pathType: ImplementationSpecific
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Plugin Examples
|
|
||||||
|
|
||||||
**Protect API with JWT validation:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
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/easyhaproxy/jwt_keys/api_pubkey.pem"
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
**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:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
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:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
**Restore Cloudflare visitor IPs:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
easyhaproxy.plugins: "cloudflare"
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
**Multiple plugins together:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
easyhaproxy.plugins: "cloudflare,deny_pages"
|
|
||||||
easyhaproxy.plugin.deny_pages.paths: "/wp-admin,/wp-login.php"
|
|
||||||
easyhaproxy.plugin.deny_pages.status_code: "404"
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
### Global Plugin Configuration
|
|
||||||
|
|
||||||
Some plugins (like `cleanup`) are global and execute once per discovery cycle. Configure these via environment variables or YAML configuration:
|
|
||||||
|
|
||||||
**Using Helm values.yaml:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
easyhaproxy:
|
|
||||||
plugins:
|
|
||||||
enabled: cleanup
|
|
||||||
config:
|
|
||||||
cleanup:
|
|
||||||
max_idle_time: 600
|
|
||||||
```
|
|
||||||
|
|
||||||
**Using environment variables:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
env:
|
|
||||||
- name: EASYHAPROXY_PLUGINS_ENABLED
|
|
||||||
value: "cleanup"
|
|
||||||
- name: EASYHAPROXY_PLUGIN_CLEANUP_MAX_IDLE_TIME
|
|
||||||
value: "600"
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information on plugin types and available plugins, see the [Using Plugins](plugins.md) guide.
|
|
||||||
|
|
||||||
## Loading Plugin Configuration from Kubernetes Secrets
|
|
||||||
|
|
||||||
EasyHAProxy supports loading sensitive plugin configuration values directly from Kubernetes Secrets using the `k8s_secret` pattern. This is a **generic, plugin-agnostic feature** that works with any plugin.
|
|
||||||
|
|
||||||
### Why Use Kubernetes Secrets?
|
|
||||||
|
|
||||||
- **Security**: Keep sensitive data (API keys, passwords, certificates) out of annotations
|
|
||||||
- **Best practices**: Follows Kubernetes conventions for managing sensitive data
|
|
||||||
- **Simplicity**: No need to mount volumes or ConfigMaps for secret data
|
|
||||||
- **Encryption**: Secrets are encrypted at rest in etcd
|
|
||||||
|
|
||||||
### Annotation Format
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Auto-detect key (tries common variations):
|
|
||||||
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name"
|
|
||||||
|
|
||||||
# Explicit key (no variations):
|
|
||||||
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name/key_name"
|
|
||||||
```
|
|
||||||
|
|
||||||
### How It Works
|
|
||||||
|
|
||||||
1. **You create** a Kubernetes Secret with your sensitive data
|
|
||||||
2. **You reference** the secret in your ingress annotation using the `k8s_secret` pattern
|
|
||||||
3. **EasyHAProxy reads** the secret from the same namespace as the ingress
|
|
||||||
4. **EasyHAProxy transforms** the annotation to inject the secret value
|
|
||||||
5. **The plugin receives** the value as if it was provided directly in the annotation
|
|
||||||
|
|
||||||
**Example transformation:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Input annotation:
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "my-jwt-secret"
|
|
||||||
|
|
||||||
# EasyHAProxy reads the secret and transforms to:
|
|
||||||
easyhaproxy.plugin.jwt_validator.pubkey: "<base64-encoded-content>"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Auto-Detect vs Explicit Key
|
|
||||||
|
|
||||||
#### Auto-Detect Key Format
|
|
||||||
|
|
||||||
When you use `"secret_name"` (without `/`), EasyHAProxy tries to find the key automatically:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "my-jwt-secret"
|
|
||||||
```
|
|
||||||
|
|
||||||
EasyHAProxy will try these keys in order:
|
|
||||||
1. Exact match: `pubkey`
|
|
||||||
2. Common variations based on the config key name
|
|
||||||
|
|
||||||
**Auto-detect key variations:**
|
|
||||||
|
|
||||||
| Config Key | Tries (in order) |
|
|
||||||
|------------|---------------------------------------|
|
|
||||||
| `pubkey` | `pubkey`, `public-key`, `jwt.pub`, `tls.crt` |
|
|
||||||
| `password` | `password`, `pass`, `pwd` |
|
|
||||||
| `api_key` | `api_key`, `apikey`, `api-key`, `key` |
|
|
||||||
|
|
||||||
#### Explicit Key Format
|
|
||||||
|
|
||||||
When you use `"secret_name/key_name"` (with `/`), EasyHAProxy only tries the exact key name:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "my-jwt-secret/rsa-public-key"
|
|
||||||
```
|
|
||||||
|
|
||||||
EasyHAProxy will **only** try: `rsa-public-key` (no variations)
|
|
||||||
|
|
||||||
**Use explicit key when:**
|
|
||||||
- Your secret uses a non-standard key name
|
|
||||||
- You want to be explicit and avoid ambiguity
|
|
||||||
- Multiple keys exist in the secret
|
|
||||||
|
|
||||||
### Complete Example
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
# 1. Create a secret with your JWT public key
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: jwt-pubkey-secret
|
|
||||||
namespace: production
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
pubkey: |
|
|
||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
|
|
||||||
---
|
|
||||||
# 2. Reference it in your ingress
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: api-ingress
|
|
||||||
namespace: production
|
|
||||||
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"
|
|
||||||
# Load pubkey from Kubernetes secret (auto-detect key)
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "jwt-pubkey-secret"
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
rules:
|
|
||||||
- host: api.example.com
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: api-service
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example with Explicit Key Name
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: app-credentials
|
|
||||||
namespace: production
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
# Custom key name
|
|
||||||
rsa-public-key: |
|
|
||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
...
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: api-ingress
|
|
||||||
namespace: production
|
|
||||||
annotations:
|
|
||||||
easyhaproxy.plugins: "jwt_validator"
|
|
||||||
# Use explicit key name after the slash
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "app-credentials/rsa-public-key"
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
# ... rest of configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using with Any Plugin
|
|
||||||
|
|
||||||
The `k8s_secret` pattern works with **any plugin configuration**:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# JWT Validator - load public key
|
|
||||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "jwt-secret"
|
|
||||||
|
|
||||||
# Hypothetical API auth plugin - load API key
|
|
||||||
easyhaproxy.plugin.api_auth.k8s_secret.api_key: "api-credentials/key"
|
|
||||||
|
|
||||||
# Hypothetical basic auth plugin - load password
|
|
||||||
easyhaproxy.plugin.basic_auth.k8s_secret.password: "auth-secret/pwd"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Priority Order
|
|
||||||
|
|
||||||
When multiple configuration methods are used, this is the priority (highest to lowest):
|
|
||||||
|
|
||||||
1. **Explicit annotation** (e.g., `easyhaproxy.plugin.jwt_validator.pubkey: "value"`)
|
|
||||||
2. **k8s_secret annotation** (e.g., `easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "secret"`)
|
|
||||||
|
|
||||||
Explicit annotations always take precedence over `k8s_secret` annotations.
|
|
||||||
|
|
||||||
### Troubleshooting
|
|
||||||
|
|
||||||
**Secret not found:**
|
|
||||||
```
|
|
||||||
WARNING: Ingress production/api-ingress - Failed to process k8s_secret annotation
|
|
||||||
'easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey' with value 'jwt-secret': ...
|
|
||||||
```
|
|
||||||
- Verify the secret exists: `kubectl get secret jwt-secret -n production`
|
|
||||||
- Check the secret is in the same namespace as the ingress
|
|
||||||
|
|
||||||
**Key not found in secret:**
|
|
||||||
```
|
|
||||||
WARNING: Ingress production/api-ingress - Secret 'jwt-secret' found but no matching
|
|
||||||
key (tried: pubkey, public-key, jwt.pub, tls.crt)
|
|
||||||
```
|
|
||||||
- List secret keys: `kubectl get secret jwt-secret -n production -o jsonpath='{.data}'`
|
|
||||||
- Use explicit key format: `"jwt-secret/actual-key-name"`
|
|
||||||
|
|
||||||
**Check EasyHAProxy logs:**
|
|
||||||
```bash
|
|
||||||
kubectl logs -n easyhaproxy -l app=easyhaproxy --tail=100
|
|
||||||
```
|
|
||||||
|
|
||||||
Look for:
|
|
||||||
- `INFO: Loaded 'pubkey' from secret 'jwt-secret'` (success)
|
|
||||||
- `WARNING: Secret 'xyz' found but no matching key` (key not found)
|
|
||||||
|
|
||||||
### Security Considerations
|
|
||||||
|
|
||||||
- Secrets are read from the **same namespace** as the ingress (no cross-namespace access)
|
|
||||||
- EasyHAProxy needs RBAC permissions to read secrets (included in default deployment)
|
|
||||||
- Secrets are encrypted at rest in etcd
|
|
||||||
- Secret values are base64-encoded by Kubernetes automatically
|
|
||||||
- Use Kubernetes RBAC to control which service accounts can read which secrets
|
|
||||||
|
|
||||||
## Certbot / ACME / Letsencrypt
|
|
||||||
|
|
||||||
It is necessary to add the annotation `easyhaproxy.certbot` to the ingress configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
easyhaproxy.certbot: 'true'
|
|
||||||
name: example-ingress
|
|
||||||
namespace: example
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
....
|
|
||||||
```
|
|
||||||
|
|
||||||
More info [here](acme.md).
|
|
||||||
|
|
||||||
Make sure your cluster is accessible both through ports 80 and 443.
|
|
||||||
|
|
||||||
## Custom SSL Certificates
|
|
||||||
|
|
||||||
Create a secret with your certificate and key and associate them with your ingress.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: host2-tls
|
|
||||||
namespace: default
|
|
||||||
data:
|
|
||||||
tls.crt: base64 of your certificate
|
|
||||||
tls.key: base64 of your certificate private key
|
|
||||||
type: kubernetes.io/tls
|
|
||||||
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: tls-example
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
ingressClassName: easyhaproxy
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- host2.local
|
|
||||||
secretName: host2-tls
|
|
||||||
rules:
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
File diff suppressed because it is too large
Load diff
4
docs/reference/_category_.json
Normal file
4
docs/reference/_category_.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"label": "Reference",
|
||||||
|
"position": 4
|
||||||
|
}
|
||||||
|
|
@ -1,84 +1,19 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 5
|
sidebar_position: 1
|
||||||
|
sidebar_label: "CLI Reference"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Install via pip / uv
|
# CLI Reference
|
||||||
|
|
||||||
EasyHAProxy can run directly on any Linux or macOS host without Docker, using the `easyhaproxy` Python package.
|
The `easy-haproxy` command is the native binary installed via `pip` or `uv`. Every option can be set as a **CLI flag** or an **environment variable**. CLI flags take precedence over environment variables.
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
HAProxy must be installed and available in your system `PATH` before running `easy-haproxy`. EasyHAProxy will refuse to start with a clear error message if HAProxy is not found.
|
|
||||||
|
|
||||||
import Tabs from '@theme/Tabs';
|
|
||||||
import TabItem from '@theme/TabItem';
|
|
||||||
|
|
||||||
<Tabs>
|
|
||||||
<TabItem value="debian" label="Debian / Ubuntu" default>
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="rhel" label="RHEL / Fedora">
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf install haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="macos" label="macOS">
|
|
||||||
|
|
||||||
```bash
|
|
||||||
brew install haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### Recommended: `uv tool` (system-wide, isolated)
|
|
||||||
|
|
||||||
[`uv`](https://docs.astral.sh/uv/) installs `easyhaproxy` into its own isolated environment and exposes the `easy-haproxy` binary in `~/.local/bin/`, similar to `pipx`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install uv (if not already installed)
|
|
||||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
||||||
|
|
||||||
# Install easyhaproxy as a tool
|
|
||||||
uv tool install easyhaproxy
|
|
||||||
|
|
||||||
# Make sure ~/.local/bin is in PATH (one-time setup)
|
|
||||||
uv tool update-shell
|
|
||||||
```
|
|
||||||
|
|
||||||
After installation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
easy-haproxy --help
|
|
||||||
```
|
|
||||||
|
|
||||||
### Alternative: `pip`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note Virtual environments
|
|
||||||
When installing inside a virtual environment, `easy-haproxy` is only available while the environment is activated. For system-wide use, prefer `uv tool install` or install with `pip` at the system/user level.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## CLI Reference
|
|
||||||
|
|
||||||
Every configuration option can be set via a CLI flag **or** an environment variable. CLI flags take precedence over environment variables.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
easy-haproxy [OPTIONS]
|
easy-haproxy [OPTIONS]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Core
|
For installation instructions, see [Native install](../getting-started/native.md).
|
||||||
|
|
||||||
|
## Core
|
||||||
|
|
||||||
| Flag | Environment Variable | Default | Description |
|
| Flag | Environment Variable | Default | Description |
|
||||||
|--------------------------|----------------------------|------------------------------------------------------|-----------------------------------------------------------|
|
|--------------------------|----------------------------|------------------------------------------------------|-----------------------------------------------------------|
|
||||||
|
|
@ -89,7 +24,7 @@ easy-haproxy [OPTIONS]
|
||||||
| `--refresh-conf SECONDS` | `EASYHAPROXY_REFRESH_CONF` | `10` | Polling interval for configuration changes |
|
| `--refresh-conf SECONDS` | `EASYHAPROXY_REFRESH_CONF` | `10` | Polling interval for configuration changes |
|
||||||
| `--customer-errors BOOL` | `HAPROXY_CUSTOMERRORS` | `false` | Enable custom HAProxy HTML error pages |
|
| `--customer-errors BOOL` | `HAPROXY_CUSTOMERRORS` | `false` | Enable custom HAProxy HTML error pages |
|
||||||
|
|
||||||
### Logging
|
## Logging
|
||||||
|
|
||||||
| Flag | Environment Variable | Default | Description |
|
| Flag | Environment Variable | Default | Description |
|
||||||
|-----------------------------|-------------------------|----------|---------------------------|
|
|-----------------------------|-------------------------|----------|---------------------------|
|
||||||
|
|
@ -99,7 +34,7 @@ easy-haproxy [OPTIONS]
|
||||||
|
|
||||||
Valid levels: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`
|
Valid levels: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`
|
||||||
|
|
||||||
### Stats Dashboard
|
## Stats Dashboard
|
||||||
|
|
||||||
| Flag | Environment Variable | Default | Description |
|
| Flag | Environment Variable | Default | Description |
|
||||||
|--------------------------------------|-----------------------------|--------------|---------------------------------------------|
|
|--------------------------------------|-----------------------------|--------------|---------------------------------------------|
|
||||||
|
|
@ -112,7 +47,7 @@ Valid levels: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`
|
||||||
The stats dashboard is only enabled when `--haproxy-password` (or `HAPROXY_PASSWORD`) is set.
|
The stats dashboard is only enabled when `--haproxy-password` (or `HAPROXY_PASSWORD`) is set.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### ACME / Certbot (SSL certificates)
|
## ACME / Certbot (SSL certificates)
|
||||||
|
|
||||||
| Flag | Environment Variable | Default | Description |
|
| Flag | Environment Variable | Default | Description |
|
||||||
|---------------------------------------|--------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------|--------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
|
@ -125,18 +60,18 @@ The stats dashboard is only enabled when `--haproxy-password` (or `HAPROXY_PASSW
|
||||||
| `--certbot-preferred-challenges TYPE` | `EASYHAPROXY_CERTBOT_PREFERRED_CHALLENGES` | `http` | ACME challenge type |
|
| `--certbot-preferred-challenges TYPE` | `EASYHAPROXY_CERTBOT_PREFERRED_CHALLENGES` | `http` | ACME challenge type |
|
||||||
| `--certbot-manual-auth-hook SCRIPT` | `EASYHAPROXY_CERTBOT_MANUAL_AUTH_HOOK` | *(none)* | Path to a manual auth hook script for certbot |
|
| `--certbot-manual-auth-hook SCRIPT` | `EASYHAPROXY_CERTBOT_MANUAL_AUTH_HOOK` | *(none)* | Path to a manual auth hook script for certbot |
|
||||||
|
|
||||||
See the full [ACME documentation](acme.md) for details.
|
See the full [ACME documentation](../guides/acme.md) for details.
|
||||||
|
|
||||||
### Plugins
|
## Plugins
|
||||||
|
|
||||||
| Flag | Environment Variable | Default | Description |
|
| Flag | Environment Variable | Default | Description |
|
||||||
|---------------------------------|--------------------------------------|----------|-------------------------------------------|
|
|---------------------------------|--------------------------------------|----------|-------------------------------------------|
|
||||||
| `--plugins-enabled LIST` | `EASYHAPROXY_PLUGINS_ENABLED` | *(none)* | Comma-separated list of plugins to enable |
|
| `--plugins-enabled LIST` | `EASYHAPROXY_PLUGINS_ENABLED` | *(none)* | Comma-separated list of plugins to enable |
|
||||||
| `--plugins-abort-on-error BOOL` | `EASYHAPROXY_PLUGINS_ABORT_ON_ERROR` | `false` | Abort startup if a plugin fails to load |
|
| `--plugins-abort-on-error BOOL` | `EASYHAPROXY_PLUGINS_ABORT_ON_ERROR` | `false` | Abort startup if a plugin fails to load |
|
||||||
|
|
||||||
See the [plugins documentation](plugins.md) for available plugins.
|
See the [plugins guide](../guides/plugins.md) for available plugins.
|
||||||
|
|
||||||
### Kubernetes
|
## Kubernetes
|
||||||
|
|
||||||
| Flag | Environment Variable | Default | Description |
|
| Flag | Environment Variable | Default | Description |
|
||||||
|--------------------------------------------|--------------------------------------|----------|----------------------------------------------|
|
|--------------------------------------------|--------------------------------------|----------|----------------------------------------------|
|
||||||
|
|
@ -144,68 +79,3 @@ See the [plugins documentation](plugins.md) for available plugins.
|
||||||
| `--deployment-mode MODE` | `EASYHAPROXY_DEPLOYMENT_MODE` | `auto` | Deployment mode: `auto`, `single`, `cluster` |
|
| `--deployment-mode MODE` | `EASYHAPROXY_DEPLOYMENT_MODE` | `auto` | Deployment mode: `auto`, `single`, `cluster` |
|
||||||
| `--external-hostname HOSTNAME` | `EASYHAPROXY_EXTERNAL_HOSTNAME` | *(none)* | External hostname reported in Ingress status |
|
| `--external-hostname HOSTNAME` | `EASYHAPROXY_EXTERNAL_HOSTNAME` | *(none)* | External hostname reported in Ingress status |
|
||||||
| `--ingress-status-update-interval SECONDS` | `EASYHAPROXY_STATUS_UPDATE_INTERVAL` | `30` | Interval to update Ingress status |
|
| `--ingress-status-update-interval SECONDS` | `EASYHAPROXY_STATUS_UPDATE_INTERVAL` | `30` | Interval to update Ingress status |
|
||||||
|
|
||||||
## Quick-start examples
|
|
||||||
|
|
||||||
### Static mode (bare-metal / VM)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p ~/easyhaproxy/static
|
|
||||||
|
|
||||||
cat > ~/easyhaproxy/static/config.yml <<EOF
|
|
||||||
containers:
|
|
||||||
"myapp.example.com:80":
|
|
||||||
ip: ["127.0.0.1:3000"]
|
|
||||||
EOF
|
|
||||||
|
|
||||||
easy-haproxy --discover static
|
|
||||||
```
|
|
||||||
|
|
||||||
### Static mode with stats and HTTPS redirect
|
|
||||||
|
|
||||||
```bash
|
|
||||||
easy-haproxy \
|
|
||||||
--discover static \
|
|
||||||
--haproxy-password mysecret \
|
|
||||||
--ssl-mode default \
|
|
||||||
--log-level INFO
|
|
||||||
```
|
|
||||||
|
|
||||||
### Let's Encrypt (ACME)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
easy-haproxy \
|
|
||||||
--discover static \
|
|
||||||
--certbot-email admin@example.com \
|
|
||||||
--certbot-autoconfig letsencrypt
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running as a systemd service
|
|
||||||
|
|
||||||
To keep `easy-haproxy` running across reboots, create a systemd unit:
|
|
||||||
|
|
||||||
```ini title="/etc/systemd/system/easy-haproxy.service"
|
|
||||||
[Unit]
|
|
||||||
Description=EasyHAProxy
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=/usr/local/bin/easy-haproxy --discover static --haproxy-password mysecret
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=5
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable --now easy-haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
:::tip Adjust ExecStart path
|
|
||||||
Run `which easy-haproxy` to get the correct binary path for `ExecStart`. If you installed with `uv tool`, it is typically `/root/.local/bin/easy-haproxy` when running as root.
|
|
||||||
:::
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 12
|
sidebar_position: 3
|
||||||
|
sidebar_label: "Container Labels"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Container Labels
|
# Container Labels
|
||||||
|
|
@ -16,7 +17,7 @@ sidebar_position: 12
|
||||||
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key |
|
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key |
|
||||||
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
||||||
| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
||||||
| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false |
|
| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](../guides/acme.md). | false | true OR false |
|
||||||
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
||||||
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use** this with `ssl` or `certbot` parameters | false | true OR false |
|
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use** this with `ssl` or `certbot` parameters | false | true OR false |
|
||||||
| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based |
|
| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based |
|
||||||
|
|
@ -146,5 +147,24 @@ docker run \
|
||||||
--label easyhaproxy.[definition].redirect='{"www.byjg.com.br":"http://byjg.com.br","byjg.com":"http://byjg.com.br"}'
|
--label easyhaproxy.[definition].redirect='{"www.byjg.com.br":"http://byjg.com.br","byjg.com":"http://byjg.com.br"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kubernetes Ingress Annotations
|
||||||
|
|
||||||
|
When using Kubernetes, configure EasyHAProxy behavior with these annotations on your Ingress resources. Annotations apply to **all hosts** in the ingress configuration.
|
||||||
|
|
||||||
|
| Annotation | Description | Default | Example |
|
||||||
|
|-------------------------------------|--------------------------------------------------------------------------------------|------------|-----------------------------|
|
||||||
|
| kubernetes.io/ingress.class | (deprecated) Activate EasyHAProxy. Use `spec.ingressClassName` instead. | *optional* | easyhaproxy-ingress |
|
||||||
|
| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false |
|
||||||
|
| easyhaproxy.certbot | (optional) Boolean. Request certbot certificates for the ingress 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](../guides/plugins.md)) | *varies* | See plugin docs |
|
||||||
|
|
||||||
|
For annotation usage examples, see the [Kubernetes getting started guide](../getting-started/kubernetes.md).
|
||||||
|
|
||||||
----
|
----
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 13
|
sidebar_position: 2
|
||||||
|
sidebar_label: "Environment Variables"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker environment variables
|
# Docker environment variables
|
||||||
|
|
@ -9,7 +10,7 @@ sidebar_position: 13
|
||||||
| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** |
|
| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** |
|
||||||
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` |
|
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` |
|
||||||
| EASYHAPROXY_BASE_PATH | (Optional) Base directory for all EasyHAProxy files. All paths (config, certs, plugins, www) are constructed relative to this base. | `/etc/easyhaproxy` |
|
| EASYHAPROXY_BASE_PATH | (Optional) Base directory for all EasyHAProxy files. All paths (config, certs, plugins, www) are constructed relative to this base. | `/etc/easyhaproxy` |
|
||||||
| EASYHAPROXY_CERTBOT_* | (Optional) Enable Let's Encrypt or any other ACME certificate. See more: [acme](acme.md) | *empty* |
|
| EASYHAPROXY_CERTBOT_* | (Optional) Enable Let's Encrypt or any other ACME certificate. See more: [acme](../guides/acme.md) | *empty* |
|
||||||
| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default` |
|
| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default` |
|
||||||
| EASYHAPROXY_REFRESH_CONF | (Optional) Check for new containers/services every N seconds. | 10 |
|
| EASYHAPROXY_REFRESH_CONF | (Optional) Check for new containers/services every N seconds. | 10 |
|
||||||
| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
||||||
|
|
@ -26,7 +27,22 @@ Statistics are only configured when `HAPROXY_PASSWORD` is set. Without a passwor
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::note ACME/Certbot Environment Variables
|
:::note ACME/Certbot Environment Variables
|
||||||
For ACME/Certbot configuration (Let's Encrypt, ZeroSSL, etc.), see the [ACME documentation](acme.md#environment-variables) for the complete list of `EASYHAPROXY_CERTBOT_*` variables.
|
For ACME/Certbot configuration (Let's Encrypt, ZeroSSL, etc.), see the [ACME documentation](../guides/acme.md#environment-variables) for the complete list of `EASYHAPROXY_CERTBOT_*` variables.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Kubernetes
|
||||||
|
|
||||||
|
These variables apply only when `EASYHAPROXY_DISCOVER=kubernetes`. They control how EasyHAProxy updates Ingress resources with load-balancer IP information.
|
||||||
|
|
||||||
|
| Environment Variable | Description | Default |
|
||||||
|
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|---------|
|
||||||
|
| EASYHAPROXY_UPDATE_INGRESS_STATUS | Update Ingress resources with the load-balancer IP. Set to `false` to disable. | `true` |
|
||||||
|
| EASYHAPROXY_DEPLOYMENT_MODE | How to detect and report Ingress IPs: `auto`, `daemonset`, `nodeport`, or `clusterip`. `auto` inspects pod owner references and service type automatically. | `auto` |
|
||||||
|
| EASYHAPROXY_EXTERNAL_HOSTNAME | Hostname to report in Ingress status when using ClusterIP mode without a cloud LoadBalancer. | *(none)* |
|
||||||
|
| EASYHAPROXY_STATUS_UPDATE_INTERVAL | Seconds between Ingress status update cycles. | `30` |
|
||||||
|
|
||||||
|
:::tip Deployment mode auto-detection
|
||||||
|
`EASYHAPROXY_DEPLOYMENT_MODE=auto` is recommended. EasyHAProxy inspects its own pod owner references (DaemonSet vs Deployment) and Service type (NodePort vs ClusterIP) to determine the correct IP source. Override only if auto-detection gives wrong results.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
----
|
----
|
||||||
115
docs/reference/helm.md
Normal file
115
docs/reference/helm.md
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 6
|
||||||
|
sidebar_label: "Helm Values"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Helm Values Reference
|
||||||
|
|
||||||
|
Complete reference for all configurable values in the `byjg/easyhaproxy` Helm chart.
|
||||||
|
|
||||||
|
## Image Configuration
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `image.repository` | Container image repository | `byjg/easy-haproxy` |
|
||||||
|
| `image.tag` | Image tag. Defaults to the chart `appVersion`. | `""` |
|
||||||
|
| `image.pullPolicy` | Image pull policy | `Always` |
|
||||||
|
| `imagePullSecrets` | List of image pull secret names | `[]` |
|
||||||
|
| `nameOverride` | Override the chart name | `""` |
|
||||||
|
| `fullnameOverride` | Override the full release name | `""` |
|
||||||
|
|
||||||
|
## Deployment Mode
|
||||||
|
|
||||||
|
Controls whether EasyHAProxy runs as a **Deployment** (with a Service) or a **DaemonSet** (binding to host ports).
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `replicaCount` | Number of replicas (only used when `service.create: true`) | `1` |
|
||||||
|
| `service.create` | `true` → Deployment + Service (NodePort or ClusterIP). `false` → DaemonSet with host ports. | `false` |
|
||||||
|
| `service.type` | Service type when `service.create: true`: `NodePort` or `ClusterIP` | `ClusterIP` |
|
||||||
|
| `service.annotations` | Annotations to add to the Service resource | `{}` |
|
||||||
|
|
||||||
|
:::tip Recommended mode
|
||||||
|
Set `service.create: true` and `service.type: NodePort` for most setups. DaemonSet mode (`service.create: false`) requires maintaining a node label across node replacements, which can cause outages if forgotten.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Port Binding
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `binding.ports.http` | HTTP port | `80` |
|
||||||
|
| `binding.ports.https` | HTTPS port | `443` |
|
||||||
|
| `binding.ports.stats` | HAProxy stats port | `1936` |
|
||||||
|
| `binding.additionalPorts` | List of additional ports to expose | `[]` |
|
||||||
|
|
||||||
|
## EasyHAProxy Settings
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `easyhaproxy.stats.username` | HAProxy stats dashboard username | `admin` |
|
||||||
|
| `easyhaproxy.stats.password` | HAProxy stats dashboard password | `password` |
|
||||||
|
| `easyhaproxy.refresh` | Seconds between service discovery polls | `"10"` |
|
||||||
|
| `easyhaproxy.customErrors` | Enable custom HTML error pages | `"true"` |
|
||||||
|
| `easyhaproxy.sslMode` | TLS mode: `strict`, `default`, or `loose` | `loose` |
|
||||||
|
| `easyhaproxy.logLevel.certbot` | Log level for Certbot | `DEBUG` |
|
||||||
|
| `easyhaproxy.logLevel.easyhaproxy` | Log level for EasyHAProxy | `DEBUG` |
|
||||||
|
| `easyhaproxy.logLevel.haproxy` | Log level for HAProxy | `DEBUG` |
|
||||||
|
| `easyhaproxy.certbot.email` | Email address for Let's Encrypt / ACME registration | `""` |
|
||||||
|
|
||||||
|
## Ingress Status
|
||||||
|
|
||||||
|
Controls how EasyHAProxy updates Kubernetes Ingress resources with load-balancer IPs.
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `ingressStatus.enabled` | Update Ingress resources with load-balancer IPs | `true` |
|
||||||
|
| `ingressStatus.deploymentMode` | How to detect/report IPs: `auto`, `daemonset`, `nodeport`, or `clusterip`. `auto` is recommended. | `auto` |
|
||||||
|
| `ingressStatus.externalHostname` | Hostname to report in Ingress status (for ClusterIP mode without a LoadBalancer) | `""` |
|
||||||
|
| `ingressStatus.updateInterval` | Seconds between Ingress status updates | `30` |
|
||||||
|
|
||||||
|
## DaemonSet Node Selection
|
||||||
|
|
||||||
|
:::note Only applies when `service.create: false`
|
||||||
|
These values are ignored when running as a Deployment (`service.create: true`).
|
||||||
|
:::
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `masterNode.label` | Node label key used for `nodeAffinity` | `easyhaproxy/node` |
|
||||||
|
| `masterNode.values` | Accepted values for the node label | `["master"]` |
|
||||||
|
|
||||||
|
Label the target node before installing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl label nodes node-01 "easyhaproxy/node=master"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Standard Kubernetes Fields
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `podAnnotations` | Annotations added to the EasyHAProxy pod | `{}` |
|
||||||
|
| `resources` | CPU/memory requests and limits for the pod | `{}` |
|
||||||
|
| `nodeSelector` | Node selector for pod scheduling | `{}` |
|
||||||
|
| `tolerations` | Tolerations for pod scheduling | `[]` |
|
||||||
|
| `affinity` | Affinity rules for pod scheduling | `{}` |
|
||||||
|
| `podSecurityContext` | Pod-level security context | `{}` |
|
||||||
|
| `securityContext` | Container-level security context | `{}` |
|
||||||
|
|
||||||
|
## Service Account
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `serviceAccount.create` | Create a dedicated ServiceAccount | `true` |
|
||||||
|
| `serviceAccount.annotations` | Annotations to add to the ServiceAccount | `{}` |
|
||||||
|
| `serviceAccount.name` | Name of the ServiceAccount. Auto-generated if empty. | `""` |
|
||||||
|
|
||||||
|
## Ingress Class
|
||||||
|
|
||||||
|
| Value | Description | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `ingressClass.create` | Create an `IngressClass` resource named `easyhaproxy` | `true` |
|
||||||
|
| `ingressClass.annotations` | Annotations to add to the IngressClass | `{}` |
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 22
|
sidebar_position: 5
|
||||||
|
sidebar_label: "Other Configurations"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Other configurations
|
# Other configurations
|
||||||
4
docs/reference/plugins/_category_.json
Normal file
4
docs/reference/plugins/_category_.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"label": "Plugins",
|
||||||
|
"position": 5
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 21
|
sidebar_position: 6
|
||||||
|
sidebar_label: "Cleanup"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Cleanup Plugin
|
# Cleanup Plugin
|
||||||
|
|
@ -39,8 +40,6 @@ plugins:
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
Configure the Cleanup plugin globally:
|
|
||||||
|
|
||||||
| Environment Variable | Config Key | Type | Default | Description |
|
| Environment Variable | Config Key | Type | Default | Description |
|
||||||
|-------------------------------------------------|----------------------|----------|---------|----------------------------------------------|
|
|-------------------------------------------------|----------------------|----------|---------|----------------------------------------------|
|
||||||
| `EASYHAPROXY_PLUGINS_ENABLED` | - | string | - | Enable cleanup plugin (value: `cleanup`) |
|
| `EASYHAPROXY_PLUGINS_ENABLED` | - | string | - | Enable cleanup plugin (value: `cleanup`) |
|
||||||
|
|
@ -48,12 +47,9 @@ Configure the Cleanup plugin globally:
|
||||||
| `EASYHAPROXY_PLUGIN_CLEANUP_MAX_IDLE_TIME` | `max_idle_time` | integer | `300` | Maximum age in seconds before deleting files |
|
| `EASYHAPROXY_PLUGIN_CLEANUP_MAX_IDLE_TIME` | `max_idle_time` | integer | `300` | Maximum age in seconds before deleting files |
|
||||||
| `EASYHAPROXY_PLUGIN_CLEANUP_CLEANUP_TEMP_FILES` | `cleanup_temp_files` | boolean | `true` | Enable temp file cleanup |
|
| `EASYHAPROXY_PLUGIN_CLEANUP_CLEANUP_TEMP_FILES` | `cleanup_temp_files` | boolean | `true` | Enable temp file cleanup |
|
||||||
|
|
||||||
**Note:** This is a global plugin - configuration applies to the entire system.
|
|
||||||
|
|
||||||
### Custom Idle Time (1 hour)
|
### Custom Idle Time (1 hour)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# /etc/easyhaproxy/static/config.yaml
|
|
||||||
plugins:
|
plugins:
|
||||||
enabled: [cleanup]
|
enabled: [cleanup]
|
||||||
config:
|
config:
|
||||||
|
|
@ -79,6 +75,6 @@ The cleanup plugin:
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
- [Plugin System Overview](../plugins.md)
|
- [Plugin System Overview](../../guides/plugins.md)
|
||||||
- [Environment Variables Reference](../environment-variable.md)
|
- [Environment Variables Reference](../environment-variables.md)
|
||||||
- [Static Configuration Reference](../static.md)
|
- [Static Configuration Reference](../../getting-started/static.md)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 18
|
sidebar_position: 3
|
||||||
|
sidebar_label: "Cloudflare"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Cloudflare Plugin
|
# Cloudflare Plugin
|
||||||
|
|
@ -38,8 +39,6 @@ services:
|
||||||
|
|
||||||
### Docker/Docker Compose (Custom IP List)
|
### Docker/Docker Compose (Custom IP List)
|
||||||
|
|
||||||
If you want to use your own IP list file instead of the built-in ranges:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
easyhaproxy.http.plugins: cloudflare
|
easyhaproxy.http.plugins: cloudflare
|
||||||
|
|
@ -72,7 +71,6 @@ spec:
|
||||||
### Static YAML Configuration
|
### Static YAML Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# /etc/easyhaproxy/static/config.yaml
|
|
||||||
plugins:
|
plugins:
|
||||||
config:
|
config:
|
||||||
cloudflare:
|
cloudflare:
|
||||||
|
|
@ -82,16 +80,12 @@ plugins:
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
Configure Cloudflare plugin defaults for all domains:
|
|
||||||
|
|
||||||
| Environment Variable | Config Key | Type | Default | Description |
|
| Environment Variable | Config Key | Type | Default | Description |
|
||||||
|-------------------------------------------------|-------------------|----------|---------------------------------------|---------------------------------------|
|
|-------------------------------------------------|-------------------|----------|---------------------------------------|---------------------------------------|
|
||||||
| `EASYHAPROXY_PLUGIN_CLOUDFLARE_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
| `EASYHAPROXY_PLUGIN_CLOUDFLARE_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
||||||
| `EASYHAPROXY_PLUGIN_CLOUDFLARE_USE_BUILTIN_IPS` | `use_builtin_ips` | boolean | `true` | Use built-in Cloudflare IP ranges |
|
| `EASYHAPROXY_PLUGIN_CLOUDFLARE_USE_BUILTIN_IPS` | `use_builtin_ips` | boolean | `true` | Use built-in Cloudflare IP ranges |
|
||||||
| `EASYHAPROXY_PLUGIN_CLOUDFLARE_IP_LIST_PATH` | `ip_list_path` | string | `/etc/easyhaproxy/cloudflare_ips.lst` | Path to Cloudflare IP list file |
|
| `EASYHAPROXY_PLUGIN_CLOUDFLARE_IP_LIST_PATH` | `ip_list_path` | string | `/etc/easyhaproxy/cloudflare_ips.lst` | Path to Cloudflare IP list file |
|
||||||
|
|
||||||
**Note:** Environment variables set defaults for ALL domains. To enable/disable per-domain, use container labels or Kubernetes annotations.
|
|
||||||
|
|
||||||
## Generated HAProxy Configuration
|
## Generated HAProxy Configuration
|
||||||
|
|
||||||
```haproxy
|
```haproxy
|
||||||
|
|
@ -114,16 +108,13 @@ The plugin includes the current Cloudflare IP ranges (22 ranges total):
|
||||||
- 2400:cb00::/32, 2606:4700::/32, 2803:f800::/32, 2405:b500::/32
|
- 2400:cb00::/32, 2606:4700::/32, 2803:f800::/32, 2405:b500::/32
|
||||||
- 2405:8100::/32, 2a06:98c0::/29, 2c0f:f248::/32
|
- 2405:8100::/32, 2a06:98c0::/29, 2c0f:f248::/32
|
||||||
|
|
||||||
These ranges are automatically written to `/etc/easyhaproxy/cloudflare_ips.lst` during each discovery cycle.
|
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
- ✅ **No manual configuration required** - Built-in Cloudflare IPs are included!
|
- ✅ **No manual configuration required** - Built-in Cloudflare IPs are included!
|
||||||
- The plugin runs once per domain during the discovery cycle
|
- The plugin runs once per domain during the discovery cycle
|
||||||
- IP list file is automatically created and updated
|
- IP list file is automatically created and updated
|
||||||
- To update Cloudflare IPs in the future, simply update the plugin source code and rebuild
|
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
- [Plugin System Overview](../plugins.md)
|
- [Plugin System Overview](../../guides/plugins.md)
|
||||||
- [Container Labels Reference](../container-labels.md)
|
- [Container Labels Reference](../container-labels.md)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 20
|
sidebar_position: 5
|
||||||
|
sidebar_label: "Deny Pages"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Deny Pages Plugin
|
# Deny Pages Plugin
|
||||||
|
|
@ -73,7 +74,6 @@ spec:
|
||||||
### Static YAML Configuration
|
### Static YAML Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# /etc/easyhaproxy/static/config.yaml
|
|
||||||
containers:
|
containers:
|
||||||
"example.com:80":
|
"example.com:80":
|
||||||
ip: ["webapp:80"]
|
ip: ["webapp:80"]
|
||||||
|
|
@ -96,16 +96,12 @@ labels:
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
Configure Deny Pages plugin defaults for all domains:
|
|
||||||
|
|
||||||
| Environment Variable | Config Key | Type | Default | Description |
|
| Environment Variable | Config Key | Type | Default | Description |
|
||||||
|---------------------------------------------|---------------|---------|---------|----------------------------------------|
|
|---------------------------------------------|---------------|---------|---------|----------------------------------------|
|
||||||
| `EASYHAPROXY_PLUGIN_DENY_PAGES_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
| `EASYHAPROXY_PLUGIN_DENY_PAGES_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
||||||
| `EASYHAPROXY_PLUGIN_DENY_PAGES_PATHS` | `paths` | string | - | Comma-separated list of paths to block |
|
| `EASYHAPROXY_PLUGIN_DENY_PAGES_PATHS` | `paths` | string | - | Comma-separated list of paths to block |
|
||||||
| `EASYHAPROXY_PLUGIN_DENY_PAGES_STATUS_CODE` | `status_code` | integer | `403` | HTTP status code to return |
|
| `EASYHAPROXY_PLUGIN_DENY_PAGES_STATUS_CODE` | `status_code` | integer | `403` | HTTP status code to return |
|
||||||
|
|
||||||
**Note:** Environment variables set defaults for ALL domains. To configure per-domain, use container labels or Kubernetes annotations.
|
|
||||||
|
|
||||||
## Generated HAProxy Configuration
|
## Generated HAProxy Configuration
|
||||||
|
|
||||||
```haproxy
|
```haproxy
|
||||||
|
|
@ -123,5 +119,5 @@ http-request deny deny_status 404 if denied_path
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
- [Plugin System Overview](../plugins.md)
|
- [Plugin System Overview](../../guides/plugins.md)
|
||||||
- [Container Labels Reference](../container-labels.md)
|
- [Container Labels Reference](../container-labels.md)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 17
|
sidebar_position: 2
|
||||||
|
sidebar_label: "FastCGI"
|
||||||
---
|
---
|
||||||
|
|
||||||
# FastCGI Plugin
|
# FastCGI Plugin
|
||||||
|
|
@ -64,16 +65,6 @@ services:
|
||||||
- /run/php:/run/php
|
- /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
|
### Kubernetes Annotations
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
@ -117,22 +108,16 @@ easymapping:
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
Configure FastCGI plugin defaults for all domains:
|
|
||||||
|
|
||||||
| Environment Variable | Config Key | Type | Default | Description |
|
| Environment Variable | Config Key | Type | Default | Description |
|
||||||
|----------------------------------------------|-------------------|----------|-----------------|---------------------------------------|
|
|----------------------------------------------|-------------------|----------|------------------------|---------------------------------------|
|
||||||
| `EASYHAPROXY_PLUGIN_FASTCGI_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
| `EASYHAPROXY_PLUGIN_FASTCGI_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
||||||
| `EASYHAPROXY_PLUGIN_FASTCGI_DOCUMENT_ROOT` | `document_root` | string | `/etc/easyhaproxy/www` | Document root path |
|
| `EASYHAPROXY_PLUGIN_FASTCGI_DOCUMENT_ROOT` | `document_root` | string | `/etc/easyhaproxy/www` | Document root path |
|
||||||
| `EASYHAPROXY_PLUGIN_FASTCGI_SCRIPT_FILENAME` | `script_filename` | string | `%[path]` | Custom pattern for SCRIPT_FILENAME |
|
| `EASYHAPROXY_PLUGIN_FASTCGI_SCRIPT_FILENAME` | `script_filename` | string | `%[path]` | Custom pattern for SCRIPT_FILENAME |
|
||||||
| `EASYHAPROXY_PLUGIN_FASTCGI_INDEX_FILE` | `index_file` | string | `index.php` | Default index file |
|
| `EASYHAPROXY_PLUGIN_FASTCGI_INDEX_FILE` | `index_file` | string | `index.php` | Default index file |
|
||||||
| `EASYHAPROXY_PLUGIN_FASTCGI_PATH_INFO` | `path_info` | boolean | `true` | Enable PATH_INFO support |
|
| `EASYHAPROXY_PLUGIN_FASTCGI_PATH_INFO` | `path_info` | boolean | `true` | Enable PATH_INFO support |
|
||||||
|
|
||||||
**Note:** Environment variables set defaults for ALL domains. To configure per-domain, use container labels or Kubernetes annotations. Custom params (`custom_params`) cannot be configured via environment variables - use YAML or labels instead.
|
|
||||||
|
|
||||||
## Generated HAProxy Configuration
|
## Generated HAProxy Configuration
|
||||||
|
|
||||||
The plugin generates a top-level `fcgi-app` section and a `use-fcgi-app` directive in the backend:
|
|
||||||
|
|
||||||
```haproxy
|
```haproxy
|
||||||
# Top-level fcgi-app definition (added after defaults, before frontends/backends)
|
# Top-level fcgi-app definition (added after defaults, before frontends/backends)
|
||||||
fcgi-app fcgi_phpapp_local
|
fcgi-app fcgi_phpapp_local
|
||||||
|
|
@ -143,16 +128,11 @@ fcgi-app fcgi_phpapp_local
|
||||||
# Backend configuration (added to the backend section)
|
# Backend configuration (added to the backend section)
|
||||||
backend srv_phpapp_local_80
|
backend srv_phpapp_local_80
|
||||||
use-fcgi-app fcgi_phpapp_local
|
use-fcgi-app fcgi_phpapp_local
|
||||||
# TCP connection:
|
|
||||||
server srv-0 172.19.0.3:9000 proto fcgi
|
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
|
## 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:
|
The plugin configures:
|
||||||
- ✅ **SCRIPT_FILENAME** - Path to PHP script
|
- ✅ **SCRIPT_FILENAME** - Path to PHP script
|
||||||
- ✅ **DOCUMENT_ROOT** - Document root directory
|
- ✅ **DOCUMENT_ROOT** - Document root directory
|
||||||
|
|
@ -169,9 +149,8 @@ The plugin configures:
|
||||||
|
|
||||||
- **Required:** Use this plugin together with `proto: fcgi` parameter for complete PHP-FPM support
|
- **Required:** Use this plugin together with `proto: fcgi` parameter for complete PHP-FPM support
|
||||||
- The plugin runs once per domain during the discovery cycle
|
- The plugin runs once per domain during the discovery cycle
|
||||||
- HAProxy handles the actual FastCGI protocol communication and CGI parameter transmission
|
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
- [Plugin System Overview](../plugins.md)
|
- [Plugin System Overview](../../guides/plugins.md)
|
||||||
- [Container Labels Reference](../container-labels.md)
|
- [Container Labels Reference](../container-labels.md)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 19
|
sidebar_position: 4
|
||||||
|
sidebar_label: "IP Whitelist"
|
||||||
---
|
---
|
||||||
|
|
||||||
# IP Whitelist Plugin
|
# IP Whitelist Plugin
|
||||||
|
|
@ -72,7 +73,6 @@ spec:
|
||||||
### Static YAML Configuration
|
### Static YAML Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# /etc/easyhaproxy/static/config.yaml
|
|
||||||
easymapping:
|
easymapping:
|
||||||
- host: admin.example.com
|
- host: admin.example.com
|
||||||
port: 443
|
port: 443
|
||||||
|
|
@ -87,16 +87,12 @@ easymapping:
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
Configure IP Whitelist plugin defaults for all domains:
|
|
||||||
|
|
||||||
| Environment Variable | Config Key | Type | Default | Description |
|
| Environment Variable | Config Key | Type | Default | Description |
|
||||||
|-----------------------------------------------|---------------|----------|---------|--------------------------------------------------|
|
|-----------------------------------------------|---------------|----------|---------|--------------------------------------------------|
|
||||||
| `EASYHAPROXY_PLUGIN_IP_WHITELIST_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
| `EASYHAPROXY_PLUGIN_IP_WHITELIST_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
||||||
| `EASYHAPROXY_PLUGIN_IP_WHITELIST_ALLOWED_IPS` | `allowed_ips` | string | - | Comma-separated list of IPs/CIDR ranges to allow |
|
| `EASYHAPROXY_PLUGIN_IP_WHITELIST_ALLOWED_IPS` | `allowed_ips` | string | - | Comma-separated list of IPs/CIDR ranges to allow |
|
||||||
| `EASYHAPROXY_PLUGIN_IP_WHITELIST_STATUS_CODE` | `status_code` | integer | `403` | HTTP status code to return for blocked IPs |
|
| `EASYHAPROXY_PLUGIN_IP_WHITELIST_STATUS_CODE` | `status_code` | integer | `403` | HTTP status code to return for blocked IPs |
|
||||||
|
|
||||||
**Note:** Environment variables set defaults for ALL domains. To configure per-domain, use container labels or Kubernetes annotations.
|
|
||||||
|
|
||||||
## Generated HAProxy Configuration
|
## Generated HAProxy Configuration
|
||||||
|
|
||||||
```haproxy
|
```haproxy
|
||||||
|
|
@ -118,9 +114,8 @@ The plugin supports:
|
||||||
- The plugin runs once per domain during the discovery cycle
|
- The plugin runs once per domain during the discovery cycle
|
||||||
- Test thoroughly before deploying to production
|
- Test thoroughly before deploying to production
|
||||||
- Consider using VPN CIDR ranges for remote access
|
- Consider using VPN CIDR ranges for remote access
|
||||||
- Works well with staging and admin environments
|
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
- [Plugin System Overview](../plugins.md)
|
- [Plugin System Overview](../../guides/plugins.md)
|
||||||
- [Container Labels Reference](../container-labels.md)
|
- [Container Labels Reference](../container-labels.md)
|
||||||
196
docs/reference/plugins/jwt-validator.md
Normal file
196
docs/reference/plugins/jwt-validator.md
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
sidebar_label: "JWT Validator"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
## Generating JWT Keys
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate RSA key pair (idempotent - skips if exists)
|
||||||
|
[ -f jwt_private.pem ] || openssl genrsa -out jwt_private.pem 2048
|
||||||
|
[ -f jwt_pubkey.pem ] || openssl rsa -in jwt_private.pem -pubout -out jwt_pubkey.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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 (priority 1: explicit file path) | (optional) |
|
||||||
|
| `pubkey` | Public key content as base64-encoded string (priority 2: inline content) | (optional) |
|
||||||
|
| `k8s_secret.pubkey` | Kubernetes secret containing public key (priority 3: Kubernetes only) | (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` |
|
||||||
|
| `allow_anonymous` | If `true`, allows requests without Authorization header (validates JWT if present) | `false` |
|
||||||
|
|
||||||
|
### Public Key Configuration Priority
|
||||||
|
|
||||||
|
When multiple public key options are configured, they are evaluated in this order:
|
||||||
|
1. **`pubkey_path`** - Direct file path (explicit configuration)
|
||||||
|
2. **`pubkey`** - Base64-encoded key content (inline configuration)
|
||||||
|
3. **`k8s_secret.pubkey`** - Kubernetes secret (recommended for Kubernetes deployments)
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Anonymous Access Logic
|
||||||
|
|
||||||
|
- **`allow_anonymous=false` (default):** Requests without `Authorization` header are denied
|
||||||
|
- **`allow_anonymous=true`:** Requests without `Authorization` header are allowed to pass through, but JWTs are validated if the header is present
|
||||||
|
|
||||||
|
## 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/easyhaproxy/jwt_keys/api_pubkey.pem
|
||||||
|
volumes:
|
||||||
|
- ./pubkey.pem:/etc/easyhaproxy/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/easyhaproxy/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/easyhaproxy/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
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kubernetes with Secrets (Recommended)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: jwt-pubkey-secret
|
||||||
|
namespace: production
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
pubkey: |
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: api-ingress
|
||||||
|
namespace: production
|
||||||
|
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.k8s_secret.pubkey: "jwt-pubkey-secret"
|
||||||
|
easyhaproxy.plugin.jwt_validator.paths: "/api/admin,/api/users"
|
||||||
|
easyhaproxy.plugin.jwt_validator.only_paths: "false"
|
||||||
|
spec:
|
||||||
|
ingressClassName: easyhaproxy
|
||||||
|
rules:
|
||||||
|
- host: api.example.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: api-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static YAML Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# /etc/easyhaproxy/static/config.yaml
|
||||||
|
containers:
|
||||||
|
"api.example.com:443":
|
||||||
|
ip: ["api-service:8080"]
|
||||||
|
ssl: true
|
||||||
|
plugins: [jwt_validator]
|
||||||
|
plugin:
|
||||||
|
jwt_validator:
|
||||||
|
algorithm: RS256
|
||||||
|
issuer: https://auth.example.com/
|
||||||
|
audience: https://api.example.com
|
||||||
|
pubkey_path: /etc/easyhaproxy/jwt_keys/api_pubkey.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Environment Variable | Config Key | Type | Default | Description |
|
||||||
|
|----------------------------------------------------|-------------------|---------|---------|---------------------------------------------|
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ENABLED` | `enabled` | boolean | `true` | Enable/disable plugin for all domains |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ALGORITHM` | `algorithm` | string | `RS256` | JWT signing algorithm |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ISSUER` | `issuer` | string | - | Expected JWT issuer (optional) |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_AUDIENCE` | `audience` | string | - | Expected JWT audience (optional) |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_PUBKEY_PATH` | `pubkey_path` | string | - | Path to public key file |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_PUBKEY` | `pubkey` | string | - | Public key as base64-encoded string |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_PATHS` | `paths` | string | - | Comma-separated paths requiring JWT |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ONLY_PATHS` | `only_paths` | boolean | `false` | If true, only specified paths accessible |
|
||||||
|
| `EASYHAPROXY_PLUGIN_JWT_VALIDATOR_ALLOW_ANONYMOUS` | `allow_anonymous` | boolean | `false` | Allow requests without Authorization header |
|
||||||
|
|
||||||
|
## 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](../../guides/plugins.md)
|
||||||
|
- [Container Labels Reference](../container-labels.md)
|
||||||
|
- [Kubernetes Secrets](../../getting-started/kubernetes.md#loading-plugin-configuration-from-kubernetes-secrets)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 14
|
sidebar_position: 4
|
||||||
|
sidebar_label: "Volumes"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Volumes
|
# Volumes
|
||||||
|
|
@ -79,15 +80,15 @@ The most commonly mapped volumes for persistence and customization:
|
||||||
|
|
||||||
| Volume | Purpose | Required |
|
| Volume | Purpose | Required |
|
||||||
|-------------------------------------------|-------------------------------------------------------------------------------------------------------|----------|
|
|-------------------------------------------|-------------------------------------------------------------------------------------------------------|----------|
|
||||||
| `/etc/easyhaproxy/static/` | [Static configuration](static.md) - mount your `config.yml` here | Optional |
|
| `/etc/easyhaproxy/static/` | [Static configuration](../getting-started/static.md) - mount your `config.yml` here | Optional |
|
||||||
| `/etc/easyhaproxy/certs/haproxy/` | [SSL certificates](ssl.md) - user-provided certificates in PEM format | Optional |
|
| `/etc/easyhaproxy/certs/haproxy/` | [SSL certificates](../guides/ssl.md) - user-provided certificates in PEM format | Optional |
|
||||||
| `/etc/easyhaproxy/certs/certbot/` | [ACME/Certbot certificates](acme.md) - auto-generated Let's Encrypt certificates | Optional |
|
| `/etc/easyhaproxy/certs/certbot/` | [ACME/Certbot certificates](../guides/acme.md) - auto-generated Let's Encrypt certificates | Optional |
|
||||||
| `/etc/easyhaproxy/certs/live/` | Certbot live certificates - persist across container restarts | Optional |
|
| `/etc/easyhaproxy/certs/live/` | Certbot live certificates - persist across container restarts | Optional |
|
||||||
| `/etc/easyhaproxy/haproxy/conf.d/` | [Custom HAProxy config](other.md) - additional `.cfg` files to include | Optional |
|
| `/etc/easyhaproxy/haproxy/conf.d/` | [Custom HAProxy config](other.md) - additional `.cfg` files to include | Optional |
|
||||||
| `/etc/easyhaproxy/haproxy/errors-custom/` | [Custom error pages](other.md) - custom HTTP error pages (400, 403, 500, etc.) | Optional |
|
| `/etc/easyhaproxy/haproxy/errors-custom/` | [Custom error pages](other.md) - custom HTTP error pages (400, 403, 500, etc.) | Optional |
|
||||||
| `/etc/easyhaproxy/plugins/` | [Custom plugins](plugins.md) - Python plugin files | Optional |
|
| `/etc/easyhaproxy/plugins/` | [Custom plugins](../guides/plugins.md) - Python plugin files | Optional |
|
||||||
| `/etc/easyhaproxy/jwt_keys/` | [JWT public keys](Plugins/jwt-validator.md) - RSA public keys for JWT validation | Optional |
|
| `/etc/easyhaproxy/jwt_keys/` | [JWT public keys](plugins/jwt-validator.md) - RSA public keys for JWT validation | Optional |
|
||||||
| `/etc/easyhaproxy/www/` | [FastCGI document root](Plugins/fastcgi.md) - PHP/FastCGI application files | Optional |
|
| `/etc/easyhaproxy/www/` | [FastCGI document root](plugins/fastcgi.md) - PHP/FastCGI application files | Optional |
|
||||||
|
|
||||||
## Directory Details
|
## Directory Details
|
||||||
|
|
||||||
|
|
@ -165,19 +166,19 @@ volumes:
|
||||||
```
|
```
|
||||||
Add custom Python plugins to extend EasyHAProxy functionality.
|
Add custom Python plugins to extend EasyHAProxy functionality.
|
||||||
|
|
||||||
See [Plugin Development](plugin-development.md) for details.
|
See [Plugin Development](../guides/plugin-development.md) for details.
|
||||||
|
|
||||||
#### JWT Public Keys
|
#### JWT Public Keys
|
||||||
```bash
|
```bash
|
||||||
/etc/easyhaproxy/jwt_keys/*.pem
|
/etc/easyhaproxy/jwt_keys/*.pem
|
||||||
```
|
```
|
||||||
RSA public keys for [JWT token validation](Plugins/jwt-validator.md).
|
RSA public keys for [JWT token validation](plugins/jwt-validator.md).
|
||||||
|
|
||||||
#### Cloudflare IP Ranges
|
#### Cloudflare IP Ranges
|
||||||
```bash
|
```bash
|
||||||
/etc/easyhaproxy/cloudflare_ips.lst
|
/etc/easyhaproxy/cloudflare_ips.lst
|
||||||
```
|
```
|
||||||
Cloudflare IP ranges for the [Cloudflare plugin](Plugins/cloudflare.md) to restore real client IPs.
|
Cloudflare IP ranges for the [Cloudflare plugin](plugins/cloudflare.md) to restore real client IPs.
|
||||||
|
|
||||||
### Error Pages
|
### Error Pages
|
||||||
|
|
||||||
140
docs/static.md
140
docs/static.md
|
|
@ -1,140 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 4
|
|
||||||
---
|
|
||||||
|
|
||||||
# Static File
|
|
||||||
|
|
||||||
## Setup Docker EasyHAProxy
|
|
||||||
|
|
||||||
This method will use a static configuration, which is simpler and easier than creating a `haproxy.cfg`
|
|
||||||
|
|
||||||
You can use this configuration to set up external servers unrelated to docker or Kubernetes.
|
|
||||||
|
|
||||||
:::tip Live Reload
|
|
||||||
EasyHAProxy monitors this file for changes and automatically reconfigures HAProxy when any changes are detected.
|
|
||||||
:::
|
|
||||||
|
|
||||||
First, create a YAML configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
stats:
|
|
||||||
username: admin
|
|
||||||
password: password
|
|
||||||
port: 1936 # Optional (default 1936)
|
|
||||||
|
|
||||||
customerrors: true # Optional (default false)
|
|
||||||
|
|
||||||
ssl_mode: default
|
|
||||||
|
|
||||||
logLevel:
|
|
||||||
haproxy: INFO
|
|
||||||
|
|
||||||
certbot:
|
|
||||||
email: "acme@example.org"
|
|
||||||
|
|
||||||
containers:
|
|
||||||
# HTTP with certbot + redirect to HTTPS
|
|
||||||
"host1.com.br:80":
|
|
||||||
ip: ["container:5000"]
|
|
||||||
certbot: true
|
|
||||||
redirect_ssl: true
|
|
||||||
|
|
||||||
# Additional HTTP host
|
|
||||||
"host2.com.br:80":
|
|
||||||
ip: ["other:3000"]
|
|
||||||
|
|
||||||
# Redirect www → main domain
|
|
||||||
"www.host1.com.br:80":
|
|
||||||
ip: ["container:5000"]
|
|
||||||
redirect_ssl: true
|
|
||||||
|
|
||||||
# HTTPS version
|
|
||||||
"host1.com.br:443":
|
|
||||||
ip: ["container:80"]
|
|
||||||
ssl: true
|
|
||||||
|
|
||||||
# Different host on different port
|
|
||||||
"host3.com.br:8080":
|
|
||||||
ip: ["domain:8181"]
|
|
||||||
```
|
|
||||||
|
|
||||||
:::info New Configuration Format
|
|
||||||
The `containers` format simplifies static configuration:
|
|
||||||
- **Flatter structure**: `"hostname:port"` keys instead of nested `easymapping` → `ports` → `hosts`
|
|
||||||
- **Better readability**: Port and localport embedded in keys (`"host:port"` and `"container:localport"`)
|
|
||||||
- **Plugin support**: Global and per-host plugin configuration
|
|
||||||
- **Clearer mapping**: Format mirrors internal Docker label structure
|
|
||||||
:::
|
|
||||||
|
|
||||||
Then map this file to `/etc/easyhaproxy/static/config.yml` in your EasyHAProxy container:
|
|
||||||
|
|
||||||
```bash title="Run EasyHAProxy with static configuration"
|
|
||||||
docker run -d \
|
|
||||||
--name easy-haproxy-container \
|
|
||||||
-v /my/static/:/etc/easyhaproxy/static/ \
|
|
||||||
-e EASYHAPROXY_DISCOVER="static" \
|
|
||||||
# + Environment Variables \
|
|
||||||
-p 80:80 \
|
|
||||||
-p 443:443 \
|
|
||||||
-p 1936:1936 \
|
|
||||||
--network easyhaproxy \
|
|
||||||
byjg/easy-haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
:::tip Docker Socket Optional
|
|
||||||
Mounting `/var/run/docker.sock` is not required in static discovery mode. Add it only if you are simultaneously discovering Docker containers.
|
|
||||||
:::
|
|
||||||
|
|
||||||
You can find other information on [docker label configuration](container-labels.md) and [environment variable guide](environment-variable.md)
|
|
||||||
|
|
||||||
## Yaml Definition
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
stats:
|
|
||||||
username: admin # Optional (default "admin")
|
|
||||||
password: password # If stats or stats.password is omitted, stats will be public with no password
|
|
||||||
port: 1936 # Optional (default 1936)
|
|
||||||
|
|
||||||
customerrors: true # Optional (default false)
|
|
||||||
|
|
||||||
ssl_mode: default # Optional
|
|
||||||
|
|
||||||
logLevel:
|
|
||||||
certbot: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
|
||||||
easyhaproxy: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
|
||||||
haproxy: INFO # Optional (default: INFO). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
|
||||||
|
|
||||||
certbot:
|
|
||||||
email: "acme@example.org" # If email is defined enable ACME/Certbot
|
|
||||||
autoconfig: "" # If empty use letsencrypt, otherwise try to set the CA defined.
|
|
||||||
eab_hmac_key: "" # If required by the CA, set here.
|
|
||||||
eab_kid: "" # If required by the CA, set here.
|
|
||||||
server: False # If empty/False uses Letsencrypt, otherwise the CA Endpoint defined here
|
|
||||||
retry_count: 60 # If the certificate reaches the Rate Limit, try again after 'n' iterations.
|
|
||||||
}
|
|
||||||
|
|
||||||
containers:
|
|
||||||
# Format: "hostname:port"
|
|
||||||
"host1.com.br:80":
|
|
||||||
ip: ["container:5000"] # Endpoints (ip, dns, container, etc) with format "address:localport"
|
|
||||||
certbot: true # Optional. Request a certbot certificate. Requires certbot.email set.
|
|
||||||
redirect_ssl: true # Optional. Redirect HTTP to HTTPS for this host.
|
|
||||||
mode: http # Optional. Default `http`. Can be http or tcp
|
|
||||||
|
|
||||||
# HTTPS version (SSL)
|
|
||||||
"host1.com.br:443":
|
|
||||||
ip: ["container:80"]
|
|
||||||
ssl: true # Enable SSL for this port
|
|
||||||
|
|
||||||
# Redirect www → main domain (using redirect_ssl with backend)
|
|
||||||
"www.host1.com.br:80":
|
|
||||||
ip: ["container:5000"]
|
|
||||||
redirect_ssl: true
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note SSL Certificates in Static Mode
|
|
||||||
The only way to provide SSL certificates in static configuration mode is to map the certificates to EasyHAProxy as a docker volume. Refer to the [SSL documentation](ssl.md) to learn how to configure this.
|
|
||||||
:::
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
109
docs/swarm.md
109
docs/swarm.md
|
|
@ -1,109 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
# Swarm
|
|
||||||
|
|
||||||
## Setup Docker EasyHAProxy
|
|
||||||
|
|
||||||
This method involves using a Docker Swarm installation to discover containers and configure HAProxy.
|
|
||||||
|
|
||||||
EasyHAProxy inspects Docker containers within the Swarm and retrieves labels to configure HAProxy. Once it identifies a container with at least the label 'easyhaproxy.http.host,' it configures HAProxy to redirect traffic to that container. To accomplish this, EasyHAProxy may need to attach the same network to its container.
|
|
||||||
|
|
||||||
:::tip Docker Swarm Advantages
|
|
||||||
- **Container Discovery**: Docker Swarm facilitates the discovery of containers within the cluster, streamlining the process of identifying services for HAProxy configuration.
|
|
||||||
- **Remote Node Management**: Docker Swarm allows for the management of containers across multiple nodes, providing flexibility and scalability in deploying services while ensuring seamless HAProxy configuration across the cluster.
|
|
||||||
:::
|
|
||||||
|
|
||||||
It's recommended to create a network external to EasyHAProxy.
|
|
||||||
|
|
||||||
:::warning Limitations
|
|
||||||
- You cannot mix Docker containers with Swarm containers.
|
|
||||||
- This method does not work with containers that use the `--network=host` option. See [limitations](limitations.md) for details.
|
|
||||||
:::
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```bash title="Create overlay network"
|
|
||||||
docker network create -d overlay --attachable easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
And then deploy the EasyHAProxy stack:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
haproxy:
|
|
||||||
image: byjg/easy-haproxy:6.0.0
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
deploy:
|
|
||||||
replicas: 1
|
|
||||||
environment:
|
|
||||||
EASYHAPROXY_DISCOVER: swarm
|
|
||||||
EASYHAPROXY_SSL_MODE: "loose"
|
|
||||||
HAPROXY_CUSTOMERRORS: "true"
|
|
||||||
HAPROXY_USERNAME: admin
|
|
||||||
HAPROXY_PASSWORD: password
|
|
||||||
HAPROXY_STATS_PORT: 1936
|
|
||||||
ports:
|
|
||||||
- "80:80/tcp"
|
|
||||||
- "443:443/tcp"
|
|
||||||
- "1936:1936/tcp"
|
|
||||||
networks:
|
|
||||||
- easyhaproxy
|
|
||||||
|
|
||||||
networks:
|
|
||||||
easyhaproxy:
|
|
||||||
external: true
|
|
||||||
```
|
|
||||||
|
|
||||||
Deploy the stack:
|
|
||||||
|
|
||||||
```bash title="Deploy EasyHAProxy stack"
|
|
||||||
docker stack deploy --compose-file docker-compose.yml easyhaproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
Mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels;
|
|
||||||
|
|
||||||
:::danger Single Replica Only
|
|
||||||
**Do not** add more than one replica for EasyHAProxy. To understand why, see the [limitations](limitations.md) page.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Running containers
|
|
||||||
|
|
||||||
To make your containers "discoverable" by EasyHAProxy, that is the minimum configuration you need:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
container:
|
|
||||||
image: my/image:tag
|
|
||||||
deploy:
|
|
||||||
replicas: 1
|
|
||||||
labels:
|
|
||||||
easyhaproxy.http.host: host1.local
|
|
||||||
easyhaproxy.http.port: 80
|
|
||||||
easyhaproxy.http.localport: 8080
|
|
||||||
networks:
|
|
||||||
- easyhaproxy
|
|
||||||
|
|
||||||
networks:
|
|
||||||
easyhaproxy:
|
|
||||||
external: true
|
|
||||||
```
|
|
||||||
|
|
||||||
Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container.
|
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
|
||||||
|
|
||||||
Please follow the [docker label configuration](container-labels.md) to see other configurations available.
|
|
||||||
|
|
||||||
## Setup the EasyHAProxy container
|
|
||||||
|
|
||||||
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [environment variable guide](environment-variable.md)
|
|
||||||
|
|
||||||
## More information
|
|
||||||
|
|
||||||
You can refer to the [Docker Documentation](docker.md) to get other detailed instructions.
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
|
|
@ -46,7 +46,7 @@ Each docker-compose file contains:
|
||||||
|
|
||||||
## Additional Documentation
|
## Additional Documentation
|
||||||
|
|
||||||
- [Container Labels Reference](../../docs/container-labels.md)
|
- [Container Labels Reference](../../docs/reference/container-labels.md)
|
||||||
- [Docker Configuration Guide](../../docs/docker.md)
|
- [Docker Configuration Guide](../../docs/getting-started/docker.md)
|
||||||
- [Environment Variables](../../docs/environment-variable.md)
|
- [Environment Variables](../../docs/reference/environment-variables.md)
|
||||||
- [Plugin Documentation](../../docs/plugins/)
|
- [Plugin Documentation](../../docs/guides/plugins.md)
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,6 @@ docker compose -f docker-compose-php-fpm.yml exec haproxy ping php-fpm
|
||||||
|
|
||||||
## Learn More
|
## Learn More
|
||||||
|
|
||||||
- [FastCGI Plugin Documentation](../../../docs/plugins.md#fastcgi-plugin)
|
- [FastCGI Plugin Documentation](../../../docs/reference/plugins/fastcgi.md)
|
||||||
- [Container Labels Reference](../../../docs/container-labels.md)
|
- [Container Labels Reference](../../../docs/reference/container-labels.md)
|
||||||
- [HAProxy FastCGI Documentation](https://docs.haproxy.org/2.8/configuration.html#5.2-proto)
|
- [HAProxy FastCGI Documentation](https://docs.haproxy.org/2.8/configuration.html#5.2-proto)
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ Each YAML file contains:
|
||||||
|
|
||||||
## Additional Documentation
|
## Additional Documentation
|
||||||
|
|
||||||
- [Kubernetes Installation Guide](../../docs/kubernetes.md)
|
- [Kubernetes Installation Guide](../../docs/getting-started/kubernetes.md)
|
||||||
- [Helm Installation](../../docs/helm.md)
|
- [Helm Installation](../../docs/guides/helm.md)
|
||||||
- [Kubernetes Annotations Reference](../../docs/kubernetes.md#kubernetes-annotations)
|
- [Kubernetes Annotations Reference](../../docs/getting-started/kubernetes.md#kubernetes-annotations)
|
||||||
- [Using Plugins with Kubernetes](../../docs/kubernetes.md#using-plugins-with-kubernetes)
|
- [Using Plugins with Kubernetes](../../docs/getting-started/kubernetes.md#using-plugins-with-kubernetes)
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,6 @@ See `conf/` directory for complete examples.
|
||||||
|
|
||||||
## Additional Documentation
|
## Additional Documentation
|
||||||
|
|
||||||
- [Static Configuration Guide](../../docs/static.md)
|
- [Static Configuration Guide](../../docs/getting-started/static.md)
|
||||||
- [Using Plugins](../../docs/plugins/)
|
- [Using Plugins](../../docs/guides/plugins.md)
|
||||||
- [Environment Variables](../../docs/environment-variable.md)
|
- [Environment Variables](../../docs/reference/environment-variables.md)
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ labels:
|
||||||
|
|
||||||
## Additional Documentation
|
## Additional Documentation
|
||||||
|
|
||||||
- [Docker Swarm Guide](../../docs/swarm.md)
|
- [Docker Swarm Guide](../../docs/getting-started/swarm.md)
|
||||||
- [Container Labels Reference](../../docs/container-labels.md)
|
- [Container Labels Reference](../../docs/reference/container-labels.md)
|
||||||
- [Using Plugins](../../docs/plugins/)
|
- [Using Plugins](../../docs/guides/plugins.md)
|
||||||
- [ACME/Let's Encrypt](../../docs/acme.md)
|
- [ACME/Let's Encrypt](../../docs/guides/acme.md)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue