diff --git a/roles/nginx/.ansible-lint b/roles/nginx/.ansible-lint new file mode 100755 index 0000000..2b1d710 --- /dev/null +++ b/roles/nginx/.ansible-lint @@ -0,0 +1,23 @@ +--- +profile: production + +# Paths to exclude from linting +exclude_paths: + - collections/ + - .git/ + +# Treat these rule IDs as warnings instead of errors +warn_list: + - experimental + - name + - no-handler + - risky-shell-pipe + - no-changed-when + - risky-file-permissions + - yaml[trailing-spaces] + +# Skip rules that don't apply to this project +skip_list: + - var-naming[no-role-prefix] + - yaml[line-length] + - name[missing] diff --git a/roles/nginx/.gitlab-ci.yml b/roles/nginx/.gitlab-ci.yml new file mode 100755 index 0000000..1ace23b --- /dev/null +++ b/roles/nginx/.gitlab-ci.yml @@ -0,0 +1,49 @@ +--- +# GitLab CI pipeline for Ansible infrastructure +# +# Required GitLab CI/CD Variables (Settings → CI/CD → Variables): +# ANSIBLE_SSH_USER — SSH username for target servers +# ANSIBLE_SSHKEY_ID_RSA — private SSH key (тип: File или Variable, содержимое id_rsa) +# ANSIBLE_BECOME_PASS — sudo password +# VCENTER_USER — vCenter username (e.g. administrator@vsphere.local) +# VCENTER_PASSWORD — vCenter password +# ANSIBLE_GIT_TOKEN — Project Access Token or Deploy Token with write_repository scope +# (required for committing connection_history.json from the pages job) +# Create: Settings → Access Tokens → role: Developer, scope: write_repository + +stages: + - validate + +# ── Shared configuration ─────────────────────────────────────────────────────── +default: + image: python:3.11-slim + before_script: + - apt-get update -qq + - apt-get install -y -qq --no-install-recommends openssh-client git + - pip install --quiet ansible ansible-lint pyvmomi requests netaddr + - ansible-galaxy collection install -r requirements.yml -p collections/ --force + - mkdir -p ~/.ssh && chmod 700 ~/.ssh + - printf '%b\n' "$ANSIBLE_SSHKEY_ID_RSA" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - eval $(ssh-agent -s) + - ssh-add ~/.ssh/id_rsa + interruptible: true + +variables: + ANSIBLE_FORCE_COLOR: "1" + ANSIBLE_HOST_KEY_CHECKING: "False" + ANSIBLE_ROLES_PATH: roles + ANSIBLE_COLLECTIONS_PATH: collections + ANSIBLE_FILTER_PLUGINS: filter_plugins + PIP_NO_CACHE_DIR: "1" + # inventory environment — change to 'staging' for a staging pipeline + INVENTORY: inventory/prod + +# ── Stage: validate ──────────────────────────────────────────────────────────── +lint: + stage: validate + script: + - ansible-lint . + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml index 47ebee7..a4d6095 100644 --- a/roles/nginx/defaults/main.yml +++ b/roles/nginx/defaults/main.yml @@ -29,6 +29,7 @@ nginx_conf_snippets: - nginx-proxy-params.conf - nginx-server-ssl.conf - nginx-cors.conf + - nginx-maps.conf nginx_conf_remote_snippets: [] # - url: 'https://example.org/git/template.conf.j2' @@ -143,8 +144,8 @@ nginx_block_dangerous_dotfiles: false nginx_block_dotfiles: true # k,M,G -nginx_logrotate_maxfilesize: 1G -nginx_logrotate_retention: "52" +nginx_logrotate_maxfilesize: 512M +nginx_logrotate_retention: "14" nginx_install_robots_txt: false nginx_robots_disallow_everything: false @@ -221,3 +222,5 @@ nginx_virthosts: [] # fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # include fastcgi_params; # } +graylog_server: 10.203.0.52 +graylog_port: 1514 diff --git a/roles/nginx/handlers/main.yml b/roles/nginx/handlers/main.yml index c5644ea..f0426cd 100644 --- a/roles/nginx/handlers/main.yml +++ b/roles/nginx/handlers/main.yml @@ -13,3 +13,8 @@ ansible.builtin.service: name: nginx daemon_reload: true + +- name: Restart rsyslog service + ansible.builtin.service: + name: rsyslog + state: restarted diff --git a/roles/nginx/requirements.yml b/roles/nginx/requirements.yml new file mode 100755 index 0000000..fefea24 --- /dev/null +++ b/roles/nginx/requirements.yml @@ -0,0 +1,8 @@ +--- +collections: + - name: ansible.posix + version: ">=1.5.0" + - name: ansible.utils + version: ">=2.0.0,<5.0.0" + - name: community.general + version: ">=8.0.0" diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml index a332e83..df43e79 100644 --- a/roles/nginx/tasks/main.yml +++ b/roles/nginx/tasks/main.yml @@ -50,3 +50,48 @@ # ignore_errors: true failed_when: false tags: nginx + +# - name: Create file with secure permissions +# ansible.builtin.file: +# path: /etc/rsyslog.d/10-local7-to-graylog.conf +# state: touch +# owner: root +# group: root +# mode: '0644' + +# - name: Add rsyslog to graylog +## ansible.builtin.lineinfile: +# path: /etc/rsyslog.d/10-local7-to-graylog.conf +# line: "local7.* @@{{ graylog_server }}:{{ graylog_port }};RSYSLOG_SyslogProtocol23Format" +# state: present +# notify: Restart rsyslog service + +- name: Add rsyslog configuration + ansible.builtin.template: + src: rsyslog.conf.j2 + dest: /etc/rsyslog.d/10-local7-to-graylog.conf + mode: '0644' + owner: root + group: root + notify: Restart rsyslog service + +- name: Set net.netfilter.nf_conntrack_max + ansible.posix.sysctl: + name: net.netfilter.nf_conntrack_max + value: 524288 + state: present + sysctl_file: /etc/sysctl.d/02-udp.conf + +- name: Set net.netfilter.nf_conntrack_udp_timeout + ansible.posix.sysctl: + name: net.netfilter.nf_conntrack_udp_timeout + value: 10 + state: present + sysctl_file: /etc/sysctl.d/02-udp.conf + +- name: Set net.netfilter.nf_conntrack_udp_timeout_stream + ansible.posix.sysctl: + name: net.netfilter.nf_conntrack_udp_timeout_stream + value: 30 + state: present + sysctl_file: /etc/sysctl.d/02-udp.conf diff --git a/roles/nginx/tasks/nginx-config.yml b/roles/nginx/tasks/nginx-config.yml index 6d87711..955fb0d 100644 --- a/roles/nginx/tasks/nginx-config.yml +++ b/roles/nginx/tasks/nginx-config.yml @@ -148,18 +148,12 @@ ansible.builtin.file: path: /etc/systemd/system/nginx.service.d state: directory - owner: root - group: root - mode: 0755 when: nginx_streams | default([]) | selectattr('transparent', 'defined') | selectattr('transparent') | list | length > 0 - name: Systemd override для nginx — CAP_NET_RAW (transparent proxy) ansible.builtin.template: src: nginx-systemd-override.j2 dest: /etc/systemd/system/nginx.service.d/transparent.conf - owner: root - group: root - mode: 0644 when: nginx_streams | default([]) | selectattr('transparent', 'defined') | selectattr('transparent') | list | length > 0 notify: - Daemon reload diff --git a/roles/nginx/tasks/nginx-stream.yml b/roles/nginx/tasks/nginx-stream.yml index 1f1c007..3ed7e30 100644 --- a/roles/nginx/tasks/nginx-stream.yml +++ b/roles/nginx/tasks/nginx-stream.yml @@ -9,14 +9,14 @@ state: directory owner: root group: root - mode: "0644" +# mode: "0755" - name: Install the nginx stream files ansible.builtin.template: - src: nginx-stream.conf.j2 + src: nginx-stream.j2 dest: /etc/nginx/stream.d/{{ item.stream }}.conf owner: root group: root - mode: "0644" +# mode: "0444" loop: "{{ nginx_streams }}" notify: Reload nginx diff --git a/roles/nginx/templates/nginx-maps.conf.j2 b/roles/nginx/templates/nginx-maps.conf.j2 new file mode 100644 index 0000000..e2a31b5 --- /dev/null +++ b/roles/nginx/templates/nginx-maps.conf.j2 @@ -0,0 +1,14 @@ +# MAPS +map $arg_action $is_yandexforms { + default 0; + YandexForms 1; +} +map $arg_class $is_yandexformshd { + default 0; + ru.bgcrm.dyn.yandex.YandexForms 1; +} + +map $arg_class $is_evotorordercallback { + default 0; + org.bgerp.plugin.custom.gigacoms.api.evotor.EvotorOrderCallback 1; +} diff --git a/roles/nginx/templates/nginx-proxy-params.conf.j2 b/roles/nginx/templates/nginx-proxy-params.conf.j2 index a3d7cad..8fca8aa 100644 --- a/roles/nginx/templates/nginx-proxy-params.conf.j2 +++ b/roles/nginx/templates/nginx-proxy-params.conf.j2 @@ -22,6 +22,7 @@ proxy_set_header X-Real-IP $remote_addr; {% if nginx_set_original_uri %} proxy_set_header nginx-request-uri $request_uri; {% endif %} +proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto {{ nginx_proxy_header_x_forwarded_proto }}; proxy_buffering {{ nginx_proxy_buffering }}; diff --git a/roles/nginx/templates/nginx-stream.j2 b/roles/nginx/templates/nginx-stream.j2 new file mode 100644 index 0000000..b11d6ba --- /dev/null +++ b/roles/nginx/templates/nginx-stream.j2 @@ -0,0 +1,26 @@ +# Ansible Managed: DO NOT EDIT +# Конфигурация для {{ item.stream }} + +upstream {{ item.stream }}_backend { +# hash $remote_addr; +{% for server in item.backends %} + server {{ server }} max_fails={{ max_fails | default(0) }}; +{% endfor %} +} + +server { + listen {{ item.listen_port }}{% if item.protocol == 'udp' %} udp reuseport{% endif %}; + proxy_pass {{ item.stream }}_backend; +{% if item.transparent | default(false) %} + proxy_bind $remote_addr transparent; +{% endif %} +{% if item.responses is defined %} + proxy_responses {{ item.responses }}; +{% endif %} +{% if item.timeout is defined %} + proxy_timeout {{ item.timeout }}; +{% endif %} +{% if item.connect_timeout is defined %} + proxy_connect_timeout {{ item.connect_timeout }}; +{% endif %} +} diff --git a/roles/nginx/templates/nginx-virthost.j2 b/roles/nginx/templates/nginx-virthost.j2 index 630a61f..4b0370e 100644 --- a/roles/nginx/templates/nginx-virthost.j2 +++ b/roles/nginx/templates/nginx-virthost.j2 @@ -27,10 +27,11 @@ upstream {{ u_bk.name }} { {% endif %} {% if item.plain_http_enabled | default(true) %} server { - listen {{ item.http_port | default ('80') }}; +{% for port in item.http_port | default(['80']) %} + listen {{ port }}; +{% endfor %} server_name {{ item.server_name }} {% if item.serveraliases is defined %}{{ item.serveraliases }}{% endif %}; - -{% if nginx_block_dotfiles %} +{% if item.nginx_block_dotfiles | default(true) %} location ~ /\.(?!well-known).* { deny all; access_log off; @@ -49,39 +50,39 @@ server { # Not needed since DNS-01 challenge installed #include /etc/nginx/snippets/letsencrypt-proxy.conf; {% endif %} +{% if item.access_log is defined %} + access_log {{ item.access_log }}; +{% else %} + access_log /var/log/nginx/{{ item.server_name }}_access.log; +{% endif %} +{% if item.error_log is defined %} + error_log {{ item.error_log }}; +{% else %} + error_log /var/log/nginx/{{ item.server_name }}_error.log; +{% endif %} + access_log syslog:server=unix:/dev/log,facility=local7,tag=nginx,severity=info graylog_json; + error_log syslog:server=unix:/dev/log,facility=local7,tag=nginx_error error; - {% if item.access_log is defined %} -access_log {{ item.access_log }}; - {% else %} -access_log /var/log/nginx/{{ item.server_name }}_access.log; - {% endif %} - {%- if item.error_log is defined %} -error_log {{ item.error_log }}; - {% else %} -error_log /var/log/nginx/{{ item.server_name }}_error.log; - {% endif %} - {% if nginx_set_xss_protection %} +{% if nginx_set_xss_protection %} proxy_hide_header X-XSS-Protection; add_header X-XSS-Protection "1; mode=block;"; - {% endif %} - {% if nginx_set_frame_origin %} +{% endif %} +{% if nginx_set_frame_origin %} proxy_hide_header X-Frame-Options; add_header X-Frame-Options "{{ nginx_x_frame_options }}"; - {% endif %} - {% if nginx_set_content_security_options %} +{% endif %} +{% if nginx_set_content_security_options %} proxy_hide_header Content-Security-Policy; - {% if nginx_disable_content_security_options %} +{% if nginx_disable_content_security_options %} add_header Content-Security-Policy ""; - {% else %} +{% else %} add_header Content-Security-Policy "frame-src{% for s in nginx_content_security_src_acl %} {{ s }}{% endfor %}; frame-ancestors{% for l in nginx_content_security_ancestor_acl %} {{ l }}{% endfor %};"; - {% endif %} - {% endif %} - {% if nginx_set_chunked_transfer_encoding -%} +{% endif %} +{% endif %} +{% if nginx_set_chunked_transfer_encoding %} chunked_transfer_encoding on; - {% endif %} - +{% endif %} server_tokens {{ item.server_tokens | default('off') }}; - {% if item.ssl_enabled and item.ssl_only %} location / { return 301 https://{{ item.server_name }}$request_uri; @@ -105,56 +106,50 @@ error_log /var/log/nginx/{{ item.server_name }}_error.log; log_not_found off; access_log off; } - {% if item.proxy_ips is defined %} +{% if item.proxy_ips is defined %} # We are behind proxy - {% for ip in item.proxy_ips %} +{% for ip in item.proxy_ips %} set_real_ip_from {{ ip }}; - {% endfor %} +{% endfor %} real_ip_header X-Forwarded-For; - {% endif %} +{% endif %} - {% if item.max_body is defined -%} -client_max_body_size {{ item.max_body }}; - {% else %} -client_max_body_size {{ nginx_client_max_body_size }}; - {% endif %} - - {% if item.body_timeout is defined -%} -client_body_timeout {{ item.body_timeout }}; - {% else %} -client_body_timeout {{ nginx_client_body_timeout }}; - {% endif %} - - {% if nginx_cors_enabled %} - {% if nginx_cors_global %} +{% if item.max_body is defined %} + client_max_body_size {{ item.max_body }}; +{% else %} + client_max_body_size {{ nginx_client_max_body_size }}; +{% endif %} +{% if item.body_timeout is defined %} + client_body_timeout {{ item.body_timeout }}; +{% else %} + client_body_timeout {{ nginx_client_body_timeout }}; +{% endif %} +{% if nginx_cors_enabled %} +{% if nginx_cors_global %} include /etc/nginx/snippets/nginx-cors.conf; - {% endif %} - {% endif %} - - {%- if item.additional_options is defined %} - {% for add_opt in item.additional_options %} - {{ add_opt }}; - {% endfor %} - {% endif %} - - {%- if item.http_acls is defined %} - {% for acl in item.http_acls %} +{% endif %} +{% endif %} +{% if item.additional_options is defined %} +{% for add_opt in item.additional_options %} + {{ add_opt }} +{% endfor %} +{% endif %} +{% if item.http_acls is defined %} +{% for acl in item.http_acls %} {{ acl }}; - {% endfor %} - {% endif %} - - {%- if nginx_websockets_support is defined and nginx_websockets_support %} +{% endfor %} +{% endif %} +{% if nginx_websockets_support is defined and nginx_websockets_support %} proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - {% else %} - {%- if item.websockets is defined and item.websockets %} +{% else %} +{% if item.websockets is defined and item.websockets %} proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - {% endif %} - {% endif %} - - {% if item.proxy_standard_setup is defined and item.proxy_standard_setup %} -# Proxy stuff +{% endif %} +{% endif %} +{% if item.proxy_standard_setup is defined and item.proxy_standard_setup %} + # Proxy stuff {% if item.include_global_proxy_conf is defined and not item.include_global_proxy_conf %} {% else %} include /etc/nginx/snippets/nginx-proxy-params.conf; @@ -165,74 +160,75 @@ client_body_timeout {{ nginx_client_body_timeout }}; {% for location in item.locations %} location {{ location.location }} { {% if nginx_cors_enabled %} - {% if not nginx_cors_global %} - {% if location.cors is defined and location.cors %} +{% if not nginx_cors_global %} +{% if location.cors is defined and location.cors %} include /etc/nginx/snippets/nginx-cors.conf; - {% endif %} - {% endif %} +{% endif %} +{% endif %} {% endif %} {% if location.target is defined %} -proxy_pass {{ location.target }}; + proxy_pass {{ location.target }}; {% elif location.php_target is defined %} - try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass {% if phpfpm_listen_on_socket is defined and phpfpm_listen_on_socket %}unix:{% endif %}{{ location.php_target }}; - fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param REMOTE_ADDR $http_x_forwarded_for; - #fastcgi_param REMOTE_ADDR $remote_addr; - include fastcgi_params; + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {% if phpfpm_listen_on_socket is defined and phpfpm_listen_on_socket %}unix:{% endif %}{{ location.php_target }}; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param REMOTE_ADDR $http_x_forwarded_for; + #fastcgi_param REMOTE_ADDR $remote_addr; + include fastcgi_params; {% endif %} - {% if location.websockets is defined and location.websockets %} - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; {% endif %} - {% if location.extra_conf is defined %} - {{ location.extra_conf }} +{% for add_opt in location.extra_conf %} + {{ add_opt }} +{% endfor %} {% endif %} {% if location.acls is defined %} - {% for acl in location.acls %} - {{ acl }}; - {% endfor %} +{% for acl in location.acls %} + {{ acl }}; +{% endfor %} +{% endif %} +{% if location.other_opts is defined %} +{% for opt in location.other_opts %} + {{ opt }}; +{% endfor %} +{% endif %} + } +{% endfor %} +{% endif %} +{% if item.extra_parameters is defined %} + {{ item.extra_parameters }} {% endif %} - - {% if location.other_opts is defined %} - {% for opt in location.other_opts %} - {{ opt }}; - {% endfor %} - {% endif %} - } - {% endfor %} - {% endif %} - - {% if item.extra_parameters is defined %} - {{ item.extra_parameters }} - {% endif %} {% endif %} } {% endif %} + {% if item.ssl_enabled %} ############################################ SSL SECTION ###################################### - server { - listen {% if item.https_port is defined %} {{ item.https_port }} {% else %} {{ https_port | default('443') }} {% endif %} ssl; - {% if ansible_distribution_release != "trusty" %}http2 on;{% endif %} - +{% for port in item.https_port | default(['443']) %} + listen {{ port }} ssl; +{% endfor %} +{% if ansible_distribution_release != "trusty" %} + http2 on; +{% endif %} server_name {{ item.server_name }} {% if item.serveraliases is defined %}{{ item.serveraliases }}{% endif %}; - - {% if item.access_log is defined %} -access_log {{ item.access_log }}; - {% else %} -access_log /var/log/nginx/{{ item.server_name }}_ssl_access.log; - {% endif %} - - {%- if item.error_log is defined %} -error_log {{ item.error_log }}; - {% else %} -error_log /var/log/nginx/{{ item.server_name }}_ssl_error.log; - {% endif %} +{% if item.access_log is defined %} + access_log {{ item.access_log }}; +{% else %} + access_log /var/log/nginx/{{ item.server_name }}_ssl_access.log; +{% endif %} +{% if item.error_log is defined %} + error_log {{ item.error_log }}; +{% else %} + error_log /var/log/nginx/{{ item.server_name }}_ssl_error.log; +{% endif %} + access_log syslog:server=unix:/dev/log,facility=local7,tag=nginx,severity=info graylog_json; + error_log syslog:server=unix:/dev/log,facility=local7,tag=nginx_error error; root {{ item.root | default('/usr/share/nginx/html/') }}; index {{ item.index | default('index.html index.htm') }}; @@ -240,94 +236,82 @@ error_log /var/log/nginx/{{ item.server_name }}_ssl_error.log; location = /50x.html { root {{ item.error_path | default('/usr/share/nginx/html') }}; } - - {%- if haproxy_ips is defined %} +{% if haproxy_ips is defined %} # We are behind haproxy - {% for ip in haproxy_ips %} +{% for ip in haproxy_ips %} set_real_ip_from {{ ip }}; - {% endfor %} +{% endfor %} real_ip_header X-Forwarded-For; - {% endif %} - - {% if item.max_body is defined %} -client_max_body_size {{ item.max_body }}; - {% else %} -client_max_body_size {{ nginx_client_max_body_size }}; - {% endif %} - {%- if item.body_timeout is defined %} -client_body_timeout {{ item.body_timeout }}; - {% else %} -client_body_timeout {{ nginx_client_body_timeout }}; - {%- endif %} - +{% endif %} +{% if item.max_body is defined %} + client_max_body_size {{ item.max_body }}; +{% else %} + client_max_body_size {{ nginx_client_max_body_size }}; +{% endif %} +{% if item.body_timeout is defined %} + client_body_timeout {{ item.body_timeout }}; +{% else %} + client_body_timeout {{ nginx_client_body_timeout }}; +{% endif %} {% if letsencrypt_acme_install is defined and letsencrypt_acme_install %} ssl_certificate {{ letsencrypt_acme_certs_dir }}/{{ item.server_name }}/fullchain.pem; ssl_certificate_key {{ letsencrypt_acme_certs_dir }}/{{ item.server_name }}/privkey.pem; {% else %} - ssl_certificate {{ nginx_ssl_cert_file | default('/etc/nginx/certs/server.crt') }}; - ssl_certificate_key {{ nginx_ssl_cert_key | default ('/etc/nginx/certs/server.key') }}; + ssl_certificate {{ item.nginx_ssl_cert_file | default('/etc/nginx/certs/server.crt') }}; + ssl_certificate_key {{ item.nginx_ssl_cert_key | default ('/etc/nginx/certs/server.key') }}; {% endif %} - include /etc/nginx/snippets/nginx-server-ssl.conf; - - {%- if nginx_set_xss_protection %} +{% if nginx_set_xss_protection %} proxy_hide_header X-XSS-Protection; add_header X-XSS-Protection "1; mode=block;"; - {% endif %} - {%- if nginx_set_frame_origin %} +{% endif %} +{% if nginx_set_frame_origin %} proxy_hide_header X-Frame-Options; add_header X-Frame-Options "{{ nginx_x_frame_options }}"; - {% endif %} - {%- if nginx_set_content_security_options %} +{% endif %} +{% if nginx_set_content_security_options %} proxy_hide_header Content-Security-Policy; - {%- if nginx_disable_content_security_options %} +{% if nginx_disable_content_security_options %} add_header Content-Security-Policy ""; - {% else %} +{% else %} add_header Content-Security-Policy "frame-src{% for s in nginx_content_security_src_acl %} {{ s }}{% endfor %}; frame-ancestors{% for l in nginx_content_security_ancestor_acl %} {{ l }}{% endfor %};"; - {% endif %} - {% endif %} - {%- if nginx_set_chunked_transfer_encoding %} +{% endif %} +{% endif %} +{% if nginx_set_chunked_transfer_encoding %} chunked_transfer_encoding on; - {% endif %} - +{% endif %} server_tokens {{ item.server_tokens | default('off') }}; - {% if nginx_cors_enabled %} - {%- if nginx_cors_global %} +{% if nginx_cors_enabled %} +{% if nginx_cors_global %} include /etc/nginx/snippets/nginx-cors.conf; - {% endif %} - {% endif %} - - {%- if nginx_websockets_support is defined and nginx_websockets_support %} +{% endif %} +{% endif %} +{% if nginx_websockets_support is defined and nginx_websockets_support %} proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - {% else %} - {%- if item.websockets is defined and item.websockets %} +{% else %} +{% if item.websockets is defined and item.websockets %} proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - {% endif %} - {% endif %} - - {%- if item.additional_options is defined %} - {%- for add_opt in item.additional_options %} - {{ add_opt }}; - {% endfor %} - {% endif %} - - {%- if item.https_acls is defined %} - {% for acl in item.https_acls %} +{% endif %} +{% endif %} +{% if item.additional_options is defined %} +{% for add_opt in item.additional_options %} + {{ add_opt }} +{% endfor %} +{% endif %} +{% if item.https_acls is defined %} +{% for acl in item.https_acls %} {{ acl }}; - {% endfor %} - {% endif %} - - {%- if item.proxy_standard_setup is defined and item.proxy_standard_setup %} - +{% endfor %} +{% endif %} +{% if item.proxy_standard_setup is defined and item.proxy_standard_setup %} # Proxy stuff - {% if item.include_global_proxy_conf is defined and not item.include_global_proxy_conf %} - {% else %} +{% if item.include_global_proxy_conf is defined and not item.include_global_proxy_conf %} +{% else %} include /etc/nginx/snippets/nginx-proxy-params.conf; - {% endif %} - {% endif %} - +{% endif %} +{% endif %} location = /favicon.ico { log_not_found off; access_log off; @@ -340,8 +324,7 @@ client_body_timeout {{ nginx_client_body_timeout }}; log_not_found off; access_log off; } - -{% if nginx_block_dotfiles %} +{% if item.nginx_block_dotfiles | default(true) %} location ~ /\.(?!well-known).* { deny all; access_log off; @@ -357,58 +340,52 @@ client_body_timeout {{ nginx_client_body_timeout }}; } {% endif %} - {% if item.locations is defined %} - {% for location in item.locations -%} +{% if item.locations is defined %} +{% for location in item.locations %} location {{ location.location }} { - - {%- if nginx_cors_enabled %} - {%- if not nginx_cors_global %} - {%- if location.cors is defined and location.cors %} - include /etc/nginx/snippets/nginx-cors.conf; - {% endif %} - {% endif %} - {% endif %} - - {% if location.target is defined %} - proxy_pass {{ location.target }}; - {% elif location.php_target is defined %} - try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass {% if phpfpm_listen_on_socket is defined and phpfpm_listen_on_socket %}unix:{% endif %}{{ location.php_target }}; - fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param REMOTE_ADDR $http_x_forwarded_for; - #fastcgi_param REMOTE_ADDR $remote_addr; - include fastcgi_params; - {% endif %} - - {%- if location.websockets is defined and location.websockets %} - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - {% endif %} - - {%- if location.extra_conf is defined %} - {{ location.extra_conf }} - {% endif %} - - {%- if location.acls is defined %} - {% for acl in location.acls %} - {{ acl }}; - {% endfor %} - {% endif %} - - {%- if location.other_opts is defined %} - {% for opt in location.other_opts %} - {{ opt }}; - {% endfor %} - {% endif %} - } - {% endfor %} - {% endif %} - - {%- if item.extra_parameters is defined %} - {{ item.extra_parameters }} - {% endif %} -} - +{% if nginx_cors_enabled %} +{% if not nginx_cors_global %} +{% if location.cors is defined and location.cors %} + include /etc/nginx/snippets/nginx-cors.conf; +{% endif %} +{% endif %} +{% endif %} +{% if location.target is defined %} + proxy_pass {{ location.target }}; +{% elif location.php_target is defined %} + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {% if phpfpm_listen_on_socket is defined and phpfpm_listen_on_socket %}unix:{% endif %}{{ location.php_target }}; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param REMOTE_ADDR $http_x_forwarded_for; + #fastcgi_param REMOTE_ADDR $remote_addr; + include fastcgi_params; +{% endif %} +{% if location.websockets is defined and location.websockets %} + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; +{% endif %} +{% if location.extra_conf is defined %} +{% for add_opt in location.extra_conf %} + {{ add_opt }} +{% endfor %} +{% endif %} +{% if location.acls is defined %} +{% for acl in location.acls %} + {{ acl }}; +{% endfor %} +{% endif %} +{% if location.other_opts is defined %} +{% for opt in location.other_opts %} + {{ opt }}; +{% endfor %} +{% endif %} + } +{% endfor %} +{% endif %} +{% if item.extra_parameters is defined %} + {{ item.extra_parameters }} +{% endif %} +} {% endif %} diff --git a/roles/nginx/templates/nginx.conf.j2 b/roles/nginx/templates/nginx.conf.j2 index 56825a1..0fb6158 100644 --- a/roles/nginx/templates/nginx.conf.j2 +++ b/roles/nginx/templates/nginx.conf.j2 @@ -30,6 +30,71 @@ http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; + + log_format graylog_json escape=json '{' +# '"time_local":"$time_iso8601",' +# '"remote_addr":"$remote_addr",' +# '"remote_user":"$remote_user",' +# '"body_bytes_sent":"$body_bytes_sent",' +# '"request_time":"$request_time",' +# '"status":"$status",' +# '"request":"$request",' +# '"request_method":"$request_method",' +# '"http_referrer":"$http_referer",' +# '"http_user_agent":"$http_user_agent"' + + # Core & Time Metadata + '"timestamp": "$time_iso8601", ' + '"connection_requests": $connection_requests, ' + + # Client & Network Info + '"remote_addr": "$remote_addr", ' + '"realip_remote_addr": "$realip_remote_addr", ' # Requires ngx_http_realip_module + '"remote_user": "$remote_user", ' + '"http_x_forwarded_for": "$http_x_forwarded_for", ' + '"server_name": "$server_name", ' + '"port": "$server_port", ' + + # Request Details + '"request_method": "$request_method", ' + '"request_uri": "$request_uri", ' + '"uri": "$uri", ' + '"query_string": "$args", ' + '"server_protocol": "$server_protocol", ' + '"http_referrer": "$http_referer", ' + '"http_user_agent": "$http_user_agent", ' + + # Security & SSL (Crucial for monitoring TLS versions) + '"ssl_protocol": "$ssl_protocol", ' + '"ssl_cipher": "$ssl_cipher", ' + '"ssl_session_reused": "$ssl_session_reused", ' + + # Performance & Compression Metrics + '"status": $status, ' + '"body_bytes_sent": $body_bytes_sent, ' + '"bytes_sent": $bytes_sent, ' + '"request_length": $request_length, ' + '"gzip_ratio": "$gzip_ratio", ' # Will be a string or "-" if gzip is off + + # Detailed NGINX Timings (In seconds with millisecond precision) + '"request_time": $request_time, ' # Total time spent on request + # '"ssl_handshake_time": "$ssl_handshake_time", ' # Requires NGINX 1.21.1+ + + # Upstream (Backend Application) Performance + '"upstream_addr": "$upstream_addr", ' + '"upstream_status": "$upstream_status", ' + '"upstream_response_time": "$upstream_response_time", ' + '"upstream_connect_time": "$upstream_connect_time", ' + '"upstream_header_time": "$upstream_header_time", ' + + # Cache Status (If using NGINX proxy_cache) + '"upstream_cache_status": "$upstream_cache_status", ' + + # Custom Security Context (Optional headers to trace specific sessions) + '"correlation_id": "$http_x_correlation_id", ' # Extracts custom tracing headers + '"cookie_session_id": "$cookie_sessionid"' # Extracts specific cookies + '}'; + sendfile on; tcp_nopush on; tcp_nodelay on; @@ -47,8 +112,13 @@ http { ## # Logging Settings ## - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; +# access_log /var/log/nginx/access.log; +# error_log /var/log/nginx/error.log; + access_log syslog:server=unix:/dev/log,facility=local7,tag=nginx,severity=info graylog_json; + error_log syslog:server=unix:/dev/log,facility=local7,tag=nginx_error error; + + map_hash_bucket_size 128; + map_hash_max_size 2048; {% if nginx_enable_compression %} include /etc/nginx/snippets/nginx-compression.conf; diff --git a/roles/nginx/templates/rsyslog.conf.j2 b/roles/nginx/templates/rsyslog.conf.j2 new file mode 100644 index 0000000..a598191 --- /dev/null +++ b/roles/nginx/templates/rsyslog.conf.j2 @@ -0,0 +1,7 @@ +if $programname == 'nginx' or $syslogtag == 'nginx' or $syslogtag == 'nginx_error' then { + # Отправка на удаленный сервер по протоколу UDP (одна @) или TCP (две @@) + action(type="omfwd" target="{{ graylog_server }}" port="{{ graylog_port }}" protocol="tcp") + + # Остановить дальнейшую обработку сообщения, чтобы оно не попало в messages + stop +}