Commit Graph

43 Commits

Author SHA1 Message Date
4769f67fe9 fix(coraza): ensure haproxy.cfg ends with LF when SPOE backend appended
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 54s
The SPOE backend block from hap_coraza_spoa_backend.tpl was being appended
last to config_parts. The template's render output doesn't end with a
newline (and config_parts is joined with '\n' BETWEEN elements, not after
the last one), so the resulting haproxy.cfg ended on `server coraza-spoa
...` with no trailing LF. HAProxy refuses to parse such files:

    [ALERT] config: parsing [/etc/haproxy/haproxy.cfg:288]: Missing LF
    on last line, file might have been truncated at position 70.

Match the existing pattern at the previous-last config_parts.append
(line 1850 uses `'\n'.join(config_backends) + '\n'`) and add an explicit
'\n' on the coraza block append.

Caught immediately on staging: HTTP 000 to localhost:80 because HAProxy
never started; gunicorn/management API kept serving on :8000 fine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:03:56 -07:00
73b9104565 PR 2/3: opt-in SPOE integration for Coraza WAF
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m59s
Adds the plumbing that lets haproxy-manager talk to the coraza-spoa sidecar
added in PR 1, while keeping the default behavior bit-identical for any
deployment that doesn't set the new env var (the home network / standalone
use cases).

