new options
This commit is contained in:
@@ -11,6 +11,7 @@ def index():
|
|||||||
frontend_name = request.form['frontend_name']
|
frontend_name = request.form['frontend_name']
|
||||||
frontend_ip = request.form['frontend_ip']
|
frontend_ip = request.form['frontend_ip']
|
||||||
frontend_port = request.form['frontend_port']
|
frontend_port = request.form['frontend_port']
|
||||||
|
frontend_hostname = request.form.get('frontend_hostname', '').strip()
|
||||||
lb_method = request.form['lb_method']
|
lb_method = request.form['lb_method']
|
||||||
protocol = request.form['protocol']
|
protocol = request.form['protocol']
|
||||||
backend_name = request.form['backend_name']
|
backend_name = request.form['backend_name']
|
||||||
@@ -156,7 +157,8 @@ def index():
|
|||||||
del_server_header=del_server_header,
|
del_server_header=del_server_header,
|
||||||
backend_ssl_redirect=backend_ssl_redirect,
|
backend_ssl_redirect=backend_ssl_redirect,
|
||||||
ssl_redirect_backend_name=ssl_redirect_backend_name,
|
ssl_redirect_backend_name=ssl_redirect_backend_name,
|
||||||
ssl_redirect_port=ssl_redirect_port
|
ssl_redirect_port=ssl_redirect_port,
|
||||||
|
frontend_hostname=frontend_hostname
|
||||||
)
|
)
|
||||||
|
|
||||||
# Determine message type
|
# Determine message type
|
||||||
|
|||||||
@@ -101,6 +101,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label for="frontend_hostname" class="form-label">Frontend Hostname (Domain)</label>
|
||||||
|
<input type="text" class="form-control" id="frontend_hostname" name="frontend_hostname"
|
||||||
|
placeholder="e.g. hosts.h.linuxiarz.pl" required>
|
||||||
|
<div class="form-text">Domain name for the ACL rule - traffic will be matched by Host header</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- SSL Section -->
|
<!-- SSL Section -->
|
||||||
<div class="row g-3 mb-3">
|
<div class="row g-3 mb-3">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@@ -116,8 +126,9 @@
|
|||||||
<div class="row g-3 mb-3 d-none" id="ssl_fields">
|
<div class="row g-3 mb-3 d-none" id="ssl_fields">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<label for="ssl_cert_path" class="form-label">SSL Certificate Path</label>
|
<label for="ssl_cert_path" class="form-label">SSL Certificate Path</label>
|
||||||
|
<small>Upload certs in /ssl/</small>
|
||||||
<input type="text" class="form-control" id="ssl_cert_path" name="ssl_cert_path"
|
<input type="text" class="form-control" id="ssl_cert_path" name="ssl_cert_path"
|
||||||
value="/etc/haproxy/certs/haproxy.pem">
|
value="/app/ssl/haproxy-configurator.pem">
|
||||||
<div class="form-text">Full path to .pem file</div>
|
<div class="form-text">Full path to .pem file</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
|||||||
@@ -75,6 +75,10 @@ def count_frontends_and_backends():
|
|||||||
|
|
||||||
return frontend_count, backend_count, acl_count, layer7_count, layer4_count
|
return frontend_count, backend_count, acl_count, layer7_count, layer4_count
|
||||||
|
|
||||||
|
def sanitize_name(name):
|
||||||
|
"""Convert hostname/name to valid ACL name (replace special chars with underscores)"""
|
||||||
|
return name.replace('.', '_').replace('-', '_').replace('/', '_').replace(':', '_')
|
||||||
|
|
||||||
def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, protocol, backend_name,
|
def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, protocol, backend_name,
|
||||||
backend_servers, health_check, health_check_tcp, health_check_link, sticky_session,
|
backend_servers, health_check, health_check_tcp, health_check_link, sticky_session,
|
||||||
add_header, header_name, header_value, sticky_session_type, is_acl, acl_name,
|
add_header, header_name, header_value, sticky_session_type, is_acl, acl_name,
|
||||||
@@ -83,7 +87,7 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
allowed_ip, forbidden_path, sql_injection_check, is_xss, is_remote_upload,
|
allowed_ip, forbidden_path, sql_injection_check, is_xss, is_remote_upload,
|
||||||
add_path_based, redirect_domain_name, root_redirect, redirect_to, is_webshells,
|
add_path_based, redirect_domain_name, root_redirect, redirect_to, is_webshells,
|
||||||
del_server_header=False, backend_ssl_redirect=False, ssl_redirect_backend_name='',
|
del_server_header=False, backend_ssl_redirect=False, ssl_redirect_backend_name='',
|
||||||
ssl_redirect_port='80'):
|
ssl_redirect_port='80', frontend_hostname=''):
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(HAPROXY_CFG), exist_ok=True)
|
os.makedirs(os.path.dirname(HAPROXY_CFG), exist_ok=True)
|
||||||
|
|
||||||
@@ -93,10 +97,11 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
# Tryb no-lb - prosty frontend→backend z pojedynczym serwerem
|
# Tryb no-lb - prosty frontend→backend z pojedynczym serwerem
|
||||||
is_no_lb = lb_method == 'no-lb'
|
is_no_lb = lb_method == 'no-lb'
|
||||||
if is_no_lb and len(backend_servers) > 1:
|
if is_no_lb and len(backend_servers) > 1:
|
||||||
backend_servers = backend_servers[:1] # Tylko pierwszy serwer
|
backend_servers = backend_servers[:1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(HAPROXY_CFG, 'a') as haproxy_cfg:
|
with open(HAPROXY_CFG, 'a') as haproxy_cfg:
|
||||||
|
# ===== PRIMARY FRONTEND =====
|
||||||
haproxy_cfg.write(f"\nfrontend {frontend_name}\n")
|
haproxy_cfg.write(f"\nfrontend {frontend_name}\n")
|
||||||
|
|
||||||
if is_frontend_exist(frontend_name, frontend_ip, frontend_port):
|
if is_frontend_exist(frontend_name, frontend_ip, frontend_port):
|
||||||
@@ -110,13 +115,19 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
|
|
||||||
haproxy_cfg.write("\n")
|
haproxy_cfg.write("\n")
|
||||||
|
|
||||||
# HTTPS redirect
|
# HTTPS redirect (global)
|
||||||
if https_redirect:
|
if https_redirect:
|
||||||
haproxy_cfg.write(f" redirect scheme https code 301 if !{{ ssl_fc }}\n")
|
haproxy_cfg.write(f" redirect scheme https code 301 if !{{ ssl_fc }}\n")
|
||||||
|
|
||||||
# Ustaw mode - zawsze dla no-lb (nie ma balance)
|
# Mode
|
||||||
haproxy_cfg.write(f" mode {protocol}\n")
|
haproxy_cfg.write(f" mode {protocol}\n")
|
||||||
|
|
||||||
|
# ===== HOSTNAME ACL =====
|
||||||
|
acl_name_sanitized = None
|
||||||
|
if frontend_hostname:
|
||||||
|
acl_name_sanitized = f"is_{sanitize_name(frontend_hostname)}"
|
||||||
|
haproxy_cfg.write(f" acl {acl_name_sanitized} hdr(host) -i {frontend_hostname}\n")
|
||||||
|
|
||||||
# W trybie no-lb używamy prostych nagłówków HTTP-request
|
# W trybie no-lb używamy prostych nagłówków HTTP-request
|
||||||
if is_no_lb:
|
if is_no_lb:
|
||||||
haproxy_cfg.write(f" http-request set-header X-Forwarded-For %[src]\n")
|
haproxy_cfg.write(f" http-request set-header X-Forwarded-For %[src]\n")
|
||||||
@@ -125,11 +136,9 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
else:
|
else:
|
||||||
haproxy_cfg.write(f" http-request set-header X-Forwarded-Proto http\n")
|
haproxy_cfg.write(f" http-request set-header X-Forwarded-Proto http\n")
|
||||||
|
|
||||||
# Opcja ukrycia nagłówka Server
|
|
||||||
if del_server_header:
|
if del_server_header:
|
||||||
haproxy_cfg.write(f" http-response del-header Server\n")
|
haproxy_cfg.write(f" http-response del-header Server\n")
|
||||||
else:
|
else:
|
||||||
# Standardowy tryb z option forwardfor i balance
|
|
||||||
haproxy_cfg.write(f" balance {lb_method}\n")
|
haproxy_cfg.write(f" balance {lb_method}\n")
|
||||||
|
|
||||||
if forward_for:
|
if forward_for:
|
||||||
@@ -138,14 +147,14 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
if del_server_header:
|
if del_server_header:
|
||||||
haproxy_cfg.write(f" http-response del-header Server\n")
|
haproxy_cfg.write(f" http-response del-header Server\n")
|
||||||
|
|
||||||
# DOS protection - działa w obu trybach
|
# DOS protection
|
||||||
if is_dos:
|
if is_dos:
|
||||||
haproxy_cfg.write(f" stick-table type ip size 1m expire {ban_duration} store http_req_rate(1m)\n")
|
haproxy_cfg.write(f" stick-table type ip size 1m expire {ban_duration} store http_req_rate(1m)\n")
|
||||||
haproxy_cfg.write(f" http-request track-sc0 src\n")
|
haproxy_cfg.write(f" http-request track-sc0 src\n")
|
||||||
haproxy_cfg.write(f" acl abuse sc_http_req_rate(0) gt {limit_requests}\n")
|
haproxy_cfg.write(f" acl abuse sc_http_req_rate(0) gt {limit_requests}\n")
|
||||||
haproxy_cfg.write(f" http-request silent-drop if abuse\n")
|
haproxy_cfg.write(f" http-request silent-drop if abuse\n")
|
||||||
|
|
||||||
# SQL Injection protection - działa w obu trybach
|
# SQL Injection protection
|
||||||
if sql_injection_check:
|
if sql_injection_check:
|
||||||
haproxy_cfg.write(" acl is_sql_injection urlp_reg -i (union|select|insert|update|delete|drop|@@|1=1|`1)\n")
|
haproxy_cfg.write(" acl is_sql_injection urlp_reg -i (union|select|insert|update|delete|drop|@@|1=1|`1)\n")
|
||||||
haproxy_cfg.write(" acl is_long_uri path_len gt 400\n")
|
haproxy_cfg.write(" acl is_long_uri path_len gt 400\n")
|
||||||
@@ -153,29 +162,34 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
haproxy_cfg.write(" acl is_sql_injection2 urlp_reg -i (;|substring|extract|union\\s+all|order\\s+by)\\s+(\\d+|--\\+)\n")
|
haproxy_cfg.write(" acl is_sql_injection2 urlp_reg -i (;|substring|extract|union\\s+all|order\\s+by)\\s+(\\d+|--\\+)\n")
|
||||||
haproxy_cfg.write(f" http-request deny if is_sql_injection or is_long_uri or semicolon_path or is_sql_injection2\n")
|
haproxy_cfg.write(f" http-request deny if is_sql_injection or is_long_uri or semicolon_path or is_sql_injection2\n")
|
||||||
|
|
||||||
# XSS protection - działa w obu trybach
|
# XSS protection
|
||||||
if is_xss:
|
if is_xss:
|
||||||
haproxy_cfg.write(" acl is_xss_attack urlp_reg -i (<|>|script|alert|onerror|onload|javascript)\n")
|
haproxy_cfg.write(" acl is_xss_attack urlp_reg -i (<|>|script|alert|onerror|onload|javascript)\n")
|
||||||
haproxy_cfg.write(" acl is_xss_attack_2 urlp_reg -i (<\\s*script\\s*|javascript:|<\\s*img\\s*src\\s*=|<\\s*a\\s*href\\s*=|<\\s*iframe\\s*src\\s*=|\\bon\\w+\\s*=|<\\s*input\\s*[^>]*\\s*value\\s*=|<\\s*form\\s*action\\s*=|<\\s*svg\\s*on\\w+\\s*=)\n")
|
haproxy_cfg.write(" acl is_xss_attack_2 urlp_reg -i (<\\s*script\\s*|javascript:|<\\s*img\\s*src\\s*=|<\\s*a\\s*href\\s*=|<\\s*iframe\\s*src\\s*=|\\bon\\w+\\s*=|<\\s*input\\s*[^>]*\\s*value\\s*=|<\\s*form\\s*action\\s*=|<\\s*svg\\s*on\\w+\\s*=)\n")
|
||||||
haproxy_cfg.write(" acl is_xss_attack_hdr hdr_reg(Cookie|Referer|User-Agent) -i (<|>|script|alert|onerror|onload|javascript)\n")
|
haproxy_cfg.write(" acl is_xss_attack_hdr hdr_reg(Cookie|Referer|User-Agent) -i (<|>|script|alert|onerror|onload|javascript)\n")
|
||||||
haproxy_cfg.write(f" http-request deny if is_xss_attack or is_xss_attack_2 or is_xss_attack_hdr\n")
|
haproxy_cfg.write(f" http-request deny if is_xss_attack or is_xss_attack_2 or is_xss_attack_hdr\n")
|
||||||
|
|
||||||
# Webshells protection - działa w obu trybach
|
# Webshells protection
|
||||||
if is_webshells:
|
if is_webshells:
|
||||||
haproxy_cfg.write(" acl blocked_webshell path_reg -i /(cmd|shell|backdoor|webshell|phpspy|c99|kacak|b374k|log4j|log4shell|wsos|madspot|malicious|evil).*\\.php.*\n")
|
haproxy_cfg.write(" acl blocked_webshell path_reg -i /(cmd|shell|backdoor|webshell|phpspy|c99|kacak|b374k|log4j|log4shell|wsos|madspot|malicious|evil).*\\.php.*\n")
|
||||||
haproxy_cfg.write(f" http-request deny if blocked_webshell\n")
|
haproxy_cfg.write(f" http-request deny if blocked_webshell\n")
|
||||||
|
|
||||||
|
# ===== BACKEND ROUTING =====
|
||||||
|
if acl_name_sanitized:
|
||||||
|
# Jeśli jest hostname, routuj z ACL
|
||||||
|
haproxy_cfg.write(f" use_backend {backend_name} if {acl_name_sanitized}\n")
|
||||||
|
else:
|
||||||
# Default backend
|
# Default backend
|
||||||
haproxy_cfg.write(f" default_backend {backend_name}\n")
|
haproxy_cfg.write(f" default_backend {backend_name}\n")
|
||||||
|
|
||||||
# Backend section
|
# ===== PRIMARY BACKEND =====
|
||||||
haproxy_cfg.write(f"\nbackend {backend_name}\n")
|
haproxy_cfg.write(f"\nbackend {backend_name}\n")
|
||||||
|
|
||||||
# Balance tylko dla standardowego trybu
|
# Balance tylko dla standardowego trybu
|
||||||
if not is_no_lb:
|
if not is_no_lb:
|
||||||
haproxy_cfg.write(f" balance {lb_method}\n")
|
haproxy_cfg.write(f" balance {lb_method}\n")
|
||||||
|
|
||||||
# Sticky sessions - tylko dla standardowego trybu
|
# Sticky sessions
|
||||||
if sticky_session and not is_no_lb:
|
if sticky_session and not is_no_lb:
|
||||||
if sticky_session_type == "cookie":
|
if sticky_session_type == "cookie":
|
||||||
haproxy_cfg.write(f" cookie SERVERID insert indirect nocache\n")
|
haproxy_cfg.write(f" cookie SERVERID insert indirect nocache\n")
|
||||||
@@ -183,13 +197,13 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
haproxy_cfg.write(f" stick-table type ip size 200k expire 30m\n")
|
haproxy_cfg.write(f" stick-table type ip size 200k expire 30m\n")
|
||||||
haproxy_cfg.write(f" stick on src\n")
|
haproxy_cfg.write(f" stick on src\n")
|
||||||
|
|
||||||
# Health checks - działa w obu trybach
|
# Health checks
|
||||||
if health_check and protocol == 'http':
|
if health_check and protocol == 'http':
|
||||||
haproxy_cfg.write(f" option httpchk GET {health_check_link}\n")
|
haproxy_cfg.write(f" option httpchk GET {health_check_link}\n")
|
||||||
elif health_check_tcp and protocol == 'tcp':
|
elif health_check_tcp and protocol == 'tcp':
|
||||||
haproxy_cfg.write(f" option tcp-check\n")
|
haproxy_cfg.write(f" option tcp-check\n")
|
||||||
|
|
||||||
# Custom headers - działa w obu trybach
|
# Custom headers
|
||||||
if add_header:
|
if add_header:
|
||||||
haproxy_cfg.write(f" http-response add-header {header_name} {header_value}\n")
|
haproxy_cfg.write(f" http-response add-header {header_name} {header_value}\n")
|
||||||
|
|
||||||
@@ -202,15 +216,21 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method,
|
|||||||
else:
|
else:
|
||||||
haproxy_cfg.write(f" server {server_name} {server_ip}:{server_port}{maxconn_str}\n")
|
haproxy_cfg.write(f" server {server_name} {server_ip}:{server_port}{maxconn_str}\n")
|
||||||
|
|
||||||
# ========== REDIRECT FRONTEND (HTTP -> HTTPS) ==========
|
# ===== REDIRECT FRONTEND (HTTP -> HTTPS) =====
|
||||||
if backend_ssl_redirect and ssl_redirect_backend_name:
|
if backend_ssl_redirect and ssl_redirect_backend_name:
|
||||||
# Sprawdź czy taki backend już istnieje
|
|
||||||
if is_backend_exist(ssl_redirect_backend_name):
|
if is_backend_exist(ssl_redirect_backend_name):
|
||||||
return f"Redirect backend {ssl_redirect_backend_name} already exists. Cannot add duplicate."
|
return f"Redirect backend {ssl_redirect_backend_name} already exists. Cannot add duplicate."
|
||||||
|
|
||||||
haproxy_cfg.write(f"\nfrontend redirect_https\n")
|
haproxy_cfg.write(f"\nfrontend redirect_https\n")
|
||||||
haproxy_cfg.write(f" bind {frontend_ip}:{ssl_redirect_port}\n")
|
haproxy_cfg.write(f" bind {frontend_ip}:{ssl_redirect_port}\n")
|
||||||
haproxy_cfg.write(f" mode http\n")
|
haproxy_cfg.write(f" mode http\n")
|
||||||
|
|
||||||
|
# ===== HOSTNAME ACL FOR REDIRECT FRONTEND =====
|
||||||
|
if frontend_hostname:
|
||||||
|
acl_name_redirect = f"is_{sanitize_name(frontend_hostname)}_redirect"
|
||||||
|
haproxy_cfg.write(f" acl {acl_name_redirect} hdr(host) -i {frontend_hostname}\n")
|
||||||
|
haproxy_cfg.write(f" use_backend {ssl_redirect_backend_name} if {acl_name_redirect}\n")
|
||||||
|
else:
|
||||||
haproxy_cfg.write(f" default_backend {ssl_redirect_backend_name}\n")
|
haproxy_cfg.write(f" default_backend {ssl_redirect_backend_name}\n")
|
||||||
|
|
||||||
# Redirect backend
|
# Redirect backend
|
||||||
|
|||||||
Reference in New Issue
Block a user