new_functions_and_fixes #1
@@ -32,7 +32,7 @@ def edit_haproxy_config():
|
|||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
if not out:
|
if not out:
|
||||||
out = "Configuration file is valid ✅"
|
out = "Configuration file is valid"
|
||||||
level = "success"
|
level = "success"
|
||||||
if "Warning" in out or "Warnings" in out:
|
if "Warning" in out or "Warnings" in out:
|
||||||
level = "warning"
|
level = "warning"
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
from flask import Blueprint, render_template, request
|
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
|
from utils.haproxy_config import update_haproxy_config, is_frontend_exist, count_frontends_and_backends
|
||||||
|
|
||||||
main_bp = Blueprint('main', __name__)
|
main_bp = Blueprint('main', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route('/', methods=['GET', 'POST'])
|
@main_bp.route('/', methods=['GET', 'POST'])
|
||||||
@requires_auth
|
@requires_auth
|
||||||
def index():
|
def index():
|
||||||
@@ -16,45 +14,65 @@ def index():
|
|||||||
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']
|
||||||
|
|
||||||
|
# Header options
|
||||||
add_header = 'add_header' in request.form
|
add_header = 'add_header' in request.form
|
||||||
header_name = request.form.get('header_name', '') if add_header else ''
|
header_name = request.form.get('header_name', '') if add_header else ''
|
||||||
header_value = request.form.get('header_value', '') if add_header else ''
|
header_value = request.form.get('header_value', '') if add_header else ''
|
||||||
|
|
||||||
|
# Server header removal
|
||||||
|
del_server_header = 'del_server_header' in request.form
|
||||||
|
|
||||||
# Get all backend servers data
|
# Backend servers
|
||||||
backend_server_names = request.form.getlist('backend_server_names[]')
|
backend_server_names = request.form.getlist('backend_server_names[]')
|
||||||
backend_server_ips = request.form.getlist('backend_server_ips[]')
|
backend_server_ips = request.form.getlist('backend_server_ips[]')
|
||||||
backend_server_ports = request.form.getlist('backend_server_ports[]')
|
backend_server_ports = request.form.getlist('backend_server_ports[]')
|
||||||
backend_server_maxconns = request.form.getlist('backend_server_maxconns[]')
|
backend_server_maxconns = request.form.getlist('backend_server_maxconns[]')
|
||||||
|
|
||||||
|
# ACL
|
||||||
is_acl = 'add_acl' in request.form
|
is_acl = 'add_acl' in request.form
|
||||||
acl_name = request.form['acl'] if 'acl' in request.form else ''
|
acl_name = request.form.get('acl', '')
|
||||||
acl_action = request.form['acl_action'] if 'acl_action' in request.form else ''
|
acl_action = request.form.get('acl_action', '')
|
||||||
acl_backend_name = request.form['backend_name_acl'] if 'backend_name_acl' in request.form else ''
|
acl_backend_name = request.form.get('backend_name_acl', '')
|
||||||
|
|
||||||
|
# SSL
|
||||||
use_ssl = 'ssl_checkbox' in request.form
|
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
|
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"]
|
# DOS Protection
|
||||||
limit_requests = request.form["limit_requests"]
|
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
|
forward_for = 'forward_for_check' in request.form
|
||||||
|
|
||||||
|
# Forbidden paths
|
||||||
is_forbidden_path = 'add_acl_path' in request.form
|
is_forbidden_path = 'add_acl_path' in request.form
|
||||||
forbidden_name = request.form["forbidden_name"]
|
forbidden_name = request.form.get('forbidden_name', '')
|
||||||
allowed_ip = request.form["allowed_ip"]
|
allowed_ip = request.form.get('allowed_ip', '')
|
||||||
forbidden_path = request.form["forbidden_path"]
|
forbidden_path = request.form.get('forbidden_path', '')
|
||||||
|
|
||||||
sql_injection_check = 'sql_injection_check' in request.form if 'sql_injection_check' in request.form else ''
|
# SQL Injection
|
||||||
is_xss = 'xss_check' in request.form if 'xss_check' in request.form else ''
|
sql_injection_check = 'sql_injection_check' in request.form
|
||||||
is_remote_upload = 'remote_uploads_check' in request.form if 'remote_uploads_check' in request.form else ''
|
|
||||||
|
|
||||||
|
# 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
|
add_path_based = 'add_path_based' in request.form
|
||||||
redirect_domain_name = request.form["redirect_domain_name"]
|
redirect_domain_name = request.form.get('redirect_domain_name', '')
|
||||||
root_redirect = request.form["root_redirect"]
|
root_redirect = request.form.get('root_redirect', '')
|
||||||
redirect_to = request.form["redirect_to"]
|
redirect_to = request.form.get('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)
|
# Webshells
|
||||||
|
is_webshells = 'webshells_check' in request.form
|
||||||
|
|
||||||
|
# Build backend_servers list
|
||||||
backend_servers = []
|
backend_servers = []
|
||||||
for i in range(len(backend_server_ips)):
|
for i in range(len(backend_server_ips)):
|
||||||
name = backend_server_names[i] if i < len(backend_server_names) else f"server{i+1}"
|
name = backend_server_names[i] if i < len(backend_server_names) else f"server{i+1}"
|
||||||
@@ -62,43 +80,90 @@ def index():
|
|||||||
port = backend_server_ports[i] if i < len(backend_server_ports) 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
|
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))
|
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):
|
if is_frontend_exist(frontend_name, frontend_ip, frontend_port):
|
||||||
return render_template('index.html', message="Frontend or Port already exists. Cannot add duplicate.")
|
return render_template('index.html',
|
||||||
|
message="Frontend or Port already exists. Cannot add duplicate.",
|
||||||
|
message_type="danger")
|
||||||
|
|
||||||
# Get health check related fields if the protocol is HTTP
|
# Health checks
|
||||||
health_check = False
|
health_check = False
|
||||||
health_check_link = ""
|
health_check_link = ""
|
||||||
if protocol == 'http':
|
if protocol == 'http':
|
||||||
health_check = 'health_check' in request.form
|
health_check = 'health_check' in request.form
|
||||||
if health_check:
|
if health_check:
|
||||||
health_check_link = request.form['health_check_link']
|
health_check_link = request.form.get('health_check_link', '/')
|
||||||
|
|
||||||
health_check_tcp = False
|
health_check_tcp = False
|
||||||
if protocol == 'tcp':
|
if protocol == 'tcp':
|
||||||
health_check_tcp = 'health_check2' in request.form
|
health_check_tcp = 'health_check2' in request.form
|
||||||
|
|
||||||
# Get sticky session related fields
|
# Sticky session
|
||||||
sticky_session = False
|
sticky_session = False
|
||||||
sticky_session_type = ""
|
sticky_session_type = ""
|
||||||
if 'sticky_session' in request.form:
|
if 'sticky_session' in request.form:
|
||||||
sticky_session = True
|
sticky_session = True
|
||||||
sticky_session_type = request.form['sticky_session_type']
|
sticky_session_type = request.form.get('sticky_session_type', 'cookie')
|
||||||
|
|
||||||
# Update the HAProxy config file
|
# Call update_haproxy_config with all parameters
|
||||||
message = update_haproxy_config(
|
message = update_haproxy_config(
|
||||||
frontend_name, frontend_ip, frontend_port, lb_method, protocol, backend_name,
|
frontend_name=frontend_name,
|
||||||
backend_servers, health_check, health_check_tcp, health_check_link, sticky_session,
|
frontend_ip=frontend_ip,
|
||||||
add_header, header_name, header_value, sticky_session_type, is_acl, acl_name,
|
frontend_port=frontend_port,
|
||||||
acl_action, acl_backend_name, use_ssl, ssl_cert_path, https_redirect, is_dos,
|
lb_method=lb_method,
|
||||||
ban_duration, limit_requests, forward_for, is_forbidden_path, forbidden_name,
|
protocol=protocol,
|
||||||
allowed_ip, forbidden_path, sql_injection_check, is_xss, is_remote_upload,
|
backend_name=backend_name,
|
||||||
add_path_based, redirect_domain_name, root_redirect, redirect_to, is_webshells
|
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)
|
||||||
|
|||||||
191
static/js/form.js
Normal file
191
static/js/form.js
Normal file
@@ -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 = '<i class="bi bi-info-circle me-2"></i><small>Tryb <strong>no-lb</strong>: frontend → backend → pojedynczy serwer. Możesz włączyć XSS, DOS, SQL injection protection itp.</small>';
|
||||||
|
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 = `
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="text" class="form-control" name="backend_server_names[]" placeholder="server${serverCount}" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" class="form-control" name="backend_server_ips[]" placeholder="192.168.1.${serverCount}" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="number" class="form-control" name="backend_server_ports[]" placeholder="80" min="1" max="65535" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="number" class="form-control" name="backend_server_maxconns[]" placeholder="100">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1">
|
||||||
|
<button type="button" class="btn btn-danger btn-sm w-100 remove-server">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -1,105 +1,150 @@
|
|||||||
(() => {
|
(() => {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const $ = (sel, root=document) => root.querySelector(sel);
|
const $ = (sel, root=document) => root.querySelector(sel);
|
||||||
const $$ = (sel, root=document) => Array.from(root.querySelectorAll(sel));
|
const $$ = (sel, root=document) => Array.from(root.querySelectorAll(sel));
|
||||||
|
|
||||||
// SSL fields
|
// SSL fields
|
||||||
const sslCheckbox = $('#ssl_checkbox');
|
const sslCheckbox = $('#ssl_checkbox');
|
||||||
const sslFields = $('#ssl_fields');
|
const sslFields = $('#ssl_fields');
|
||||||
|
const toggle = (on, el) => el.classList.toggle('d-none', !on);
|
||||||
|
|
||||||
const toggle = (on, el) => el.classList.toggle('d-none', !on);
|
sslCheckbox?.addEventListener('change', () => toggle(sslCheckbox.checked, sslFields));
|
||||||
|
|
||||||
sslCheckbox?.addEventListener('change', () => toggle(sslCheckbox.checked, sslFields));
|
// DOS
|
||||||
|
const dosCheckbox = $('#add_dos');
|
||||||
|
const dosFields = $('#dos_fields');
|
||||||
|
dosCheckbox?.addEventListener('change', () => toggle(dosCheckbox.checked, dosFields));
|
||||||
|
|
||||||
// DOS
|
// HTTP only groups
|
||||||
const dosCheckbox = $('#add_dos');
|
const protocolSelect = $('#protocol');
|
||||||
const dosFields = $('#dos_fields');
|
const httpGroups = $$('.http-only, #forbidden_acl_container');
|
||||||
dosCheckbox?.addEventListener('change', () => toggle(dosCheckbox.checked, dosFields));
|
const httpToggles = [
|
||||||
|
$('#sql_injection_check'),
|
||||||
|
$('#xss_check'),
|
||||||
|
$('#remote_uploads_check'),
|
||||||
|
$('#webshells_check'),
|
||||||
|
$('#forward_for_check'),
|
||||||
|
$('#add_acl_path'),
|
||||||
|
$('#add_path_based'),
|
||||||
|
];
|
||||||
|
|
||||||
// HTTP only groups
|
const forbiddenFields = $('#forbidden_fields');
|
||||||
const protocolSelect = $('#protocol');
|
const pathFields = $('#base_redirect_fields');
|
||||||
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 onProtocolChange = () => {
|
||||||
const isHttp = protocolSelect?.value === 'http';
|
const isHttp = protocolSelect?.value === 'http';
|
||||||
httpGroups.forEach(el => toggle(isHttp, el));
|
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
|
if (!isHttp) {
|
||||||
const aclCheckbox = $('#add_acl');
|
// hide optional groups if protocol != http
|
||||||
const aclFields = $('#acl_fields');
|
[forbiddenFields, pathFields].forEach(el => el && toggle(false, el));
|
||||||
aclCheckbox?.addEventListener('change', () => toggle(aclCheckbox.checked, aclFields));
|
httpToggles.forEach(input => {
|
||||||
|
if (input) input.checked = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// toggles that reveal their fields
|
protocolSelect?.addEventListener('change', onProtocolChange);
|
||||||
const bindToggle = (checkboxSel, targetSel) => {
|
onProtocolChange();
|
||||||
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
|
// ACL
|
||||||
let serverCount = 1;
|
const aclCheckbox = $('#add_acl');
|
||||||
const container = $('#backend_servers_container');
|
const aclFields = $('#acl_fields');
|
||||||
const addBtn = $('#add_backend_btn');
|
aclCheckbox?.addEventListener('change', () => toggle(aclCheckbox.checked, aclFields));
|
||||||
|
|
||||||
const createRow = () => {
|
// toggles that reveal their fields
|
||||||
serverCount++;
|
const bindToggle = (checkboxSel, targetSel) => {
|
||||||
const row = document.createElement('div');
|
const cb = $(checkboxSel);
|
||||||
row.className = 'row g-3 backend-server-row mt-1';
|
const target = $(targetSel);
|
||||||
row.innerHTML = `
|
cb?.addEventListener('change', () => toggle(cb.checked, target));
|
||||||
<div class="col-md-3">
|
// initial
|
||||||
<label class="form-label" for="name${serverCount}">Nazwa serwera</label>
|
if (cb && target) toggle(cb.checked, target);
|
||||||
<input type="text" id="name${serverCount}" class="form-control" name="backend_server_names[]" placeholder="server${serverCount}" required>
|
};
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
bindToggle('#add_path_based', '#base_redirect_fields');
|
||||||
<label class="form-label" for="ip${serverCount}">IP</label>
|
bindToggle('#add_acl_path', '#forbidden_fields');
|
||||||
<input type="text" id="ip${serverCount}" class="form-control" name="backend_server_ips[]" required>
|
|
||||||
</div>
|
const lbMethodSelect = $('#lb_method');
|
||||||
<div class="col-md-3">
|
const backendServersContainer = $('#backend_servers_container');
|
||||||
<label class="form-label" for="port${serverCount}">Port</label>
|
const addServerBtn = $('#add_backend_btn');
|
||||||
<input type="number" id="port${serverCount}" class="form-control" name="backend_server_ports[]" required>
|
|
||||||
</div>
|
const onLbMethodChange = () => {
|
||||||
<div class="col-md-3">
|
const isNoLb = lbMethodSelect?.value === 'no-lb';
|
||||||
<label class="form-label" for="maxconn${serverCount}">MaxConn</label>
|
|
||||||
<div class="d-flex gap-2">
|
if (isNoLb) {
|
||||||
<input type="number" id="maxconn${serverCount}" class="form-control" name="backend_server_maxconns[]">
|
if (addServerBtn) addServerBtn.classList.add('d-none');
|
||||||
<button type="button" class="btn btn-danger" title="Usuń">Usuń</button>
|
|
||||||
</div>
|
const serverRows = $$('.backend-server-row', backendServersContainer);
|
||||||
</div>`;
|
serverRows.forEach((row, idx) => {
|
||||||
row.querySelector('button.btn-danger')?.addEventListener('click', () => {
|
if (idx > 0) row.remove();
|
||||||
const rows = $$('.backend-server-row');
|
});
|
||||||
if (rows.length > 1) row.remove();
|
|
||||||
else alert('Musi istnieć co najmniej jeden backend.');
|
if (!$('.no-lb-info')) {
|
||||||
|
const info = document.createElement('div');
|
||||||
|
info.className = 'alert alert-info alert-sm no-lb-info mt-2';
|
||||||
|
info.innerHTML = '<i class="bi bi-info-circle me-2"></i><small>Tryb <strong>no-lb</strong>: frontend → backend → pojedynczy serwer. Możesz włączyć XSS, DOS, SQL injection protection itp.</small>';
|
||||||
|
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 = `
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="text" class="form-control" name="backend_server_names[]" placeholder="server${serverCount}" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" class="form-control" name="backend_server_ips[]" placeholder="192.168.1.${serverCount}" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="number" class="form-control" name="backend_server_ports[]" placeholder="80" min="1" max="65535" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="number" class="form-control" name="backend_server_maxconns[]" placeholder="100">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1">
|
||||||
|
<button type="button" class="btn btn-danger btn-sm w-100 remove-server">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const removeBtn = row.querySelector('.remove-server');
|
||||||
|
removeBtn.addEventListener('click', () => row.remove());
|
||||||
|
|
||||||
|
return row;
|
||||||
|
};
|
||||||
|
|
||||||
|
addBtn?.addEventListener('click', () => {
|
||||||
|
if (container) {
|
||||||
|
container.appendChild(createRow());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return row;
|
|
||||||
};
|
|
||||||
addBtn?.addEventListener('click', () => container?.appendChild(createRow()));
|
|
||||||
|
|
||||||
// auto dismiss alerts
|
// Remove button for dynamically added rows
|
||||||
setTimeout(() => $$('.alert').forEach(a => {
|
container?.addEventListener('click', (e) => {
|
||||||
if (typeof bootstrap !== 'undefined') new bootstrap.Alert(a).close();
|
if (e.target.closest('.remove-server')) {
|
||||||
}), 5000);
|
e.target.closest('.backend-server-row').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,220 +1,453 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% set active_page = "index" %}
|
{% set active_page = "index" %}
|
||||||
{% block title %}HAProxy • Index{% endblock %}
|
|
||||||
{% block breadcrumb %}<nav aria-label="breadcrumb" class="mb-3"><ol class="breadcrumb mb-0"><li class="breadcrumb-item"><a href="{{ url_for('main.index') }}"><i class="bi bi-house"></i></a></li><li class="breadcrumb-item active" aria-current="page">Konfiguracja</li></ol></nav>{% endblock %}
|
{% block title %}HAProxy • Configuration{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumb %}Configuration{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="card shadow-sm">
|
|
||||||
<div class="card-body">
|
|
||||||
<form method="post" action="/" id="fe-be-form" novalidate>
|
|
||||||
<h5 class="mb-3"><i class="fas fa-globe me-2"></i>New frontend</h5>
|
|
||||||
{% if message %}
|
|
||||||
<div class="alert {% if 'already exists' in message %}alert-danger{% else %}alert-success{% endif %} alert-dismissible" role="alert">
|
|
||||||
{{ message }}
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="row g-3">
|
<div class="row mb-4">
|
||||||
<div class="col-md-4">
|
<div class="col-md-3">
|
||||||
<label class="form-label" for="frontend_name">Name</label>
|
<div class="card text-center shadow-sm">
|
||||||
<input type="text" class="form-control" name="frontend_name" id="frontend_name" required>
|
<div class="card-body">
|
||||||
</div>
|
<h5 class="card-title text-primary">{{ frontend_count|default(0) }}</h5>
|
||||||
<div class="col-md-4">
|
<p class="card-text"><i class="bi bi-diagram-2"></i> Frontends</p>
|
||||||
<label class="form-label" for="frontend_ip">IP</label>
|
|
||||||
<input type="text" class="form-control" name="frontend_ip" id="frontend_ip" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<label class="form-label" for="frontend_port">Port</label>
|
|
||||||
<input type="number" class="form-control" name="frontend_port" id="frontend_port" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-check mt-3">
|
|
||||||
<input type="checkbox" class="form-check-input" id="ssl_checkbox" name="ssl_checkbox">
|
|
||||||
<label class="form-check-label" for="ssl_checkbox"><i class="fas fa-lock me-2"></i>SSL cert</label>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3 mt-1 d-none" id="ssl_fields">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label" for="ssl_cert_path">Certificate path (put in /ssl/)</label>
|
|
||||||
<input type="text" id="ssl_cert_path" class="form-control" name="ssl_cert_path">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 d-flex align-items-end">
|
|
||||||
<div class="form-check">
|
|
||||||
<input type="checkbox" class="form-check-input" id="ssl_redirect_checkbox" name="ssl_redirect_checkbox">
|
|
||||||
<label class="form-check-label" for="ssl_redirect_checkbox"><i class="fas fa-arrow-circle-right me-2"></i>Redirect do HTTPS</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row g-3 mt-1">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="lb_method">Metoda LB</label>
|
|
||||||
<select class="form-select" name="lb_method" id="lb_method">
|
|
||||||
<option value="roundrobin">Round Robin</option>
|
|
||||||
<option value="leastconn">Least Connections</option>
|
|
||||||
<option value="source">Source</option>
|
|
||||||
<option value="wrr">WRR</option>
|
|
||||||
<option value="wlc">WLC</option>
|
|
||||||
<option value="random">Random</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="protocol">Tryb</label>
|
|
||||||
<select class="form-select" name="protocol" id="protocol" required>
|
|
||||||
<option value="" disabled selected>--Select--</option>
|
|
||||||
<option value="tcp">TCP</option>
|
|
||||||
<option value="http">HTTP</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-check mt-3">
|
|
||||||
<input type="checkbox" class="form-check-input" name="add_dos" id="add_dos">
|
|
||||||
<label class="form-check-label" for="add_dos"><i class="fas fa-shield-alt me-2"></i>DOS protection</label>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3 mt-1 d-none" id="dos_fields">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label" for="limit_requests">Limit (np. 20)</label>
|
|
||||||
<input type="text" class="form-control" name="limit_requests" id="limit_requests">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label" for="ban_duration">Ban (np. 15s)</label>
|
|
||||||
<input type="text" class="form-control" name="ban_duration" id="ban_duration">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row g-3 mt-2 d-none http-only" id="http_extras">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="form-check">
|
|
||||||
<input type="checkbox" class="form-check-input" id="sql_injection_check" name="sql_injection_check">
|
|
||||||
<label class="form-check-label" for="sql_injection_check"><i class="fas fa-shield-alt me-2"></i>SQLi</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
</div>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input type="checkbox" class="form-check-input" id="xss_check" name="xss_check">
|
<div class="col-md-3">
|
||||||
<label class="form-check-label" for="xss_check"><i class="fas fa-shield-alt me-2"></i>XSS</label>
|
<div class="card text-center shadow-sm">
|
||||||
</div>
|
<div class="card-body">
|
||||||
|
<h5 class="card-title text-success">{{ backend_count|default(0) }}</h5>
|
||||||
|
<p class="card-text"><i class="bi bi-hdd-rack"></i> Backends</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
</div>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input type="checkbox" class="form-check-input" id="remote_uploads_check" name="remote_uploads_check">
|
<div class="col-md-3">
|
||||||
<label class="form-check-label" for="remote_uploads_check"><i class="fas fa-shield-alt me-2"></i>Remote uploads</label>
|
<div class="card text-center shadow-sm">
|
||||||
</div>
|
<div class="card-body">
|
||||||
|
<h5 class="card-title text-warning">{{ acl_count|default(0) }}</h5>
|
||||||
|
<p class="card-text"><i class="bi bi-shield"></i> ACLs</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
</div>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input type="checkbox" class="form-check-input" id="webshells_check" name="webshells_check">
|
<div class="col-md-3">
|
||||||
<label class="form-check-label" for="webshells_check"><i class="fas fa-shield-alt me-2"></i>Webshells</label>
|
<div class="card text-center shadow-sm">
|
||||||
</div>
|
<div class="card-body">
|
||||||
|
<h5 class="card-title text-info">L7: {{ layer7_count|default(0) }} / L4: {{ layer4_count|default(0) }}</h5>
|
||||||
|
<p class="card-text"><i class="bi bi-layers"></i> Layers</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="form-check">
|
|
||||||
<input type="checkbox" class="form-check-input" id="forward_for_check" name="forward_for_check">
|
|
||||||
<label class="form-check-label" for="forward_for_check"><i class="fas fa-network-wired me-2"></i>forwardfor</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check mt-3">
|
|
||||||
<input type="checkbox" class="form-check-input" name="add_acl" id="add_acl">
|
|
||||||
<label class="form-check-label" for="add_acl"><i class="fas fa-user-lock me-2"></i>ACL for frontend</label>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3 mt-1 d-none" id="acl_fields">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="acl">ACL</label>
|
|
||||||
<input type="text" class="form-control" name="acl" id="acl" placeholder="acl_name">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="acl_action">Action</label>
|
|
||||||
<input type="text" class="form-control" name="acl_action" id="acl_action" placeholder="hdr(host) -i test.com">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="backend_name_acl">Backend</label>
|
|
||||||
<input type="text" class="form-control" name="backend_name_acl" id="backend_name_acl" placeholder="somebackend">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-check mt-3 http-only d-none" id="forbidden_acl_container">
|
|
||||||
<input type="checkbox" class="form-check-input" name="add_acl_path" id="add_acl_path">
|
|
||||||
<label class="form-check-label" for="add_acl_path"><i class="fas fa-ban me-2"></i>Block path</label>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3 mt-1 d-none" id="forbidden_fields">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="forbidden_name">ACL name</label>
|
|
||||||
<input type="text" class="form-control" name="forbidden_name" id="forbidden_name">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="allowed_ip">Allowed IP</label>
|
|
||||||
<input type="text" class="form-control" name="allowed_ip" id="allowed_ip">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="forbidden_path">Path (ex. /admin)</label>
|
|
||||||
<input type="text" class="form-control" name="forbidden_path" id="forbidden_path">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-check mt-3 http-only d-none" id="path_based_container">
|
|
||||||
<input type="checkbox" class="form-check-input" name="add_path_based" id="add_path_based">
|
|
||||||
<label class="form-check-label" for="add_path_based"><i class="fas fa-arrow-circle-right me-2"></i>Path-based redirect</label>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3 mt-1 d-none" id="base_redirect_fields">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="redirect_domain_name">Domena docelowa</label>
|
|
||||||
<input type="text" class="form-control" name="redirect_domain_name" id="redirect_domain_name" placeholder="test2.com:8888">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="root_redirect">Root path</label>
|
|
||||||
<input type="text" class="form-control" name="root_redirect" id="root_redirect" placeholder="/">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label" for="redirect_to">Redirect to</label>
|
|
||||||
<input type="text" class="form-control" name="redirect_to" id="redirect_to" placeholder="/test">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="my-4">
|
|
||||||
|
|
||||||
<h5 class="mb-3"><i class="fas fa-sitemap me-2"></i>Backend pool</h5>
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label" for="backend_name">Backend name</label>
|
|
||||||
<input type="text" class="form-control" name="backend_name" id="backend_name" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="backend_servers_container" class="mt-3">
|
|
||||||
<div class="row g-3 backend-server-row">
|
|
||||||
<div class="col-md-3">
|
|
||||||
<label class="form-label" for="name1">Server name</label>
|
|
||||||
<input type="text" id="name1" class="form-control" name="backend_server_names[]" placeholder="server1" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<label class="form-label" for="ip1">IP</label>
|
|
||||||
<input type="text" id="ip1" class="form-control" name="backend_server_ips[]" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<label class="form-label" for="port1">Port</label>
|
|
||||||
<input type="number" id="port1" class="form-control" name="backend_server_ports[]" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<label class="form-label" for="maxconn1">MaxConn</label>
|
|
||||||
<input type="number" id="maxconn1" class="form-control" name="backend_server_maxconns[]">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex gap-2 mt-3">
|
|
||||||
<button type="button" class="btn btn-secondary" id="add_backend_btn"><i class="bi bi-plus-lg me-1"></i>Add backend</button>
|
|
||||||
<button type="submit" class="btn btn-success" id="success_btn"><i class="bi bi-check2-circle me-1"></i>Save</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
{% block page_js %}
|
{% if message %}
|
||||||
|
<div class="alert alert-{{ message_type|default('info') }} alert-dismissible fade show" role="alert">
|
||||||
|
<i class="bi bi-{% if message_type == 'success' %}check-circle{% elif message_type == 'danger' %}exclamation-circle{% else %}info-circle{% endif %} me-2"></i>
|
||||||
|
{{ message }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" class="needs-validation">
|
||||||
|
<div class="card shadow-sm mb-4">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h5 class="mb-0"><i class="bi bi-plus-circle me-2"></i>Add New Configuration</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<!-- FRONTEND SECTION -->
|
||||||
|
<h6 class="text-primary mb-3"><i class="bi bi-hdd-network me-2"></i>Frontend</h6>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label for="frontend_name" class="form-label">Frontend Name</label>
|
||||||
|
<input type="text" class="form-control" id="frontend_name" name="frontend_name"
|
||||||
|
placeholder="e.g. fe_web" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label for="frontend_ip" class="form-label">IP Address (bind)</label>
|
||||||
|
<input type="text" class="form-control" id="frontend_ip" name="frontend_ip"
|
||||||
|
placeholder="0.0.0.0" value="0.0.0.0" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label for="frontend_port" class="form-label">Port</label>
|
||||||
|
<input type="number" class="form-control" id="frontend_port" name="frontend_port"
|
||||||
|
placeholder="80" min="1" max="65535" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="protocol" class="form-label">Protocol</label>
|
||||||
|
<select class="form-select" id="protocol" name="protocol" required>
|
||||||
|
<option value="http">HTTP</option>
|
||||||
|
<option value="tcp">TCP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="lb_method" class="form-label">Load Balancing Method</label>
|
||||||
|
<select class="form-select" id="lb_method" name="lb_method" required>
|
||||||
|
<option value="roundrobin">Round Robin</option>
|
||||||
|
<option value="leastconn">Least Connections</option>
|
||||||
|
<option value="source">Source IP Hash</option>
|
||||||
|
<option value="uri">URI Hash</option>
|
||||||
|
<option value="static-rr">Static Round Robin (WRR)</option>
|
||||||
|
<option value="no-lb">No Load Balancing (single host)</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Choose load balancing algorithm or simple single host</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SSL Section -->
|
||||||
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" id="ssl_checkbox" name="ssl_checkbox">
|
||||||
|
<label class="form-check-label" for="ssl_checkbox">
|
||||||
|
<i class="bi bi-lock me-1"></i>Use SSL (HTTPS)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-3 d-none" id="ssl_fields">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label for="ssl_cert_path" class="form-label">SSL Certificate Path</label>
|
||||||
|
<input type="text" class="form-control" id="ssl_cert_path" name="ssl_cert_path"
|
||||||
|
value="/etc/haproxy/certs/haproxy.pem">
|
||||||
|
<div class="form-text">Full path to .pem file</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="ssl_redirect_checkbox"
|
||||||
|
name="ssl_redirect_checkbox">
|
||||||
|
<label class="form-check-label" for="ssl_redirect_checkbox">
|
||||||
|
Redirect HTTP to HTTPS
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<!-- BACKEND SECTION -->
|
||||||
|
<h6 class="text-primary mb-3"><i class="bi bi-hdd-rack me-2"></i>Backend</h6>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label for="backend_name" class="form-label">Backend Name</label>
|
||||||
|
<input type="text" class="form-control" id="backend_name" name="backend_name"
|
||||||
|
placeholder="e.g. be_web" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Backend servers -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Backend Servers</label>
|
||||||
|
<div id="backend_servers_container">
|
||||||
|
<div class="row g-3 backend-server-row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="text" class="form-control" name="backend_server_names[]"
|
||||||
|
placeholder="server1" value="server1" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" class="form-control" name="backend_server_ips[]"
|
||||||
|
placeholder="192.168.1.10" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="number" class="form-control" name="backend_server_ports[]"
|
||||||
|
placeholder="80" min="1" max="65535" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="number" class="form-control" name="backend_server_maxconns[]"
|
||||||
|
placeholder="100">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1">
|
||||||
|
<button type="button" class="btn btn-danger btn-sm w-100 remove-server" style="visibility: hidden;">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm mt-2" id="add_backend_btn">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i>Add Server
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Health Check -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="health_check" name="health_check">
|
||||||
|
<label class="form-check-label" for="health_check">
|
||||||
|
<i class="bi bi-heart-pulse me-1"></i>Enable Health Check
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 d-none" id="health_check_fields">
|
||||||
|
<label for="health_check_link" class="form-label">Health Check Path</label>
|
||||||
|
<input type="text" class="form-control" id="health_check_link" name="health_check_link"
|
||||||
|
value="/" placeholder="/">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-3" style="display: none;" id="tcp_health_check">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="health_check2" name="health_check2">
|
||||||
|
<label class="form-check-label" for="health_check2">
|
||||||
|
<i class="bi bi-heart-pulse me-1"></i>Enable TCP Health Check
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sticky Session -->
|
||||||
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="sticky_session" name="sticky_session">
|
||||||
|
<label class="form-check-label" for="sticky_session">
|
||||||
|
<i class="bi bi-pin-angle me-1"></i>Sticky Session
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 d-none" id="sticky_fields">
|
||||||
|
<select class="form-select" id="sticky_session_type" name="sticky_session_type">
|
||||||
|
<option value="cookie">Cookie-based</option>
|
||||||
|
<option value="source">Source IP-based</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<!-- HEADERS & SECURITY SECTION -->
|
||||||
|
<h6 class="text-primary mb-3"><i class="bi bi-shield-lock me-2"></i>Headers & Security</h6>
|
||||||
|
|
||||||
|
<!-- Custom Headers -->
|
||||||
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="add_header" name="add_header">
|
||||||
|
<label class="form-check-label" for="add_header">
|
||||||
|
<i class="bi bi-tag me-1"></i>Add Custom Header
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 d-none" id="header_fields">
|
||||||
|
<input type="text" class="form-control" id="header_name" name="header_name"
|
||||||
|
placeholder="e.g. X-Custom-Header">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 d-none" id="header_fields">
|
||||||
|
<input type="text" class="form-control" id="header_value" name="header_value"
|
||||||
|
placeholder="e.g. custom-value">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Server Header Removal -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="del_server_header"
|
||||||
|
name="del_server_header">
|
||||||
|
<label class="form-check-label" for="del_server_header">
|
||||||
|
<i class="bi bi-shield-lock me-1"></i>Hide Server Header
|
||||||
|
</label>
|
||||||
|
<div class="form-text small">
|
||||||
|
Adds: <code>http-response del-header Server</code> (security)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Forward For -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="forward_for_check"
|
||||||
|
name="forward_for_check" checked>
|
||||||
|
<label class="form-check-label" for="forward_for_check">
|
||||||
|
<i class="bi bi-arrow-right me-1"></i>Forward For (X-Forwarded-For)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<!-- PROTECTION SECTION -->
|
||||||
|
<h6 class="text-primary mb-3"><i class="bi bi-bug me-2"></i>Protection</h6>
|
||||||
|
|
||||||
|
<!-- DOS Protection -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="add_dos" name="add_dos">
|
||||||
|
<label class="form-check-label" for="add_dos">
|
||||||
|
<i class="bi bi-exclamation-triangle me-1"></i>DOS/DDoS Protection
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 d-none" id="dos_fields">
|
||||||
|
<label for="ban_duration" class="form-label">Ban Duration</label>
|
||||||
|
<input type="text" class="form-control" id="ban_duration" name="ban_duration"
|
||||||
|
value="30m" placeholder="30m">
|
||||||
|
<div class="form-text">e.g. 30m, 1h, 24h</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 d-none" id="dos_fields">
|
||||||
|
<label for="limit_requests" class="form-label">Request Limit (per min)</label>
|
||||||
|
<input type="number" class="form-control" id="limit_requests" name="limit_requests"
|
||||||
|
value="100" min="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SQL Injection -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="sql_injection_check"
|
||||||
|
name="sql_injection_check">
|
||||||
|
<label class="form-check-label" for="sql_injection_check">
|
||||||
|
<i class="bi bi-database-exclamation me-1"></i>SQL Injection Protection
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- XSS -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="xss_check" name="xss_check">
|
||||||
|
<label class="form-check-label" for="xss_check">
|
||||||
|
<i class="bi bi-code-slash me-1"></i>XSS Protection
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Remote Uploads -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="remote_uploads_check"
|
||||||
|
name="remote_uploads_check">
|
||||||
|
<label class="form-check-label" for="remote_uploads_check">
|
||||||
|
<i class="bi bi-cloud-upload me-1"></i>Block Remote Uploads
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Webshells -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="webshells_check"
|
||||||
|
name="webshells_check">
|
||||||
|
<label class="form-check-label" for="webshells_check">
|
||||||
|
<i class="bi bi-shield-exclamation me-1"></i>Block Webshells
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<!-- ACL SECTION -->
|
||||||
|
<h6 class="text-primary mb-3"><i class="bi bi-diagram-3 me-2"></i>ACL & Routing</h6>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="add_acl" name="add_acl">
|
||||||
|
<label class="form-check-label" for="add_acl">
|
||||||
|
<i class="bi bi-shuffle me-1"></i>Add ACL
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="acl_fields">
|
||||||
|
<input type="text" class="form-control" id="acl" name="acl" placeholder="acl_name">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="acl_fields">
|
||||||
|
<select class="form-select" id="acl_action" name="acl_action">
|
||||||
|
<option value="hdr">Header</option>
|
||||||
|
<option value="path">Path</option>
|
||||||
|
<option value="src">Source IP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="acl_fields">
|
||||||
|
<input type="text" class="form-control" id="backend_name_acl" name="backend_name_acl"
|
||||||
|
placeholder="backend_name">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Path-based routing -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="add_path_based" name="add_path_based">
|
||||||
|
<label class="form-check-label" for="add_path_based">
|
||||||
|
<i class="bi bi-arrow-repeat me-1"></i>Path-based Redirect
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="base_redirect_fields">
|
||||||
|
<input type="text" class="form-control" id="redirect_domain_name" name="redirect_domain_name"
|
||||||
|
placeholder="domain.com">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="base_redirect_fields">
|
||||||
|
<input type="text" class="form-control" id="root_redirect" name="root_redirect"
|
||||||
|
placeholder="/">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="base_redirect_fields">
|
||||||
|
<input type="text" class="form-control" id="redirect_to" name="redirect_to"
|
||||||
|
placeholder="https://example.com">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Forbidden paths -->
|
||||||
|
<div class="row g-3 mb-3 http-only">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="add_acl_path" name="add_acl_path">
|
||||||
|
<label class="form-check-label" for="add_acl_path">
|
||||||
|
<i class="bi bi-ban me-1"></i>Block Paths
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="forbidden_fields">
|
||||||
|
<input type="text" class="form-control" id="forbidden_name" name="forbidden_name"
|
||||||
|
placeholder="forbidden_acl">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="forbidden_fields">
|
||||||
|
<input type="text" class="form-control" id="allowed_ip" name="allowed_ip"
|
||||||
|
placeholder="192.168.1.0/24">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-none" id="forbidden_fields">
|
||||||
|
<input type="text" class="form-control" id="forbidden_path" name="forbidden_path"
|
||||||
|
placeholder="/admin">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<!-- SUBMIT BUTTON -->
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg">
|
||||||
|
<i class="bi bi-check-circle me-2"></i>Save Configuration
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='js/index.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/index.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/form.js') }}"></script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import os
|
|||||||
HAPROXY_CFG = '/etc/haproxy/haproxy.cfg'
|
HAPROXY_CFG = '/etc/haproxy/haproxy.cfg'
|
||||||
|
|
||||||
def is_frontend_exist(frontend_name, frontend_ip, frontend_port):
|
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):
|
if not os.path.exists(HAPROXY_CFG):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -28,7 +27,6 @@ def is_frontend_exist(frontend_name, frontend_ip, frontend_port):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def is_backend_exist(backend_name):
|
def is_backend_exist(backend_name):
|
||||||
"""Check if backend with given name already exists"""
|
|
||||||
if not os.path.exists(HAPROXY_CFG):
|
if not os.path.exists(HAPROXY_CFG):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -45,8 +43,52 @@ def is_backend_exist(backend_name):
|
|||||||
|
|
||||||
return False
|
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():
|
def count_frontends_and_backends():
|
||||||
"""Count frontends, backends, ACLs and layer types"""
|
|
||||||
if not os.path.exists(HAPROXY_CFG):
|
if not os.path.exists(HAPROXY_CFG):
|
||||||
return 0, 0, 0, 0, 0
|
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,
|
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):
|
||||||
|
|
||||||
# Ensure directory exists
|
|
||||||
os.makedirs(os.path.dirname(HAPROXY_CFG), exist_ok=True)
|
os.makedirs(os.path.dirname(HAPROXY_CFG), exist_ok=True)
|
||||||
|
|
||||||
if is_backend_exist(backend_name):
|
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" mode {protocol}\n")
|
||||||
haproxy_cfg.write(f" balance {lb_method}\n")
|
haproxy_cfg.write(f" balance {lb_method}\n")
|
||||||
|
|
||||||
# Add protection rules
|
|
||||||
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")
|
||||||
@@ -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")
|
haproxy_cfg.write(f" http-request silent-drop if abuse\n")
|
||||||
|
|
||||||
if sql_injection_check:
|
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_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")
|
||||||
haproxy_cfg.write(" acl semicolon_path path_reg -i ^.*;.*\n")
|
haproxy_cfg.write(" acl semicolon_path path_reg -i ^.*;.*\n")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import requests
|
import requests
|
||||||
import csv
|
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():
|
def fetch_haproxy_stats():
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user