Single gate: HAPROXY_CORAZA_SPOE_BACKEND env var on the haproxy-manager
container. Unset (default) = generate_config() renders zero SPOE-related
output. Set (e.g. "coraza-spoa:9000") = three things happen at config
generation time:

  1. hap_listener.tpl injects 5 lines at the end of the frontend block:
       filter spoe engine coraza config /etc/haproxy/coraza-spoe.cfg
       http-request send-spoe-group coraza coraza-check
     ...placed AFTER rate-limit and IP-block guards so we don't waste WAF
     calls on requests we were going to drop anyway.

  2. A new TCP backend (hap_coraza_spoa_backend.tpl) is appended:
       backend coraza-spoa-backend
           mode tcp
           server coraza-spoa <env-var-target> check ...

  3. The SPOE engine config (hap_coraza_spoe_engine.tpl) is rendered and
     written to /etc/haproxy/coraza-spoe.cfg, defining the spoe-agent
     "coraza" + spoe-message "coraza-check". This sets:
       - option set-on-error continue   (FAIL-OPEN if SPOA is unreachable)
       - timeout processing 100ms       (per-request inspection budget)
       - app=str(haproxy)               (matches sidecar's application name)

Verification (template render only, before staging deploy):
  - hap_listener.tpl with no env var: 55 lines, zero SPOE references
  - hap_listener.tpl with env var:    62 lines, filter + send-spoe-group present
  - Engine cfg + backend block render with correct agent_target substitution

Next: PR 3 wires this into WHP (sidecar deploy via container-manager.sh
extension, server-settings UI for on/off, AI Monitor source for the audit
log). Staging verification of PR 1 + PR 2 together happens after PR 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:49:29 -07:00
bdd7d2f098 swap werkzeug dev server for gunicorn + accept all HTTP methods on default/blocked pages
Some checks failed
HAProxy Manager Build and Push / Build-and-Push (push) Failing after 13s
Two related fixes for the issues the AI Monitor surfaced on whp01 on
2026-05-12 (haproxy-manager going "healthy but stalled" after long
uptime, and noise from POST /blocked-ip returning 405):

1. Production WSGI server. The Flask app was running on werkzeug's
   built-in dev server (the one that prints "WARNING: This is a
   development server" on every startup). werkzeug is single-threaded
   and accumulates worker state over long uptimes; after ~24h on whp01
   the health endpoint stops responding while the container still
   reports "healthy" because Docker's HEALTHCHECK uses an HTTP probe
   from inside the same werkzeug process that's stalled.

   Replace with gunicorn (gthread worker class, --max-requests=1000
   with jitter so workers recycle periodically). Two gunicorn instances,
   one per Flask app — port 8000 for the management API, port 8080 for
   the default/blocked-ip page server. Both lift their app objects from
   the haproxy_manager module so gunicorn can import them.

   Required structural change: default_app was created INSIDE the
   __name__ == '__main__' block at module bottom, where gunicorn could
   never reach it. Moved to module level. The __main__ block now stays
   only for `python haproxy_manager.py` local-dev workflow.

   Container init (init_db, certbot register, generate_config,
   start_haproxy) extracted into a do_initial_setup() function called
   from a new scripts/init.py. start-up.sh runs init.py to completion
   before either gunicorn binds, which keeps HAProxy startup off the
   WSGI workers' fork paths (no race between workers all trying to
   start_haproxy() at once).

2. /blocked-ip and / accept ALL methods. HAProxy proxies blocked-IP
   traffic to default_app preserving the original verb, so a blocked
   POST request used to hit Flask's GET-only route and get a 405 +
   the AI Monitor flagged the noise. Adding the full method list lets
   the 403 page render regardless of verb.

Gunicorn settings tunable via env (workers, timeout, max-requests).
API gets --timeout 120 because ACME cert issuance can be slow; the
default page server stays on the gunicorn default 30s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:24:28 -07:00
8a86beac73 feat: clear stale certbot lock files before each ACME run + at startup
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 53s
certbot uses fasteners (fcntl-based locking) to serialize concurrent
invocations. The kernel auto-releases fcntl locks when the holding
process exits, but the .certbot.lock FILES persist on disk — and we've
seen real cases where subsequent runs report "Another instance of
Certbot is already running" even when no certbot process is alive.
Observed during the 2026-05-09 bundling rollout when a hung worker
held a lock across container-internal Python crashes.

When SSL is blocked on a customer site, this is high-impact: the
certbot lock can sit stale until somebody manually deletes it.

clear_stale_certbot_locks():
  - probes each known lock path with fcntl.LOCK_NB
  - if the lock is unheld → file is stale → delete it
  - if the lock IS held → leave it alone (real certbot is running)

Wired in:
  - container startup (init block)
  - /api/ssl single-domain handler
  - /api/ssl/bundle handler
  - /api/certificates/renew handler

Safe to call repeatedly; never deletes a lock a real process holds, so
can never trigger concurrent certbot runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 12:09:19 -07:00
f7ef34b988 feat(api/ssl/bundle): clean up superseded lineages after issuance
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 53s
The bundle endpoint correctly issued multi-SAN certs but left old
single-SAN .pem files (e.g. <name>-0001.pem) in /etc/haproxy/certs/.
HAProxy's `bind ... ssl crt /etc/haproxy/certs` loads everything in the
directory and picked the alphabetically-first matching file — typically
the older single-SAN one — so the new bundle had no effect on what was
served. Repro on peptidesaver.net: bundle covered 4 SANs but HAProxy
kept serving peptidesaver.net-0001.pem (single SAN, April-issued).

After a successful bundle write, walk SSL_CERTS_DIR and remove any
.pem whose CN is in the new bundle's name list (excluding the bundle's
own combined file). Drop the matching certbot lineage with
`certbot delete --cert-name <X> -n` so `certbot renew` stops touching
the dead lineage too.

Returns a `cleanup` summary in the API response so callers can log /
display what was deleted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:58:21 -07:00
90255cc4b3 feat(api): add /api/ssl/bundle for per-site SAN cert issuance
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m1s
WHP's renewal orchestrator now bundles a site's domains into one cert
covering all SANs, instead of N separate single-domain orders. Single
ACME order = better behavior under Let's Encrypt's 50/hour orders limit
when many domains need attention at once.

Endpoint: POST /api/ssl/bundle
Body: {"primary": "example.com", "sans": ["www.example.com", ...]}

- Uses --cert-name <primary> so the lineage stays stable across renewals
  (no -0001/-0002 proliferation seen with the legacy single-domain flow).
- Single combined .pem at /etc/haproxy/certs/<primary>.pem; HAProxy SNI-
  matches against the cert's SAN list, so one file serves all included
  hostnames.
- Updates the domains table for every SAN in the bundle.
- Hard cap at 100 SANs (LE limit).

Existing /api/ssl single-domain endpoint kept for backwards compat.
The WHP haproxy_manager::bundleSSL() helper falls back to a per-domain
loop if /api/ssl/bundle returns 404, so the WHP side keeps working
during the rolling image upgrade window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:32:15 -07:00
124a5373d2 Fix wildcard SSL cert: find certbot -NNNN dirs and use _wildcard_ filename
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m1s
Add find_certbot_live_dir() helper to locate the most recent certbot live
directory for a domain, handling -NNNN suffixed dirs from repeated requests.
Fix combined cert filename from *.domain.pem to _wildcard_.domain.pem.
Apply the helper across all SSL endpoints (request, renew, verify, download).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 06:38:28 -08:00
657cd28344 Fix certbot hook script paths and add logging
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 3m4s
Hook scripts are at /haproxy/scripts/ inside the container (per
Dockerfile COPY), not /app/scripts/. Also added logging of certbot
stdout/stderr so failures are visible in haproxy-manager.log.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 06:18:14 -08:00
91c92dd07e Add wildcard domain support with DNS-01 ACME challenge flow
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m17s
Support wildcard domains (*.domain.tld) in HAProxy config generation
with exact-match ACLs prioritized over wildcard ACLs. Add DNS-01
challenge endpoints that coordinate with certbot via auth/cleanup
hook scripts for wildcard SSL certificate issuance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:06:08 -08:00
1fcb25bb88 Update SQL logic to update instead of delete and re-add
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m18s
2025-12-18 12:23:06 -08:00
1d22d789b8 Simplify certificate renewal scripts and add certbot cleanup
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 59s
Simplified all certificate renewal scripts to be more straightforward and reliable:
- Scripts now just run certbot renew and copy cert+key files to HAProxy format
- Removed overly complex retry logic and error handling
- Both in-container and host-side scripts work with cron scheduling

Added automatic certbot cleanup when domains are removed:
- When a domain is deleted via API, certbot certificate is also removed
- Prevents renewal errors for domains that no longer exist in HAProxy
- Cleans up both HAProxy combined cert and Let's Encrypt certificate

Script changes:
- renew-certificates.sh: Simplified to 87 lines (from 215)
- sync-certificates.sh: Simplified to 79 lines (from 200+)
- host-renew-certificates.sh: Simplified to 36 lines (from 40)
- All scripts use same pattern: query DB, copy certs, reload HAProxy

Python changes:
- remove_domain() now calls 'certbot delete' to remove certificates
- Prevents orphaned certificates from causing renewal failures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 09:56:56 -08:00
71f4b9ef05 Add CIDR notation support for IP blocking
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 2m1s
- Update map file format to include value (IP/CIDR 1)
- Fix HAProxy template to use map_ip() for CIDR support
- Update runtime map commands to include value
- Document CIDR range blocking in API documentation
- Support blocking entire network ranges (e.g., 192.168.1.0/24)

This allows blocking compromised ISP ranges and other large-scale attacks.
2025-11-17 12:07:32 -08:00
65248680a5 Fix HAProxy 3.0.11 compatibility issues
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m54s
Major syntax and configuration updates for HAProxy 3.0.11:

Configuration Fixes:
- Remove conflicting stick-table declarations in frontend
- Move security tables to separate backend sections
- Fix ACL syntax errors (missing_browser_headers → separate ACLs)
- Remove unsupported add-var() syntax
- Simplify threat scoring to use flags instead of cumulative values

Security Table Architecture:
- security_blacklist: 24h persistent offender tracking
- wp_403_track: WordPress authentication failure monitoring
- Separated from main frontend table to avoid conflicts

Simplified Threat Detection:
- low_threat: Rate abuse, suspicious methods, missing headers
- medium_threat: SQL injection, directory traversal, WordPress brute force
- high_threat: Bot scanners, admin scans, shell attempts
- critical_threat: Blacklisted IPs, auto-blacklist candidates

Response System:
- Low threat: Warning headers only
- Medium threat: Tarpit delays
- High threat: Immediate deny (403)
- Critical threat: Blacklist and deny

Enhanced Compatibility:
- Removed HAProxy 2.6-specific syntax
- Updated to HAProxy 3.0.11 requirements
- Maintained security effectiveness with simpler logic
- Added security tables template integration

The system maintains comprehensive protection while being compatible
with HAProxy 3.0.11's stricter parsing and syntax requirements.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 17:29:32 -07:00
e2f350ce95 Add comprehensive anti-scan and brute force protection
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 54s
Implement multi-layered security system to protect against exploit
scanning and brute force attacks while maintaining legitimate traffic flow.

Security Features:
- Attack detection for common exploit paths (WordPress, phpMyAdmin, shells)
- Malicious user agent filtering (sqlmap, nikto, metasploit, etc.)
- SQL injection and directory traversal pattern detection
- Progressive rate limiting (50 req/10s, 20 conn/10s, 10 err/10s)
- Three-tier response: tarpit → deny → repeat offender blocking
- Strict authentication endpoint protection (5 req/10s limit)
- Real IP detection through proxy headers (Cloudflare, X-Real-IP)

Management Tools:
- manage-blocked-ips.sh: Dynamic IP blocking/unblocking
- monitor-attacks.sh: Real-time threat monitoring
- API endpoints for security stats and temporary blocking
- Auto-expiring temporary blocks with cleanup endpoint

HAProxy 2.6 Compatibility:
- Removed silent-drop (not available in 2.6)
- Fixed stick table counter syntax
- Using standard tarpit and deny actions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 16:50:35 -07:00
2406d9f995 Add 403 status to blocked IP page and reload HAProxy on IP block/unblock
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 51s
- Modified /blocked-ip route to return 403 Forbidden status with HTML page
- Added HAProxy reload after adding blocked IP to ensure consistency
- Added HAProxy reload after removing blocked IP to ensure consistency
- Includes error handling for reload failures without breaking the operation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 10:06:04 -07:00
7869b81f27 CRITICAL FIX: Migrate HAProxy IP blocking from ACL to map files
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 51s
**Problem Solved:**
- HAProxy ACL 64-word limit caused config parsing failures
- "too many words, truncating after word 64" error
- Complete service outage when >64 IPs were blocked
- Error: "no such ACL : 'is_blocked'" broke all traffic routing

**Solution: HAProxy Map Files (v1.6+)**
-  Unlimited IP addresses (no word limits)
-  Runtime updates without config reloads
-  Better performance (hash table vs linear search)
-  Safer config management with validation & rollback

**Technical Implementation:**

**Map File Integration:**
- `/etc/haproxy/blocked_ips.map` stores all blocked IPs
- `http-request deny status 403 if { src -f /etc/haproxy/blocked_ips.map }`
- Runtime updates: `echo "add map #0 IP" | socat stdio /var/run/haproxy.sock`

**Safety Features Added:**
- `create_backup()` - Automatic config/map backups before changes
- `validate_haproxy_config()` - Config validation before applying
- `restore_backup()` - Automatic rollback on failures
- `reload_haproxy_safely()` - Safe reload with validation pipeline

**Runtime Management:**
- `update_blocked_ips_map()` - Sync database to map file
- `add_ip_to_runtime_map()` - Immediate IP blocking without reload
- `remove_ip_from_runtime_map()` - Immediate IP unblocking

**New API Endpoints:**
- `POST /api/config/reload` - Safe config reload with rollback
- `POST /api/blocked-ips/sync` - Sync database to runtime map

**Template Changes:**
- Replaced ACL method: `acl is_blocked src IP1 IP2...` (64 limit)
- With map method: `http-request deny if { src -f blocked_ips.map }` (unlimited)

**Backwards Compatibility:**
- Existing API endpoints unchanged (GET/POST/DELETE /api/blocked-ips)
- Database schema unchanged
- Automatic migration on first config generation

**Performance Improvements:**
- O(1) hash table lookups vs O(n) linear ACL search
- No config reloads needed for IP changes
- Supports millions of IPs if needed
- Memory efficient external file storage

**Documentation:**
- Complete migration guide in MIGRATION_GUIDE.md
- Updated API documentation with new endpoints
- Runtime management examples
- Troubleshooting guide

**Production Safety:**
- All changes include automatic backup/restore
- Config validation prevents bad deployments
- Runtime updates avoid service interruption
- Comprehensive error logging and monitoring

This fixes the critical production outage caused by ACL word limits
while providing a more scalable and performant IP blocking solution.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 08:31:17 -07:00
ca37a68255 Add IP blocking functionality to HAProxy Manager
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m1s
- Add blocked_ips database table to store blocked IP addresses
- Implement API endpoints for IP blocking management:
  - GET /api/blocked-ips: List all blocked IPs
  - POST /api/blocked-ips: Block an IP address
  - DELETE /api/blocked-ips: Unblock an IP address
- Update HAProxy configuration generation to include blocked IP ACLs
- Create blocked IP page template for denied access
- Add comprehensive API documentation for WHP integration
- Include test script for IP blocking functionality
- Update .gitignore with Python patterns
- Add CLAUDE.md for codebase documentation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 18:32:47 -07:00
d4f54aef35 Fix HAProxy crash loop and improve startup resilience
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 36s
- Add configuration regeneration before HAProxy startup
- Add configuration validation before starting HAProxy
- Add automatic configuration regeneration if invalid config detected
- Prevent container crashes when HAProxy fails to start
- Allow container to continue running even if HAProxy is not available
- Add better error handling and logging for startup issues
2025-07-11 19:37:41 -07:00
fac6cef0db Fix HAProxy 2.6 compatibility for default backend
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 37s
- Replace http-response set-body (HAProxy 2.8+) with local server approach
- Add separate Flask server on port 8080 to serve default page
- Update default backend template to use local server instead of inline HTML
- Maintain all customization features via environment variables
- Fix JavaScript error handling for domains API response
2025-07-11 19:27:42 -07:00
27f3f8959b Add default backend page for unmatched domains
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 37s
- 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
2025-07-11 19:10:05 -07:00
ef488a253d Add /api/certificates/request endpoint for programmatic certificate requests, update docs and add test script
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 37s
2025-07-11 17:14:01 -07:00
7b0b4c0476 Major upgrade: API key authentication, certificate renewal/download endpoints, monitoring/alerting scripts, improved logging, and documentation updates. See UPGRADE_SUMMARY.md for details.
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 43s
2025-07-11 06:24:56 -07:00
7550df9890 Fixing reload issue 2025-04-18 16:52:57 -07:00
8ae1a6b99f debug reload
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m9s
2025-03-09 11:56:18 -07:00
9de12c72de added missing return
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 58s
2025-03-09 11:11:35 -07:00
cb58f1d762 Switch reload from post to get
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 35s
2025-03-09 11:07:21 -07:00
2492eab708 Fix missing '/'
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 35s
2025-03-09 11:02:20 -07:00
64c707317f Adding reload function and more tweaks for backends
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 48s
2025-03-09 10:59:03 -07:00
9621786175 Adding web interface
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m18s
2025-03-06 16:51:29 -08:00
c5f29374e1 Fix Template Override
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 38s
2025-02-21 10:17:15 -08:00
d944a75fb5 fix backend creation
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 39s
2025-02-21 08:28:56 -08:00
ac40737fd7 Adding template overrides
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 38s
2025-02-21 08:07:58 -08:00
6b28c118de Adding template overrides
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 38s
2025-02-21 08:01:16 -08:00
c47118729f add new line at the end of the server block to prevent issue with haproxy reloading
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 38s
2025-02-21 06:42:30 -08:00
ff529be07f Fix Templates from causing errors with haproxy when added, Fix add notice when haproxy fails check
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 38s
2025-02-21 06:28:51 -08:00
c951103b3b adding function on start up
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 39s
2025-02-21 06:00:37 -08:00
1df58758b5 Make haproxy start with the script
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 39s
2025-02-20 17:00:28 -08:00
32498f1a04 fix order of acls and backends, put db in volume
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 38s
2025-02-20 16:26:27 -08:00
ff46f0a616 Add regenerate endpoint
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 38s
2025-02-20 15:40:32 -08:00
576666d9da Fixing order for haproxy config
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 39s
2025-02-20 15:29:42 -08:00
cef684b0a9 register certbot by default 2025-02-20 14:01:53 -08:00
305fffba42 haproxy manager 2025-02-20 13:41:38 -08:00
9c52edd53a Not fully working, but saving progress 2025-02-19 07:53:26 -08:00