diff --git a/routes/edit_routes.py b/routes/edit_routes.py index b05024d..bcd385b 100644 --- a/routes/edit_routes.py +++ b/routes/edit_routes.py @@ -32,7 +32,7 @@ def edit_haproxy_config(): if result.returncode == 0: if not out: - out = "Configuration file is valid ✅" + out = "Configuration file is valid" level = "success" if "Warning" in out or "Warnings" in out: level = "warning" diff --git a/routes/main_routes.py b/routes/main_routes.py index 95a5436..017ac3a 100644 --- a/routes/main_routes.py +++ b/routes/main_routes.py @@ -1,11 +1,9 @@ from flask import Blueprint, render_template, request -from auth.auth_middleware import requires_auth # Updated import +from auth.auth_middleware import requires_auth from utils.haproxy_config import update_haproxy_config, is_frontend_exist, count_frontends_and_backends main_bp = Blueprint('main', __name__) - - @main_bp.route('/', methods=['GET', 'POST']) @requires_auth def index(): @@ -16,89 +14,156 @@ def index(): lb_method = request.form['lb_method'] protocol = request.form['protocol'] backend_name = request.form['backend_name'] + + # Header options add_header = 'add_header' in request.form header_name = request.form.get('header_name', '') if add_header else '' header_value = request.form.get('header_value', '') if add_header else '' - - # Get all backend servers data + # Server header removal + del_server_header = 'del_server_header' in request.form + + # Backend servers backend_server_names = request.form.getlist('backend_server_names[]') backend_server_ips = request.form.getlist('backend_server_ips[]') backend_server_ports = request.form.getlist('backend_server_ports[]') backend_server_maxconns = request.form.getlist('backend_server_maxconns[]') - + + # ACL is_acl = 'add_acl' in request.form - acl_name = request.form['acl'] if 'acl' in request.form else '' - acl_action = request.form['acl_action'] if 'acl_action' in request.form else '' - acl_backend_name = request.form['backend_name_acl'] if 'backend_name_acl' in request.form else '' + acl_name = request.form.get('acl', '') + acl_action = request.form.get('acl_action', '') + acl_backend_name = request.form.get('backend_name_acl', '') + + # SSL use_ssl = 'ssl_checkbox' in request.form - ssl_cert_path = request.form['ssl_cert_path'] + ssl_cert_path = request.form.get('ssl_cert_path', '/etc/haproxy/certs/haproxy.pem') https_redirect = 'ssl_redirect_checkbox' in request.form - is_dos = 'add_dos' in request.form if 'add_dos' in request.form else '' - ban_duration = request.form["ban_duration"] - limit_requests = request.form["limit_requests"] + + # DOS Protection + is_dos = 'add_dos' in request.form + ban_duration = request.form.get('ban_duration', '30m') + limit_requests = request.form.get('limit_requests', '100') + + # Forward For forward_for = 'forward_for_check' in request.form - + + # Forbidden paths is_forbidden_path = 'add_acl_path' in request.form - forbidden_name = request.form["forbidden_name"] - allowed_ip = request.form["allowed_ip"] - forbidden_path = request.form["forbidden_path"] - - sql_injection_check = 'sql_injection_check' in request.form if 'sql_injection_check' in request.form else '' - is_xss = 'xss_check' in request.form if 'xss_check' in request.form else '' - is_remote_upload = 'remote_uploads_check' in request.form if 'remote_uploads_check' in request.form else '' - + forbidden_name = request.form.get('forbidden_name', '') + allowed_ip = request.form.get('allowed_ip', '') + forbidden_path = request.form.get('forbidden_path', '') + + # SQL Injection + sql_injection_check = 'sql_injection_check' in request.form + + # XSS + is_xss = 'xss_check' in request.form + + # Remote uploads + is_remote_upload = 'remote_uploads_check' in request.form + + # Path-based redirects add_path_based = 'add_path_based' in request.form - redirect_domain_name = request.form["redirect_domain_name"] - root_redirect = request.form["root_redirect"] - redirect_to = request.form["redirect_to"] - is_webshells = 'webshells_check' in request.form if 'webshells_check' in request.form else '' - - # Combine backend server info into a list of tuples (name, ip, port, maxconns) + redirect_domain_name = request.form.get('redirect_domain_name', '') + root_redirect = request.form.get('root_redirect', '') + redirect_to = request.form.get('redirect_to', '') + + # Webshells + is_webshells = 'webshells_check' in request.form + + # Build backend_servers list backend_servers = [] for i in range(len(backend_server_ips)): name = backend_server_names[i] if i < len(backend_server_names) else f"server{i+1}" ip = backend_server_ips[i] if i < len(backend_server_ips) else '' port = backend_server_ports[i] if i < len(backend_server_ports) else '' maxconn = backend_server_maxconns[i] if i < len(backend_server_maxconns) else None - - if ip and port: # Only add if we have IP and port + + if ip and port: backend_servers.append((name, ip, port, maxconn)) - # Check if frontend or port already exists + # Validate frontend existence if is_frontend_exist(frontend_name, frontend_ip, frontend_port): - return render_template('index.html', message="Frontend or Port already exists. Cannot add duplicate.") - - # Get health check related fields if the protocol is HTTP + return render_template('index.html', + message="Frontend or Port already exists. Cannot add duplicate.", + message_type="danger") + + # Health checks health_check = False health_check_link = "" if protocol == 'http': health_check = 'health_check' in request.form if health_check: - health_check_link = request.form['health_check_link'] - + health_check_link = request.form.get('health_check_link', '/') + health_check_tcp = False if protocol == 'tcp': health_check_tcp = 'health_check2' in request.form - - # Get sticky session related fields + + # Sticky session sticky_session = False sticky_session_type = "" if 'sticky_session' in request.form: sticky_session = True - sticky_session_type = request.form['sticky_session_type'] - - # Update the HAProxy config file + sticky_session_type = request.form.get('sticky_session_type', 'cookie') + + # Call update_haproxy_config with all parameters message = 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, - add_header, header_name, header_value, sticky_session_type, is_acl, acl_name, - acl_action, acl_backend_name, use_ssl, ssl_cert_path, https_redirect, is_dos, - ban_duration, limit_requests, forward_for, is_forbidden_path, forbidden_name, - allowed_ip, forbidden_path, sql_injection_check, is_xss, is_remote_upload, - add_path_based, redirect_domain_name, root_redirect, redirect_to, is_webshells + frontend_name=frontend_name, + frontend_ip=frontend_ip, + frontend_port=frontend_port, + lb_method=lb_method, + protocol=protocol, + backend_name=backend_name, + backend_servers=backend_servers, + health_check=health_check, + health_check_tcp=health_check_tcp, + health_check_link=health_check_link, + sticky_session=sticky_session, + add_header=add_header, + header_name=header_name, + header_value=header_value, + sticky_session_type=sticky_session_type, + is_acl=is_acl, + acl_name=acl_name, + acl_action=acl_action, + acl_backend_name=acl_backend_name, + use_ssl=use_ssl, + ssl_cert_path=ssl_cert_path, + https_redirect=https_redirect, + is_dos=is_dos, + ban_duration=ban_duration, + limit_requests=limit_requests, + forward_for=forward_for, + is_forbidden_path=is_forbidden_path, + forbidden_name=forbidden_name, + allowed_ip=allowed_ip, + forbidden_path=forbidden_path, + sql_injection_check=sql_injection_check, + is_xss=is_xss, + is_remote_upload=is_remote_upload, + add_path_based=add_path_based, + redirect_domain_name=redirect_domain_name, + root_redirect=root_redirect, + redirect_to=redirect_to, + is_webshells=is_webshells, + del_server_header=del_server_header ) - return render_template('index.html', message=message) - - return render_template('index.html') - + + # Determine message type + message_type = "success" if "successfully" in message else "danger" + + return render_template('index.html', + message=message, + message_type=message_type) + + # GET request - display stats + frontend_count, backend_count, acl_count, layer7_count, layer4_count = count_frontends_and_backends() + + return render_template('index.html', + frontend_count=frontend_count, + backend_count=backend_count, + acl_count=acl_count, + layer7_count=layer7_count, + layer4_count=layer4_count) diff --git a/static/js/form.js b/static/js/form.js new file mode 100644 index 0000000..e5e6443 --- /dev/null +++ b/static/js/form.js @@ -0,0 +1,191 @@ +(() => { + 'use strict'; + + // Toggle health check fields based on protocol + const protocolSelect = document.getElementById('protocol'); + const healthCheckFields = document.getElementById('health_check_fields'); + const tcpHealthCheck = document.getElementById('tcp_health_check'); + + const onProtocolChange = () => { + if (protocolSelect?.value === 'http') { + document.getElementById('health_check')?.parentElement.parentElement.style.display = 'block'; + tcpHealthCheck.style.display = 'none'; + } else { + document.getElementById('health_check')?.parentElement.parentElement.style.display = 'none'; + tcpHealthCheck.style.display = 'flex'; + } + }; + + protocolSelect?.addEventListener('change', onProtocolChange); + + // Toggle sticky session fields + const stickyCheckbox = document.getElementById('sticky_session'); + const stickyFields = document.getElementById('sticky_fields'); + + stickyCheckbox?.addEventListener('change', function() { + stickyFields.classList.toggle('d-none', !this.checked); + }); + + // Toggle health check link field + const healthCheckbox = document.getElementById('health_check'); + healthCheckbox?.addEventListener('change', function() { + document.getElementById('health_check_fields')?.classList.toggle('d-none', !this.checked); + }); + + // Toggle header fields + const headerCheckbox = document.getElementById('add_header'); + const headerFields = document.querySelectorAll('#header_fields'); + + headerCheckbox?.addEventListener('change', function() { + headerFields.forEach(field => field.classList.toggle('d-none', !this.checked)); + }); + + // Helper functions + const $ = (sel, root = document) => root.querySelector(sel); + const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel)); + const toggle = (on, el) => el.classList.toggle('d-none', !on); + + // SSL fields + const sslCheckbox = $('#ssl_checkbox'); + const sslFields = $('#ssl_fields'); + sslCheckbox?.addEventListener('change', () => toggle(sslCheckbox.checked, sslFields)); + + // DOS + const dosCheckbox = $('#add_dos'); + const dosFields = $('#dos_fields'); + dosCheckbox?.addEventListener('change', () => toggle(dosCheckbox.checked, dosFields)); + + // HTTP only groups + const httpGroups = $$('.http-only, #forbidden_acl_container'); + const httpToggles = [ + $('#sql_injection_check'), + $('#xss_check'), + $('#remote_uploads_check'), + $('#webshells_check'), + $('#forward_for_check'), + $('#add_acl_path'), + $('#add_path_based'), + ]; + + const forbiddenFields = $('#forbidden_fields'); + const pathFields = $('#base_redirect_fields'); + + const onProtocolChangeExtended = () => { + const isHttp = protocolSelect?.value === 'http'; + httpGroups.forEach(el => toggle(isHttp, el)); + + if (!isHttp) { + [forbiddenFields, pathFields].forEach(el => el && toggle(false, el)); + httpToggles.forEach(input => { + if (input) input.checked = false; + }); + } + }; + + protocolSelect?.addEventListener('change', onProtocolChangeExtended); + onProtocolChangeExtended(); + + // ACL + const aclCheckbox = $('#add_acl'); + const aclFields = $('#acl_fields'); + aclCheckbox?.addEventListener('change', () => toggle(aclCheckbox.checked, aclFields)); + + // toggles that reveal their fields + const bindToggle = (checkboxSel, targetSel) => { + const cb = $(checkboxSel); + const target = $(targetSel); + cb?.addEventListener('change', () => toggle(cb.checked, target)); + if (cb && target) toggle(cb.checked, target); + }; + + bindToggle('#add_path_based', '#base_redirect_fields'); + bindToggle('#add_acl_path', '#forbidden_fields'); + + // LB Method - obsługa trybu no-lb + const lbMethodSelect = $('#lb_method'); + const backendServersContainer = $('#backend_servers_container'); + const addServerBtn = $('#add_backend_btn'); + + const onLbMethodChange = () => { + const isNoLb = lbMethodSelect?.value === 'no-lb'; + + if (isNoLb) { + // Ukryj przycisk dodawania kolejnych serwerów + if (addServerBtn) addServerBtn.classList.add('d-none'); + + // Zostaw tylko pierwszy serwer i usuń pozostałe + const serverRows = $$('.backend-server-row', backendServersContainer); + serverRows.forEach((row, idx) => { + if (idx > 0) row.remove(); + }); + + // Dodaj informację o trybie no-lb jeśli jeszcze nie istnieje + if (!$('.no-lb-info')) { + const info = document.createElement('div'); + info.className = 'alert alert-info alert-sm no-lb-info mt-2'; + info.innerHTML = 'Tryb no-lb: frontend → backend → pojedynczy serwer. Możesz włączyć XSS, DOS, SQL injection protection itp.'; + if (backendServersContainer?.parentElement) { + backendServersContainer.parentElement.appendChild(info); + } + } + } else { + // Pokaż przycisk dodawania serwerów + if (addServerBtn) addServerBtn.classList.remove('d-none'); + + // Usuń informację o no-lb + const info = $('.no-lb-info'); + if (info) info.remove(); + } + }; + + lbMethodSelect?.addEventListener('change', onLbMethodChange); + if (lbMethodSelect) onLbMethodChange(); + + // Backend rows + let serverCount = 1; + const container = $('#backend_servers_container'); + const addBtn = $('#add_backend_btn'); + + const createRow = () => { + serverCount++; + const row = document.createElement('div'); + row.className = 'row g-3 backend-server-row mt-1'; + row.innerHTML = ` +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ `; + + const removeBtn = row.querySelector('.remove-server'); + removeBtn.addEventListener('click', () => row.remove()); + + return row; + }; + + addBtn?.addEventListener('click', () => { + if (container) { + container.appendChild(createRow()); + } + }); + + // Remove button for dynamically added rows + container?.addEventListener('click', (e) => { + if (e.target.closest('.remove-server')) { + e.target.closest('.backend-server-row').remove(); + } + }); +})(); diff --git a/static/js/index.js b/static/js/index.js index cdb311c..4b85071 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -1,105 +1,150 @@ (() => { - 'use strict'; - - const $ = (sel, root=document) => root.querySelector(sel); - const $$ = (sel, root=document) => Array.from(root.querySelectorAll(sel)); - - // SSL fields - const sslCheckbox = $('#ssl_checkbox'); - const sslFields = $('#ssl_fields'); - - const toggle = (on, el) => el.classList.toggle('d-none', !on); - - sslCheckbox?.addEventListener('change', () => toggle(sslCheckbox.checked, sslFields)); - - // DOS - const dosCheckbox = $('#add_dos'); - const dosFields = $('#dos_fields'); - dosCheckbox?.addEventListener('change', () => toggle(dosCheckbox.checked, dosFields)); - - // HTTP only groups - const protocolSelect = $('#protocol'); - const httpGroups = $$('.http-only, #forbidden_acl_container'); - const httpToggles = [ - $('#sql_injection_check'), - $('#xss_check'), - $('#remote_uploads_check'), - $('#webshells_check'), - $('#forward_for_check'), - $('#add_acl_path'), - $('#add_path_based'), - ]; - const forbiddenFields = $('#forbidden_fields'); - const pathFields = $('#base_redirect_fields'); - - const onProtocolChange = () => { - const isHttp = protocolSelect?.value === 'http'; - httpGroups.forEach(el => toggle(isHttp, el)); - if (!isHttp) { - // hide optional groups if protocol != http - [forbiddenFields, pathFields].forEach(el => el && toggle(false, el)); - httpToggles.forEach(input => { if (input) input.checked = false; }); - } - }; - protocolSelect?.addEventListener('change', onProtocolChange); - onProtocolChange(); - - // ACL - const aclCheckbox = $('#add_acl'); - const aclFields = $('#acl_fields'); - aclCheckbox?.addEventListener('change', () => toggle(aclCheckbox.checked, aclFields)); - - // toggles that reveal their fields - const bindToggle = (checkboxSel, targetSel) => { - const cb = $(checkboxSel); - const target = $(targetSel); - cb?.addEventListener('change', () => toggle(cb.checked, target)); - // initial - if (cb && target) toggle(cb.checked, target); - }; - bindToggle('#add_path_based', '#base_redirect_fields'); - bindToggle('#add_acl_path', '#forbidden_fields'); - - // Backend rows - let serverCount = 1; - const container = $('#backend_servers_container'); - const addBtn = $('#add_backend_btn'); - - const createRow = () => { - serverCount++; - const row = document.createElement('div'); - row.className = 'row g-3 backend-server-row mt-1'; - row.innerHTML = ` -
- - -
-
- - -
-
- - -
-
- -
- - -
-
`; - row.querySelector('button.btn-danger')?.addEventListener('click', () => { - const rows = $$('.backend-server-row'); - if (rows.length > 1) row.remove(); - else alert('Musi istnieć co najmniej jeden backend.'); + 'use strict'; + + const $ = (sel, root=document) => root.querySelector(sel); + const $$ = (sel, root=document) => Array.from(root.querySelectorAll(sel)); + + // SSL fields + const sslCheckbox = $('#ssl_checkbox'); + const sslFields = $('#ssl_fields'); + const toggle = (on, el) => el.classList.toggle('d-none', !on); + + sslCheckbox?.addEventListener('change', () => toggle(sslCheckbox.checked, sslFields)); + + // DOS + const dosCheckbox = $('#add_dos'); + const dosFields = $('#dos_fields'); + dosCheckbox?.addEventListener('change', () => toggle(dosCheckbox.checked, dosFields)); + + // HTTP only groups + const protocolSelect = $('#protocol'); + const httpGroups = $$('.http-only, #forbidden_acl_container'); + const httpToggles = [ + $('#sql_injection_check'), + $('#xss_check'), + $('#remote_uploads_check'), + $('#webshells_check'), + $('#forward_for_check'), + $('#add_acl_path'), + $('#add_path_based'), + ]; + + const forbiddenFields = $('#forbidden_fields'); + const pathFields = $('#base_redirect_fields'); + + const onProtocolChange = () => { + const isHttp = protocolSelect?.value === 'http'; + httpGroups.forEach(el => toggle(isHttp, el)); + + if (!isHttp) { + // hide optional groups if protocol != http + [forbiddenFields, pathFields].forEach(el => el && toggle(false, el)); + httpToggles.forEach(input => { + if (input) input.checked = false; + }); + } + }; + + protocolSelect?.addEventListener('change', onProtocolChange); + onProtocolChange(); + + // ACL + const aclCheckbox = $('#add_acl'); + const aclFields = $('#acl_fields'); + aclCheckbox?.addEventListener('change', () => toggle(aclCheckbox.checked, aclFields)); + + // toggles that reveal their fields + const bindToggle = (checkboxSel, targetSel) => { + const cb = $(checkboxSel); + const target = $(targetSel); + cb?.addEventListener('change', () => toggle(cb.checked, target)); + // initial + if (cb && target) toggle(cb.checked, target); + }; + + bindToggle('#add_path_based', '#base_redirect_fields'); + bindToggle('#add_acl_path', '#forbidden_fields'); + + const lbMethodSelect = $('#lb_method'); + const backendServersContainer = $('#backend_servers_container'); + const addServerBtn = $('#add_backend_btn'); + + const onLbMethodChange = () => { + const isNoLb = lbMethodSelect?.value === 'no-lb'; + + if (isNoLb) { + if (addServerBtn) addServerBtn.classList.add('d-none'); + + const serverRows = $$('.backend-server-row', backendServersContainer); + serverRows.forEach((row, idx) => { + if (idx > 0) row.remove(); + }); + + if (!$('.no-lb-info')) { + const info = document.createElement('div'); + info.className = 'alert alert-info alert-sm no-lb-info mt-2'; + info.innerHTML = 'Tryb no-lb: frontend → backend → pojedynczy serwer. Możesz włączyć XSS, DOS, SQL injection protection itp.'; + if (backendServersContainer?.parentElement) { + backendServersContainer.parentElement.appendChild(info); + } + } + } else { + if (addServerBtn) addServerBtn.classList.remove('d-none'); + + const info = $('.no-lb-info'); + if (info) info.remove(); + } + }; + + lbMethodSelect?.addEventListener('change', onLbMethodChange); + // Wywołaj na starcie + if (lbMethodSelect) onLbMethodChange(); + + // Backend rows + let serverCount = 1; + const container = $('#backend_servers_container'); + const addBtn = $('#add_backend_btn'); + + const createRow = () => { + serverCount++; + const row = document.createElement('div'); + row.className = 'row g-3 backend-server-row mt-1'; + row.innerHTML = ` +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ `; + + const removeBtn = row.querySelector('.remove-server'); + removeBtn.addEventListener('click', () => row.remove()); + + return row; + }; + + addBtn?.addEventListener('click', () => { + if (container) { + container.appendChild(createRow()); + } + }); + + // Remove button for dynamically added rows + container?.addEventListener('click', (e) => { + if (e.target.closest('.remove-server')) { + e.target.closest('.backend-server-row').remove(); + } }); - return row; - }; - addBtn?.addEventListener('click', () => container?.appendChild(createRow())); - - // auto dismiss alerts - setTimeout(() => $$('.alert').forEach(a => { - if (typeof bootstrap !== 'undefined') new bootstrap.Alert(a).close(); - }), 5000); })(); diff --git a/templates/index.html b/templates/index.html index dcba33a..96b0ade 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,220 +1,453 @@ {% extends "base.html" %} + {% set active_page = "index" %} -{% block title %}HAProxy • Index{% endblock %} -{% block breadcrumb %}{% endblock %} + +{% block title %}HAProxy • Configuration{% endblock %} + +{% block breadcrumb %}Configuration{% endblock %} + {% block content %} -
-
-
-
New frontend
- {% if message %} - - {% endif %} -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
-
- - -
-
-
- -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- -
-
-
-
-
- - -
+
+
+
+
+
{{ frontend_count|default(0) }}
+

Frontends

-
-
- - -
+
+
+
+
+
+
{{ backend_count|default(0) }}
+

Backends

-
-
- - -
+
+
+
+
+
+
{{ acl_count|default(0) }}
+

ACLs

-
-
- - -
+
+
+
+
+
+
L7: {{ layer7_count|default(0) }} / L4: {{ layer4_count|default(0) }}
+

Layers

-
-
- - -
-
-
-
- -
- - -
-
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- - -
-
- -
- -
Backend pool
-
-
- - -
-
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - -
- -
+
-{% endblock %} -{% block page_js %} + +{% if message %} + +{% endif %} + +
+
+
+
Add New Configuration
+
+
+ + +
Frontend
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
Choose load balancing algorithm or simple single host
+
+
+ + +
+
+
+ + +
+
+
+ +
+
+ + +
Full path to .pem file
+
+
+
+ + +
+
+
+ +
+ + +
Backend
+ +
+
+ + +
+
+ + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + +
+
+
+ + +
+
+
+ +
+
+ +
+ + +
Headers & Security
+ + +
+
+
+ + +
+
+
+ +
+
+ +
+
+ + +
+
+
+ + +
+ Adds: http-response del-header Server (security) +
+
+
+
+ + +
+
+
+ + +
+
+
+ +
+ + +
Protection
+ + +
+
+
+ + +
+
+
+ + +
e.g. 30m, 1h, 24h
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ +
+ + +
ACL & Routing
+ +
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ + +
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ + +
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+ +
+ +
+
+
+ + + {% endblock %} diff --git a/utils/haproxy_config.py b/utils/haproxy_config.py index a55174c..9b054c6 100644 --- a/utils/haproxy_config.py +++ b/utils/haproxy_config.py @@ -3,7 +3,6 @@ import os HAPROXY_CFG = '/etc/haproxy/haproxy.cfg' def is_frontend_exist(frontend_name, frontend_ip, frontend_port): - """Check if frontend with given name, IP and port already exists""" if not os.path.exists(HAPROXY_CFG): return False @@ -28,7 +27,6 @@ def is_frontend_exist(frontend_name, frontend_ip, frontend_port): return False def is_backend_exist(backend_name): - """Check if backend with given name already exists""" if not os.path.exists(HAPROXY_CFG): return False @@ -45,8 +43,52 @@ def is_backend_exist(backend_name): return False +def update_simple_haproxy_config(frontend_name, frontend_host, use_ssl, ssl_cert_path, + backend_name, backend_ip, backend_port, + forward_for=True, del_server_header=True): + """ + Tworzy prostą konfigurację frontend->backend bez load balancingu + """ + os.makedirs(os.path.dirname(HAPROXY_CFG), exist_ok=True) + + if is_backend_exist(backend_name): + return f"Backend {backend_name} already exists. Cannot add duplicate." + + try: + with open(HAPROXY_CFG, 'a') as haproxy_cfg: + # Frontend section + haproxy_cfg.write(f"\nfrontend {frontend_name}\n") + + if use_ssl: + haproxy_cfg.write(f" bind :443 ssl crt {ssl_cert_path}\n") + else: + haproxy_cfg.write(f" bind :80\n") + + # Headers + if forward_for: + haproxy_cfg.write(f" http-request set-header X-Forwarded-For %[src]\n") + haproxy_cfg.write(f" http-request set-header X-Forwarded-Proto {'https' if use_ssl else 'http'}\n") + + if del_server_header: + haproxy_cfg.write(f" http-response del-header Server\n") + + # ACL dla hosta + haproxy_cfg.write(f"\n acl host_{backend_name} hdr(host) -i {frontend_host}\n") + haproxy_cfg.write(f" use_backend {backend_name} if host_{backend_name}\n") + + # Backend section + haproxy_cfg.write(f"\nbackend {backend_name}\n") + haproxy_cfg.write(f" server s1 {backend_ip}:{backend_port} check\n") + + return "Configuration updated successfully!" + + except Exception as e: + print(f"[HAPROXY_CONFIG] Error updating simple config: {e}", flush=True) + return f"Error: {e}" + + + def count_frontends_and_backends(): - """Count frontends, backends, ACLs and layer types""" if not os.path.exists(HAPROXY_CFG): return 0, 0, 0, 0, 0 @@ -86,7 +128,6 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, allowed_ip, forbidden_path, sql_injection_check, is_xss, is_remote_upload, add_path_based, redirect_domain_name, root_redirect, redirect_to, is_webshells): - # Ensure directory exists os.makedirs(os.path.dirname(HAPROXY_CFG), exist_ok=True) if is_backend_exist(backend_name): @@ -114,7 +155,6 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, haproxy_cfg.write(f" mode {protocol}\n") haproxy_cfg.write(f" balance {lb_method}\n") - # Add protection rules 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" http-request track-sc0 src\n") @@ -122,7 +162,6 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, haproxy_cfg.write(f" http-request silent-drop if abuse\n") if sql_injection_check: - # POPRAWNE escape sequence'i - podwójny backslash dla haproxy 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 semicolon_path path_reg -i ^.*;.*\n") diff --git a/utils/stats_utils.py b/utils/stats_utils.py index 4d2ddb6..38c9f47 100644 --- a/utils/stats_utils.py +++ b/utils/stats_utils.py @@ -1,7 +1,7 @@ import requests import csv -HAPROXY_STATS_URL = 'http://127.0.0.1:8484/;csv' +HAPROXY_STATS_URL = 'http://127.0.0.1:8404/;csv' def fetch_haproxy_stats(): try: