Skip to content

Conversation

@byjg
Copy link
Owner

@byjg byjg commented Nov 28, 2025

Summary

This PR implements a complete plugin system for EasyHAProxy, including four built-in security plugins, comprehensive test coverage, and extensive documentation with runnable examples across all deployment methods.


Major Features

🔌 Plugin System Architecture

Plugin Types:

  • PluginType.DOMAIN - Execute once per domain/host
  • PluginType.GLOBAL - Execute once per discovery cycle

Configuration Sources (in order of precedence):

  • Docker: Container labels > YAML config > Environment variables
  • Kubernetes: Ingress annotations > YAML config > Environment variables
  • Swarm: Service labels > YAML config > Environment variables
  • Static: Per-domain config > Global plugins > Environment variables

Plugin Lifecycle:

  1. Discovery and loading from src/plugins/builtin/ and custom paths
  2. Validation of plugin configuration
  3. Execution with context (domain info, SSL settings, etc.)
  4. HAProxy configuration injection

🛡️ Built-in Plugins

1. JWT Validator Plugin

File: src/plugins/builtin/jwt_validator.py

Features:

  • Validates JWT tokens using RS256 algorithm
  • Supports issuer and audience validation
  • Configurable public key path
  • Validates token signature and expiration
  • Returns meaningful HTTP errors for validation failures

Configuration Example:

plugins:
  - jwt_validator
plugin_config:
  jwt_validator:
    algorithm: RS256
    issuer: https://auth.example.com/
    audience: https://api.example.com
    pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem

2. IP Whitelist Plugin

File: src/plugins/builtin/ip_whitelist.py

Features:

  • Restricts access to specific IP addresses or CIDR ranges
  • Supports multiple IPs (comma-separated)
  • Configurable HTTP status code (403 or 404)
  • Ideal for admin panels and internal tools

Configuration Example:

plugins:
  - ip_whitelist
plugin_config:
  ip_whitelist:
    allowed_ips: 192.168.0.0/16,10.0.0.0/8
    status_code: 403

3. Deny Pages Plugin

File: src/plugins/builtin/deny_pages.py

Features:

  • Blocks access to specific URL paths
  • Supports path prefix matching
  • Configurable HTTP status code
  • Protects sensitive endpoints like /admin, /.env, /wp-admin

Configuration Example:

plugins:
  - deny_pages
plugin_config:
  deny_pages:
    paths: [/admin, /.env, /config]
    status_code: 404

4. Cloudflare Plugin

File: src/plugins/builtin/cloudflare.py

Features:

  • Restores original visitor IP from Cloudflare headers
  • Validates requests come from Cloudflare IPs
  • Sets X-Forwarded-For header with real visitor IP
  • Supports custom Cloudflare IP list path

Configuration Example:

plugins:
  - cloudflare
plugin_config:
  cloudflare:
    ip_list_path: /etc/haproxy/cloudflare_ips.lst

🧪 Testing

Added comprehensive plugin test suite with 34 new tests:

  • Plugin discovery and loading
  • Configuration validation
  • HAProxy config generation
  • Global vs domain plugin execution
  • Plugin enabled/disabled logic
  • Critical bug fix: Plugins now correctly skip execution when enabled_list=None

Test Results:

pytest -c src/tests -v
# 80 tests passed (including 34 plugin-specific tests)

📁 File Structure

Core Plugin System

  • src/plugins/__init__.py - Plugin base classes and discovery logic
  • src/plugins/builtin/jwt_validator.py - JWT validation plugin
  • src/plugins/builtin/ip_whitelist.py - IP restriction plugin
  • src/plugins/builtin/deny_pages.py - Path blocking plugin
  • src/plugins/builtin/cloudflare.py - IP restoration plugin
  • src/processor/__init__.py - Modified to integrate plugin system
  • src/easymapping/__init__.py - Modified to support plugin configuration
  • src/templates/haproxy.cfg.j2 - Modified to inject plugin configurations

Documentation

  • docs/plugins.md - Complete plugin system documentation
  • docs/plugin-development.md - Guide for creating custom plugins
  • examples/docker/README.md - Docker Compose examples guide (165+ lines)
  • examples/kubernetes/README.md - Kubernetes ingress examples guide (485+ lines)
  • examples/static/README.md - Static configuration guide (437+ lines)
  • examples/swarm/README.md - Docker Swarm deployment guide (596+ lines)

🚀 Runnable Examples (12 files)

Docker Compose Examples (4 files)

  • examples/docker/docker-compose-jwt-validator.yml - JWT token validation with key generation and test commands
  • examples/docker/docker-compose-cloudflare.yml - Cloudflare IP restoration with download instructions
  • examples/docker/docker-compose-ip-whitelist.yml - IP address restriction with CIDR notation examples
  • examples/docker/docker-compose-plugins-combined.yml - Multiple plugins working together

