diff --git a/infra/ansible/playbook.yml b/infra/ansible/playbook.yml index 01f1b0f..b8c8027 100644 --- a/infra/ansible/playbook.yml +++ b/infra/ansible/playbook.yml @@ -52,8 +52,14 @@ - traceroute - ncdu - prometheus-node-exporter + - fail2ban + - tzdata state: present + - name: Установить часовой пояс Europe/Moscow + timezone: + name: Europe/Moscow + - name: Проверить существование swap-файла stat: path: /swapfile @@ -99,6 +105,77 @@ state: present create: yes + # --- НАСТРОЙКА БЕЗОПАСНОСТИ ЯДРА --- + - name: Настроить параметры безопасности ядра + sysctl: + name: "{{ item.name }}" + value: "{{ item.value }}" + state: present + reload: yes + loop: + # Защита от DDoS + - { name: "net.ipv4.tcp_syn_retries", value: "2" } + - { name: "net.ipv4.tcp_synack_retries", value: "2" } + - { name: "net.ipv4.tcp_max_syn_backlog", value: "2048" } + - { name: "net.ipv4.tcp_fin_timeout", value: "15" } + - { name: "net.ipv4.tcp_keepalive_time", value: "1200" } + - { name: "net.ipv4.tcp_keepalive_intvl", value: "15" } + - { name: "net.ipv4.tcp_keepalive_probes", value: "5" } + - { name: "net.core.netdev_max_backlog", value: "1000" } + - { name: "net.core.somaxconn", value: "65535" } + # Защита от IP спуфинга + - { name: "net.ipv4.conf.all.accept_source_route", value: "0" } + - { name: "net.ipv4.conf.default.accept_source_route", value: "0" } + - { name: "net.ipv6.conf.all.accept_source_route", value: "0" } + - { name: "net.ipv6.conf.default.accept_source_route", value: "0" } + # Защита от фрагментации + - { name: "net.ipv4.conf.all.log_martians", value: "1" } + - { name: "net.ipv4.conf.default.log_martians", value: "1" } + - { name: "net.ipv4.icmp_echo_ignore_broadcasts", value: "1" } + - { name: "net.ipv4.icmp_ignore_bogus_error_responses", value: "1" } + - { name: "net.ipv4.tcp_syncookies", value: "1" } + - { name: "net.ipv4.conf.all.rp_filter", value: "1" } + - { name: "net.ipv4.conf.default.rp_filter", value: "1" } + # Для Docker + - { name: "kernel.pid_max", value: "65536" } + - { name: "kernel.threads-max", value: "4096" } + - { name: "vm.max_map_count", value: "262144" } + + - name: Сохранить параметры безопасности в /etc/sysctl.conf + lineinfile: + path: /etc/sysctl.conf + regexp: "^{{ item.name }}\\s*=" + line: "{{ item.name }} = {{ item.value }}" + state: present + loop: + # Защита от DDoS + - { name: "net.ipv4.tcp_syn_retries", value: "2" } + - { name: "net.ipv4.tcp_synack_retries", value: "2" } + - { name: "net.ipv4.tcp_max_syn_backlog", value: "2048" } + - { name: "net.ipv4.tcp_fin_timeout", value: "15" } + - { name: "net.ipv4.tcp_keepalive_time", value: "1200" } + - { name: "net.ipv4.tcp_keepalive_intvl", value: "15" } + - { name: "net.ipv4.tcp_keepalive_probes", value: "5" } + - { name: "net.core.netdev_max_backlog", value: "1000" } + - { name: "net.core.somaxconn", value: "65535" } + # Защита от IP спуфинга + - { name: "net.ipv4.conf.all.accept_source_route", value: "0" } + - { name: "net.ipv4.conf.default.accept_source_route", value: "0" } + - { name: "net.ipv6.conf.all.accept_source_route", value: "0" } + - { name: "net.ipv6.conf.default.accept_source_route", value: "0" } + # Защита от фрагментации + - { name: "net.ipv4.conf.all.log_martians", value: "1" } + - { name: "net.ipv4.conf.default.log_martians", value: "1" } + - { name: "net.ipv4.icmp_echo_ignore_broadcasts", value: "1" } + - { name: "net.ipv4.icmp_ignore_bogus_error_responses", value: "1" } + - { name: "net.ipv4.tcp_syncookies", value: "1" } + - { name: "net.ipv4.conf.all.rp_filter", value: "1" } + - { name: "net.ipv4.conf.default.rp_filter", value: "1" } + # Для Docker + - { name: "kernel.pid_max", value: "65536" } + - { name: "kernel.threads-max", value: "4096" } + - { name: "vm.max_map_count", value: "262144" } + - name: Проверить статус swap command: swapon --show register: swap_status @@ -151,6 +228,12 @@ port: "22" proto: tcp + - name: Разрешить новый SSH порт (15722) перед включением UFW + ufw: + rule: allow + port: "15722" + proto: tcp + - name: Настроить политику UFW по умолчанию ufw: policy: deny @@ -171,6 +254,8 @@ - "9090" # Prometheus - "3000" # Grafana - "9100" # Node Exporter + - "80" # HTTP + - "443" # HTTPS - name: Проверить существование пользователя deploy getent: @@ -216,11 +301,16 @@ line: "{{ item.line }}" backup: yes loop: + - { regexp: "Port", line: "Port 15722" } - { regexp: "PermitRootLogin", line: "PermitRootLogin no" } - { regexp: "PasswordAuthentication", line: "PasswordAuthentication no" } - { regexp: "PubkeyAuthentication", line: "PubkeyAuthentication yes" } + - { regexp: "AllowUsers", line: "AllowUsers {{ deploy_user }}" } notify: reload ssh + - name: Переподключиться по новому SSH порту + meta: reset_connection + - name: Удалить /home/prod, если требуется (чистое развертывание) file: path: "{{ project_root }}" @@ -483,6 +573,79 @@ become_user: "{{ deploy_user }}" tags: ["start_bots"] + # --- НАСТРОЙКА FAIL2BAN --- + - name: Создать конфигурацию Fail2ban для SSH + copy: + content: | + [sshd] + enabled = true + port = 15722 + filter = sshd + logpath = /var/log/auth.log + maxretry = 3 + bantime = 3600 + findtime = 600 + dest: /etc/fail2ban/jail.d/sshd.local + owner: root + group: root + mode: '0644' + + - name: Создать конфигурацию Fail2ban для Nginx + copy: + content: | + [nginx-http-auth] + enabled = true + port = http,https + filter = nginx-http-auth + logpath = /var/log/nginx/error.log + maxretry = 3 + bantime = 3600 + findtime = 600 + + [nginx-limit-req] + enabled = true + port = http,https + filter = nginx-limit-req + logpath = /var/log/nginx/error.log + maxretry = 3 + bantime = 3600 + findtime = 600 + dest: /etc/fail2ban/jail.d/nginx.local + owner: root + group: root + mode: '0644' + + - name: Создать конфигурацию Fail2ban для Docker + copy: + content: | + [docker] + enabled = true + port = 2375,2376 + filter = docker + logpath = /var/log/syslog + maxretry = 3 + bantime = 3600 + findtime = 600 + dest: /etc/fail2ban/jail.d/docker.local + owner: root + group: root + mode: '0644' + + - name: Включить и запустить Fail2ban + systemd: + name: fail2ban + enabled: yes + state: started + + - name: Проверить статус Fail2ban + command: fail2ban-client status + register: fail2ban_status + changed_when: false + + - name: Показать статус Fail2ban + debug: + var: fail2ban_status.stdout_lines + # --- НОВОЕ: Проверка портов --- - name: Пауза на 30 секунд — дать контейнерам запуститься pause: @@ -532,24 +695,16 @@ retries: 5 delay: 10 - - name: Проверить, что дашборд Node Exporter загружен в Grafana - uri: - url: "http://{{ ansible_host }}:3000/api/search?query=Node%20Exporter%20Full" - method: GET - user: "{{ grafana_admin_user | default('admin') }}" - password: "{{ grafana_admin_password | default('admin') }}" - status_code: 200 - register: dashboard_check - retries: 3 - delay: 5 - - name: Показать информацию о найденных дашбордах - debug: - var: dashboard_check.json + - name: Закрыть старый SSH порт 22 в UFW (финальный шаг) + ufw: + rule: deny + port: "22" + proto: tcp - name: Проверка запуска ботов завершена — всё работает 🟢 debug: - msg: "Все сервисы запущены и слушают нужные порты." + msg: "Все сервисы запущены и слушают нужные порты. SSH настроен на порт 15722, Fail2ban активен, параметры безопасности ядра применены. Порт 22 закрыт для безопасности." # handlers для перезагрузки сервисов handlers: