From dccc2b3e206411639d822209ab39e573746f1cc2 Mon Sep 17 00:00:00 2001 From: Aidaho Date: Tue, 26 Aug 2025 09:24:20 +0300 Subject: [PATCH] v8.2.3: Add backend header hiding option and improve UDP listener updates - Introduced `hide_backend_headers` option in Nginx configuration for enhanced security. - Updated models, templates, and JavaScript to support `hide_backend_headers`. - Improved UDP listener rendering logic by dynamically fetching and syncing listeners via AJAX. - Added new database migration to update version to `8.2.3`. --- .../db/migrations/v8_2_3_update_version.py | 22 ++++++++++ .../nginx_section/templates/proxy_pass.j2 | 2 +- .../ansible/roles/nginx_section/vars/main.yml | 1 - app/static/js/add_nginx.js | 42 +++++++++++++------ app/static/js/edit_config.js | 5 +++ app/static/js/udp.js | 30 +++++++++++++ .../include/add_nginx/proxy_pass.html | 1 + app/templates/languages/en.html | 2 + app/templates/languages/es-ES.html | 2 + app/templates/languages/fr.html | 2 + app/templates/languages/pt-br.html | 2 + app/templates/languages/ru.html | 2 + app/templates/languages/zh.html | 2 + app/templates/udp/listeners.html | 11 ++--- app/views/udp/views.py | 1 - 15 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 app/modules/db/migrations/v8_2_3_update_version.py diff --git a/app/modules/db/migrations/v8_2_3_update_version.py b/app/modules/db/migrations/v8_2_3_update_version.py new file mode 100644 index 00000000..c56a1abf --- /dev/null +++ b/app/modules/db/migrations/v8_2_3_update_version.py @@ -0,0 +1,22 @@ +from playhouse.migrate import * +from app.modules.db.db_model import connect, Version + +migrator = connect(get_migrator=1) + + +def up(): + """Apply the migration.""" + try: + Version.update(version='8.2.3').execute() + except Exception as e: + print(f"Error updating version: {str(e)}") + raise e + + +def down(): + """Roll back the migration.""" + try: + Version.update(version='8.2.2').execute() + except Exception as e: + print(f"Error rolling back migration: {str(e)}") + raise e diff --git a/app/scripts/ansible/roles/nginx_section/templates/proxy_pass.j2 b/app/scripts/ansible/roles/nginx_section/templates/proxy_pass.j2 index 4371006b..76aaf747 100644 --- a/app/scripts/ansible/roles/nginx_section/templates/proxy_pass.j2 +++ b/app/scripts/ansible/roles/nginx_section/templates/proxy_pass.j2 @@ -135,7 +135,7 @@ location {{ location.location }} { gzip_proxied any; {% endif %} - {% if nginx_proxy.security.hide_backend_headers %} + {% if config.security.hide_backend_headers %} # Hide backend headers proxy_hide_header X-Powered-By; proxy_hide_header Server; diff --git a/app/scripts/ansible/roles/nginx_section/vars/main.yml b/app/scripts/ansible/roles/nginx_section/vars/main.yml index f18ed33b..ef00a98e 100644 --- a/app/scripts/ansible/roles/nginx_section/vars/main.yml +++ b/app/scripts/ansible/roles/nginx_section/vars/main.yml @@ -9,7 +9,6 @@ nginx_proxy: # Security configurations security: - hide_backend_headers: true # Hide backend server headers (e.g., X-Powered-By) hsts_max_age: 15768000 # HSTS duration in seconds (6 months) content_security_policy: "default-src 'self'" # Content Security Policy rules diff --git a/app/static/js/add_nginx.js b/app/static/js/add_nginx.js index cdc77b35..345d3b0b 100644 --- a/app/static/js/add_nginx.js +++ b/app/static/js/add_nginx.js @@ -237,7 +237,14 @@ function getNginxFormData($form, form_name) { let header = {action, name, value}; headers.push(header); }); - let location_config = {location, proxy_connect_timeout, proxy_read_timeout, proxy_send_timeout, headers, upstream}; + let location_config = { + location, + proxy_connect_timeout, + proxy_read_timeout, + proxy_send_timeout, + headers, + upstream + }; indexed_array['locations'].push(location_config) } else if (n['name'] === 'ssl_offloading') { if ($('input[name="ssl_offloading"]').is(':checked')) { @@ -261,23 +268,31 @@ function getNginxFormData($form, form_name) { indexed_array[n['name']] = n['value']; } }); - $('#name_alias_div p').each(function (){ + $('#name_alias_div p').each(function () { let name = $(this).children("input[name='name_alias']").val(); if (name === undefined || name === '') { return; } indexed_array['name_aliases'].push(name); }); - let hide_server_tokens = false; - let security_headers = false; - if ($('#hide_server_tokens').is(':checked')) { - hide_server_tokens = true; - } - if ($('#security_headers').is(':checked')) { - security_headers = true; - } - indexed_array['security'] = {'hide_server_tokens': hide_server_tokens, 'security_headers': security_headers}; - $('#'+form_name+' span[name="add_servers"] p').each(function (){ + let hide_server_tokens = false; + let security_headers = false; + let hide_backend_headers = false; + if ($('#hide_server_tokens').is(':checked')) { + hide_server_tokens = true; + } + if ($('#security_headers').is(':checked')) { + security_headers = true; + } + if ($('#hide_backend_headers').is(':checked')) { + hide_backend_headers = true; + } + indexed_array['security'] = { + 'hide_server_tokens': hide_server_tokens, + 'security_headers': security_headers, + 'hide_backend_headers': hide_backend_headers + }; + $('#' + form_name + ' span[name="add_servers"] p').each(function () { let server = $(this).children("input[name='servers']").val(); if (server === undefined || server === '') { return; @@ -290,7 +305,8 @@ function getNginxFormData($form, form_name) { }); let elementsForDelete = [ 'servers', 'server_port', 'max_fails', 'fail_timeout', 'proxy_connect_timeout', 'proxy_read_timeout', 'proxy_send_timeout', - 'headers_res', 'header_name', 'header_value', 'upstream', 'server', 'name_alias', 'hide_server_tokens', 'security_headers' + 'headers_res', 'header_name', 'header_value', 'upstream', 'server', 'name_alias', 'hide_server_tokens', 'security_headers', + 'hide_backend_headers' ] for (let element of elementsForDelete) { delete indexed_array[element] diff --git a/app/static/js/edit_config.js b/app/static/js/edit_config.js index 4dbe5886..3866fa74 100644 --- a/app/static/js/edit_config.js +++ b/app/static/js/edit_config.js @@ -826,6 +826,11 @@ function openNginxSection(section) { } else { $('#security_headers').prop("checked", false); } + if (data.security.hide_backend_headers) { + $('#hide_backend_headers').prop("checked", true); + } else { + $('#hide_backend_headers').prop("checked", false); + } } if (data.hsts) { $('#hsts').prop("checked", true); diff --git a/app/static/js/udp.js b/app/static/js/udp.js index 68d454ed..4ebe08ea 100644 --- a/app/static/js/udp.js +++ b/app/static/js/udp.js @@ -615,3 +615,33 @@ function checkUdpBackendStatus(listener_id, backend_ip) { } }); } +function getUdpListeners() { + $.ajax({ + url: api_prefix + "/udp/listeners", + success: function (data) { + let clusters = document.querySelectorAll('.up-pannel > [id^="listener-"]'); + let clusterNumbers = Array.from(clusters).map(div => + parseInt(div.id.replace('listener-', ''), 10) + ); + if (data.status === 'failed') { + toastr.error(data.error); + return; + } + if (data.length > 0) { + const dataIds = data.map(item => item.id); + const idsToDelete = clusterNumbers.filter(id => !dataIds.includes(id)); + + for (const [key, value] of Object.entries(data)) { + if (!clusterNumbers.includes(value.id)) { + getUDPListener(value.id, true); + } else { + getUDPListener(value.id, false); + } + } + for (let id of idsToDelete) { + $('#listener-' + id).remove(); + } + } + } + }); +} diff --git a/app/templates/include/add_nginx/proxy_pass.html b/app/templates/include/add_nginx/proxy_pass.html index 3b938e6f..f0fa71f6 100644 --- a/app/templates/include/add_nginx/proxy_pass.html +++ b/app/templates/include/add_nginx/proxy_pass.html @@ -120,6 +120,7 @@ Timeouts: Timeouts (proxy_connect_timeout). {% set enable_sec_headers = lang.words.enable|title() + ' ' + lang.words.security + ' ' + lang.words.headers %}

{{ checkbox('hide_server_tokens', title=lang.add_page.desc.hide_server_tokens, desc=lang.phrases.hide_server_tokens, checked='checked') }}

{{ checkbox('security_headers', title=lang.add_page.desc.security_headers, desc=lang.phrases.security_headers, checked='checked') }}

+

{{ checkbox('hide_backend_headers', title=lang.add_page.desc.hide_backend_headers, desc=lang.phrases.hide_backend_headers, checked='checked') }}

diff --git a/app/templates/languages/en.html b/app/templates/languages/en.html index 984530f6..29d4cb3b 100644 --- a/app/templates/languages/en.html +++ b/app/templates/languages/en.html @@ -345,6 +345,7 @@ "check_interval_title": "Check interval. In seconds.", "hide_server_tokens": "Hide server tokens", "security_headers": "Enable security headers", + "hide_backend_headers": "Hide backend server headers", } %} {% set roles = { @@ -358,6 +359,7 @@ "desc": { "security_headers": "Enable security headers (X-Content-Type-Options, etc.)", "hide_server_tokens": "Hide Nginx version in headers", + "hide_backend_headers": "Hide backend server headers (e.g., X-Powered-By)", "port_check": "A basic TCP-layer health check tries to connect to the server's TCP port. The check is valid when the server answers with a SYN/ACK packet.", "maxconn": "The total number of connections allowed, process-wide. This stops the process from accepting too many connections at once, which safeguards it from running out of memory.", "server_template": "Create the list of servers from the template", diff --git a/app/templates/languages/es-ES.html b/app/templates/languages/es-ES.html index 73f0091c..87813050 100644 --- a/app/templates/languages/es-ES.html +++ b/app/templates/languages/es-ES.html @@ -332,6 +332,7 @@ "check_interval_title": "Intervalo de comprobación. En segundos.", "hide_server_tokens": "Ocultar tokens del servidor", "security_headers": "Habilitar encabezados de seguridad", + "hide_backend_headers": "Ocultar los encabezados del servidor backend", } %} {% set roles = { @@ -345,6 +346,7 @@ "desc": { "security_headers": "Habilitar encabezados de seguridad (X-Content-Type-Options, etc.)", "hide_server_tokens": "Ocultar la versión de Nginx en los encabezados", + "hide_backend_headers": "Ocultar los encabezados del servidor backend (por ejemplo, X-Powered-By)", "port_check": "Una verificación básica a nivel TCP intenta conectarse al puerto TCP del servidor. La verificación es válida cuando el servidor responde con un paquete SYN/ACK.", "maxconn": "Número total de conexiones permitidas a nivel de proceso. Esto evita aceptar demasiadas conexiones a la vez, protegiendo de quedarse sin memoria.", "server_template": "Crear la lista de servidores desde la plantilla", diff --git a/app/templates/languages/fr.html b/app/templates/languages/fr.html index 967c0b2a..7497b503 100644 --- a/app/templates/languages/fr.html +++ b/app/templates/languages/fr.html @@ -332,6 +332,7 @@ "check_interval_title": "Vérifiez l'intervalle. En secondes.", "hide_server_tokens": "Masquer les jetons du serveur", "security_headers": "Activer les en-têtes de sécurité", + "hide_backend_headers": "Masquer les en-têtes du serveur principal", } %} {% set roles = { @@ -345,6 +346,7 @@ "desc": { "security_headers": "Activer les en-têtes de sécurité (X-Content-Type-Options, etc.)", "hide_server_tokens": "Masquer la version Nginx dans les en-têtes", + "hide_backend_headers": "Masquer les en-têtes du serveur principal (par exemple, X-Powered-By)", "port_check": "Un contrôle de base de la couche TCP tente de se connecter au port TCP du serveur. Le contrôle est valide lorsque le serveur répond par un paquet SYN/ACK.", "maxconn": "Le nombre total de connexions autorisées, à l\'échelle du processus. Cela empêche le processus d\'accepter trop de connexions à la fois, ce qui lui évite de manquer de mémoire.", "server_template": "Créez la liste des serveurs à partir du modèle", diff --git a/app/templates/languages/pt-br.html b/app/templates/languages/pt-br.html index 6f93635a..25360088 100644 --- a/app/templates/languages/pt-br.html +++ b/app/templates/languages/pt-br.html @@ -332,6 +332,7 @@ "check_interval_title": "Verifique o intervalo. Em segundos.", "hide_server_tokens": "Ocultar tokens do servidor", "security_headers": "Habilitar cabeçalhos de segurança", + "hide_backend_headers": "Ocultar cabeçalhos do servidor de backend", } %} {% set roles = { @@ -345,6 +346,7 @@ "desc": { "security_headers": "Habilitar cabeçalhos de segurança (X-Content-Type-Options, etc.)", "hide_server_tokens": "Ocultar versão do Nginx nos cabeçalhos", + "hide_backend_headers": "Ocultar cabeçalhos do servidor de backend (por exemplo, X-Powered-By)", "port_check": "a verificação de integridade básica do nivel TCP tenta se conectar à porta TCP do servidor. A verificação é válida quando o servidor responde com um pacote SYN/ACK", "maxconn": "número total de conexões permitidas. Isso impede que o processo aceite muitas conexões de uma só vez, o que o protege de ficar sem memória.", "server_template": "Criar uma lista de servidores de um modelo", diff --git a/app/templates/languages/ru.html b/app/templates/languages/ru.html index b0f2fb99..4977eb72 100644 --- a/app/templates/languages/ru.html +++ b/app/templates/languages/ru.html @@ -345,6 +345,7 @@ "check_interval_title": "Интервал проверки. В секундах.", "hide_server_tokens": "Скрыть токены сервера", "security_headers": "Включить заголовки безопасности", + "hide_backend_headers": "Скрыть заголовки внутреннего сервера", } %} {% set roles = { @@ -358,6 +359,7 @@ "desc": { "security_headers": "Включить заголовки безопасности (X-Content-Type-Options и т. д.)", "hide_server_tokens": "Скрыть версию Nginx в заголовках", + "hide_backend_headers": "Скрыть заголовки внутреннего сервера (например, X-Powered-By)", "port_check": "Базовая проверка работоспособности на уровне TCP пытается подключиться к TCP-порту сервера. Проверка действительна, когда сервер отвечает пакетом SYN/ACK.", "maxconn": "Общее количество разрешенных подключений для всего процесса. Это не позволяет процессу одновременно принимать слишком много подключений, что защищает его от нехватки памяти.", "server_template": "Создать список серверов из шаблона", diff --git a/app/templates/languages/zh.html b/app/templates/languages/zh.html index 85e34026..b9ec5e7f 100644 --- a/app/templates/languages/zh.html +++ b/app/templates/languages/zh.html @@ -332,6 +332,7 @@ "check_interval_title": "检查间隔(以秒为单位)。", "hide_server_tokens": "隐藏服务器令牌", "security_headers": "启用安全标头", + "hide_backend_headers": "隐藏后端服务器标头", } %} {% set roles = { @@ -345,6 +346,7 @@ "desc": { "security_headers": "启用安全标头(X-Content-Type-Options 等)", "hide_server_tokens": "在标头中隐藏 Nginx 版本", + "hide_backend_headers": "隐藏后端服务器标头(例如 X-Powered-By)", "port_check": "基本的 TCP 层健康检查尝试连接到服务器的 TCP 端口。当服务器返回 SYN/ACK 数据包时,检查被认为是成功的。", "maxconn": "进程允许的总连接数。这可以防止进程接受过多连接,从而防止内存耗尽。", "server_template": "根据模板创建服务器列表。", diff --git a/app/templates/udp/listeners.html b/app/templates/udp/listeners.html index fef078a7..07073b34 100644 --- a/app/templates/udp/listeners.html +++ b/app/templates/udp/listeners.html @@ -22,11 +22,7 @@ {% if g.user_params['role'] <= 3 %}
+ {{lang.words.create|title()}} UDP {{ lang.words.listener }}
{% endif %} -
- {% for listener in listeners %} -
- {% endfor %} -
+