From 27f3f8959bd4dfdec4a7fa59ebc356103d92c453 Mon Sep 17 00:00:00 2001 From: jknapp Date: Fri, 11 Jul 2025 19:10:05 -0700 Subject: [PATCH] Add default backend page for unmatched domains - Add default backend template (hap_default_backend.tpl) - Add customizable default page HTML template (default_page.html) - Modify generate_config() to include default backend for unmatched domains - Add environment variables for customizing default page content: - HAPROXY_DEFAULT_PAGE_TITLE - HAPROXY_DEFAULT_MAIN_MESSAGE - HAPROXY_DEFAULT_SECONDARY_MESSAGE - Update README with documentation and examples - Ensure backward compatibility with existing configurations - Remove email contact link as requested --- README.md | 29 ++++++++++ haproxy_manager.py | 32 ++++++++++- templates/default_page.html | 91 +++++++++++++++++++++++++++++++ templates/hap_default_backend.tpl | 12 ++++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 templates/default_page.html create mode 100644 templates/hap_default_backend.tpl diff --git a/README.md b/README.md index 10968a3..a4bb036 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ docker run -d -p 80:80 -p 443:443 -p 8000:8000 -v lets-encrypt:/etc/letsencrypt - **NEW**: Certificate download endpoints for other services - **NEW**: Comprehensive error logging and alerting system - **NEW**: Certificate status monitoring with expiration dates +- **NEW**: Default backend page for unmatched domains ## Security @@ -326,6 +327,34 @@ tail -f /var/log/haproxy-manager-errors.log | Variable | Description | Default | |----------|-------------|---------| | `HAPROXY_API_KEY` | API key for authentication (optional) | None (no auth) | +| `HAPROXY_DEFAULT_PAGE_TITLE` | Title for the default page | Site Not Configured | +| `HAPROXY_DEFAULT_MAIN_MESSAGE` | Main message on the default page | This domain has not been configured yet. Please contact your system administrator to set up this website. | +| `HAPROXY_DEFAULT_SECONDARY_MESSAGE` | Secondary message on the default page | If you believe this is an error, please check the domain name and try again. | + +## Default Backend Configuration + +When a domain is accessed that hasn't been configured in HAProxy, the system will serve a default page instead of showing an error. This default page: + +- Informs visitors that the site is not configured +- Displays the domain name and current timestamp +- Is fully customizable through environment variables + +### Customizing the Default Page + +You can customize the default page by setting environment variables: + +```bash +docker run -d \ + -p 80:80 -p 443:443 -p 8000:8000 \ + -v lets-encrypt:/etc/letsencrypt \ + -v haproxy:/etc/haproxy \ + -e HAPROXY_API_KEY=your-secure-api-key-here \ + -e HAPROXY_DEFAULT_PAGE_TITLE="Website Coming Soon" \ + -e HAPROXY_DEFAULT_MAIN_MESSAGE="This website is currently under construction and will be available soon." \ + -e HAPROXY_DEFAULT_SECONDARY_MESSAGE="Please check back later or contact us for more information." \ + --name haproxy-manager \ + repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:latest +``` ## Example Usage diff --git a/haproxy_manager.py b/haproxy_manager.py index 05b861e..09c8c62 100644 --- a/haproxy_manager.py +++ b/haproxy_manager.py @@ -728,7 +728,12 @@ def generate_config(): config_parts.append(letsencrypt_acl) config_acls = [] config_backends = [] -# Add domain configurations + + # Add default backend rule (will be used when no domain matches) + default_rule = " # Default backend for unmatched domains\n default_backend default-backend\n" + config_parts.append(default_rule) + + # Add domain configurations for domain in domains: if not domain['backend_name']: logger.warning(f"Skipping domain {domain['domain']} - no backend name") @@ -782,6 +787,31 @@ def generate_config(): # Add LetsEncrypt Backend letsencrypt_backend = template_env.get_template('hap_letsencrypt_backend.tpl').render() config_parts.append(letsencrypt_backend) + # Add Default Backend + try: + # Render the default page template with customizable content + default_page_template = template_env.get_template('default_page.html') + default_page_content = default_page_template.render( + page_title=os.environ.get('HAPROXY_DEFAULT_PAGE_TITLE', 'Site Not Configured'), + main_message=os.environ.get('HAPROXY_DEFAULT_MAIN_MESSAGE', 'This domain has not been configured yet. Please contact your system administrator to set up this website.'), + secondary_message=os.environ.get('HAPROXY_DEFAULT_SECONDARY_MESSAGE', 'If you believe this is an error, please check the domain name and try again.') + ) + default_page_content = default_page_content.replace('"', '\\"').replace('\n', '\\n') + + default_backend = template_env.get_template('hap_default_backend.tpl').render( + default_page_content=default_page_content + ) + config_parts.append(default_backend) + except Exception as e: + logger.error(f"Error generating default backend: {e}") + # Fallback to a simple default backend + fallback_backend = '''# Default backend for unmatched domains +backend default-backend + mode http + option http-server-close + http-response set-header Content-Type text/html + http-response set-body "Site Not Configured

Site Not Configured

This domain has not been configured yet.

"''' + config_parts.append(fallback_backend) # Add Backends config_parts.append('\n' .join(config_backends) + '\n') # Write complete configuration to tmp diff --git a/templates/default_page.html b/templates/default_page.html new file mode 100644 index 0000000..3bebf23 --- /dev/null +++ b/templates/default_page.html @@ -0,0 +1,91 @@ + + + + + + {{ page_title }} + + + +
+ ⚠️ +

{{ page_title }}

+

{{ main_message }}

+

{{ secondary_message }}

+ +
+ Domain:
+ Time: +
+ + +
+ + + + \ No newline at end of file diff --git a/templates/hap_default_backend.tpl b/templates/hap_default_backend.tpl new file mode 100644 index 0000000..e080ded --- /dev/null +++ b/templates/hap_default_backend.tpl @@ -0,0 +1,12 @@ +# Default backend for unmatched domains +backend default-backend + mode http + option http-server-close + http-request set-header X-Forwarded-Proto https if { ssl_fc } + http-request set-header X-Forwarded-Port %[dst_port] + http-request set-header X-Forwarded-For %[src] + http-request set-header X-Real-IP %[src] + + # Serve the default page HTML response + http-response set-header Content-Type text/html + http-response set-body "{{ default_page_content }}" \ No newline at end of file