Kubernetes Examples (4 files)

  • examples/kubernetes/jwt-validator.yml - Complete JWT setup with ConfigMap, Deployment, Service, and Ingress
  • examples/kubernetes/cloudflare.yml - Cloudflare IP restoration with actual IP ranges in ConfigMap
  • examples/kubernetes/ip-whitelist.yml - IP whitelist with annotation-based configuration
  • examples/kubernetes/plugins-combined.yml - Three-tier security setup (Website, API, Admin)

Docker Swarm Examples (4 files)

  • examples/swarm/jwt-validator.yml - JWT validation using Docker configs with creation commands
  • examples/swarm/cloudflare.yml - Cloudflare IP restoration using Docker configs
  • examples/swarm/ip-whitelist.yml - IP whitelist with service label syntax
  • examples/swarm/plugins-combined.yml - Production-ready multi-service stack

Static Configuration Examples (4 files)

  • examples/static/conf/config-basic.yml - Minimal configuration without plugins
  • examples/static/conf/config-deny-pages.yml - Global plugin with per-domain overrides
  • examples/static/conf/config-jwt-validator.yml - JWT validation for multiple APIs
  • examples/static/conf/config-certbot.yml - Let's Encrypt/ACME automatic SSL certificates

🐛 Bug Fixes

1. Critical: Plugin execution with None enabled_list

Problem: Plugins were executing even when enabled_list=None

Fix: Added proper check to skip plugin execution when no plugins are enabled

Impact: Prevents unnecessary plugin execution and potential errors

2. Double indentation in HAProxy output

Problem: Plugins were adding 4 spaces to HAProxy config lines, and the Jinja2 template was also adding 4 spaces via indent(4, first=True), resulting in 8 spaces total instead of 4.

Fix: Removed indentation from all plugin output. The Jinja2 template now handles all indentation consistently.

Files Modified:

  • src/plugins/builtin/jwt_validator.py
  • src/plugins/builtin/ip_whitelist.py
  • src/plugins/builtin/deny_pages.py
  • src/plugins/builtin/cloudflare.py

Before:

haproxy_config = f"""# Deny Pages
    acl denied_path path_beg {paths_str}
    http-request deny deny_status {self.status_code} if denied_path"""

After:

haproxy_config = f"""# Deny Pages
acl denied_path path_beg {paths_str}
http-request deny deny_status {self.status_code} if denied_path"""

🎯 Kubernetes Annotation Support

Added full plugin support via Kubernetes Ingress annotations:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    easyhaproxy.plugins: "jwt_validator,deny_pages"
    easyhaproxy.plugin.jwt_validator.algorithm: "RS256"
    easyhaproxy.plugin.jwt_validator.issuer: "https://auth.example.com/"
    easyhaproxy.plugin.deny_pages.paths: "/admin,/.env"
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080

Benefits

  1. Extensible Architecture - Easy to add custom plugins for specialized needs
  2. Security Hardening - Built-in plugins cover common security requirements (JWT validation, IP whitelisting, path blocking, Cloudflare integration)
  3. Multi-Platform Support - Works seamlessly with Docker, Kubernetes, Swarm, and Static modes
  4. Production-Ready - Comprehensive test coverage and real-world examples
  5. Developer-Friendly - Clear documentation and step-by-step plugin development guide
  6. Flexible Configuration - Supports both global and per-domain plugin settings with clear precedence rules

Migration Guide

Enabling Plugins

Docker Compose

services:
  app:
    labels:
      easyhaproxy.http.plugins: "jwt_validator,deny_pages"
      easyhaproxy.http.plugin.jwt_validator.algorithm: "RS256"
      easyhaproxy.http.plugin.jwt_validator.issuer: "https://auth.example.com/"
      easyhaproxy.http.plugin.deny_pages.paths: "/admin,/.env"

Kubernetes

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    easyhaproxy.plugins: "jwt_validator"
    easyhaproxy.plugin.jwt_validator.algorithm: "RS256"
    easyhaproxy.plugin.jwt_validator.pubkey_path: "/etc/haproxy/jwt_keys/api.pem"

Static Mode (Global)

plugins:
  enabled:
    - deny_pages
  config:
    deny_pages:
      paths:
        - /admin
        - /.env
      status_code: 404

Static Mode (Per-Domain)

easymapping:
  - port: 443
    ssl: true
    hosts:
      api.local:
        containers:
          - api:8080
        plugins:
          - jwt_validator
        plugin_config:
          jwt_validator:
            algorithm: RS256
            issuer: https://auth.example.com/
            pubkey_path: /etc/haproxy/jwt_keys/api.pem

Breaking Changes

None - This is a new feature addition. Existing configurations continue to work without modification.


What's Next

Users can now:

  • Use built-in security plugins immediately
  • Develop custom plugins following the plugin development guide
  • Mix and match plugins per domain for flexible security policies
  • Deploy with confidence using production-ready examples

byjg added 9 commits November 27, 2025 17:43
- Introduced a plugin system enabling EasyHAProxy extensions.
- Added `CleanupPlugin` (GLOBAL): Handles temporary file cleanup.
- Added `CloudflarePlugin` (DOMAIN): Restores visitor IP from Cloudflare headers.
- Added `DenyPagesPlugin` (DOMAIN): Blocks access to specified paths.
- Documented the plugin architecture, configuration options, and built-in plugins in `docs/plugin-development.md` and `docs/plugins.md`.
- Added `plugin_configs` attribute to test cases.
- Improved handling of enabled plugins in `easymapping/__init__.py`.
- Updated templates directory path resolution for robustness.
- Refactored example files to enhance formatting consistency.
…dling

- Created fixtures for `services-with-cloudflare`, `services-with-deny-pages`, and `services-with-multiple-plugins`.
- Added comprehensive test cases for `CloudflarePlugin`, `DenyPagesPlugin`, and `CleanupPlugin`.
- Ensured proper configuration propagation to plugins using labels in `easymapping/__init__.py`.
- Validated multi-plugin integration and consistent output in HAProxy configuration generation.
- Introduced `IpWhitelistPlugin` (DOMAIN): Restricts access to specific IP addresses or CIDR ranges.
- Added configuration options: `enabled`, `allowed_ips`, and `status_code`.
- Updated `plugins.md` and `plugin-development.md` to document `IpWhitelistPlugin`.
- Created fixtures for `services-with-ip-whitelist`.
- Added extensive test cases for `IpWhitelistPlugin` to validate configuration and HAProxy output generation.
- Ensured seamless integration into the plugin framework alongside other domain plugins.
- Introduced `JwtValidatorPlugin` (DOMAIN): Validates JWT tokens for protected API endpoints using HAProxy's JWT functionality.
- Added configuration options: `enabled`, `algorithm`, `issuer`, `audience`, `pubkey_path`, and `pubkey`.
- Documented the plugin setup and usage in `plugins.md` and `plugin-development.md`.
- Created fixtures for `services-with-jwt-validator`.
- Added extensive test cases to validate configuration and HAProxy output generation.
- Ensured seamless integration into the plugin framework alongside existing domain plugins.
…port

- Updated `plugins.md` and `kubernetes.md` with detailed plugin configuration examples for Kubernetes annotations.
- Introduced support for `easyhaproxy.plugins` and `easyhaproxy.plugin.{name}.{key}` annotations in Kubernetes.
- Added comprehensive examples for JWT validation, IP whitelisting, and deny pages
… examples

- Added detailed `README.md` files with step-by-step instructions for Docker Compose, Kubernetes, Swarm, and static configuration examples.
- Included use cases for SSL setup, Let's Encrypt integration, load balancing, and advanced features like plugins and path-based routing.
- Documented environment variables, service labels, and troubleshooting tips across all examples.
- Enhanced examples with clear testing guidelines, SSL certificate generation steps, and debugging workflows.
…warm

- Introduced multiple detailed plugin examples for Cloudflare IP restoration, IP whitelisting, JWT validation, and combined usage.
- Added configurations for `docker-compose`, `Kubernetes`, and `Swarm` showcasing individual and multi-plugin use cases.
- Included clear prerequisites, setup steps, and testing procedures for each example.
- Documented advanced scenarios like path blocking, custom IP lists, and JWT validation for production environments.
…mentation

- Replaced `config.yml` with modular configuration examples: `config-basic.yml`, `config-certbot.yml`, `config-deny-pages.yml`, and `config-jwt-validator.yml`.
- Improved examples with detailed usage instructions, prerequisites, and testing steps for each configuration.
- Fixed indentation and formatting inconsistencies across plugin files and HAProxy configuration generation.
- Streamlined README comparison table for static vs. dynamic discovery.
- Updated `docker-compose-jwt-validator.yml` to correct audience key formatting.
@byjg byjg changed the title Implement Plugin System with Built-in Plugins and Comprehensive Examples Implement Plugin System with Built-in Plugins and Comprehensive Examples #51 Nov 28, 2025
@byjg byjg changed the title Implement Plugin System with Built-in Plugins and Comprehensive Examples #51 Implement Plugin System with Built-in Plugins and Comprehensive Examples Nov 28, 2025
@byjg
Copy link
Owner Author

byjg commented Nov 28, 2025

Link to Issue #51

@byjg byjg linked an issue Nov 28, 2025 that may be closed by this pull request
byjg added 2 commits November 27, 2025 20:36
- Introduced `paths` and `only_paths` configuration options to define protected API paths.
- Enhanced HAProxy configuration generation to handle path-specific JWT validation.
- Updated plugin metadata to include path details and validation logic.
- Modified documentation with detailed examples for protecting paths.
- Added comprehensive tests for path-based validation scenarios and edge cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create Plugins for EasyHAProxy

2 participants