init
This commit is contained in:
14
ansible/roles/harden/fail2ban/handlers/main.yml
Normal file
14
ansible/roles/harden/fail2ban/handlers/main.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: validate fail2ban config
|
||||
listen: "validate and restart fail2ban"
|
||||
become: true
|
||||
ansible.builtin.command: fail2ban-client -t
|
||||
register: f2b_validate
|
||||
changed_when: false
|
||||
|
||||
- name: restart fail2ban
|
||||
listen: "validate and restart fail2ban"
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: fail2ban
|
||||
state: restarted
|
||||
58
ansible/roles/harden/fail2ban/tasks/main.yml
Normal file
58
ansible/roles/harden/fail2ban/tasks/main.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
- name: install fail2ban + deps
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- fail2ban
|
||||
- python3
|
||||
- python3-systemd
|
||||
- nftables
|
||||
state: present
|
||||
update_cache: true
|
||||
become: true
|
||||
|
||||
- name: enable & start nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
enabled: true
|
||||
state: started
|
||||
become: true
|
||||
|
||||
- name: ensure fail2ban directories exist
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
loop:
|
||||
- /etc/fail2ban
|
||||
- /etc/fail2ban/jail.d
|
||||
- /etc/fail2ban/filter.d
|
||||
become: true
|
||||
|
||||
- name: deploy /etc/fail2ban/fail2ban.local
|
||||
ansible.builtin.template:
|
||||
src: fail2ban.local.j2
|
||||
dest: /etc/fail2ban/fail2ban.local
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: validate and restart fail2ban
|
||||
become: true
|
||||
|
||||
- name: deploy /etc/fail2ban/jail.local
|
||||
ansible.builtin.template:
|
||||
src: jail.local.j2
|
||||
dest: /etc/fail2ban/jail.local
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: validate and restart fail2ban
|
||||
become: true
|
||||
|
||||
- name: ensure fail2ban enabled and started
|
||||
ansible.builtin.systemd:
|
||||
name: fail2ban
|
||||
enabled: true
|
||||
state: started
|
||||
become: true
|
||||
@@ -0,0 +1,6 @@
|
||||
[Definition]
|
||||
loglevel = INFO
|
||||
logtarget = /var/log/fail2ban.log
|
||||
socket = /run/fail2ban/fail2ban.sock
|
||||
pidfile = /run/fail2ban/fail2ban.pid
|
||||
dbpurgeage = 86400
|
||||
18
ansible/roles/harden/fail2ban/templates/jail.local.j2
Normal file
18
ansible/roles/harden/fail2ban/templates/jail.local.j2
Normal file
@@ -0,0 +1,18 @@
|
||||
[DEFAULT]
|
||||
ignoreip = 127.0.0.1/8 ::1
|
||||
|
||||
findtime = 600
|
||||
maxretry = 5
|
||||
bantime = 1h
|
||||
|
||||
backend = systemd
|
||||
banaction = nftables[type=multiport]
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = 25105
|
||||
filter = sshd
|
||||
maxretry = 5
|
||||
findtime = 600
|
||||
bantime = 1h
|
||||
mode = aggressive
|
||||
12
ansible/roles/harden/nftables/handlers/main.yml
Normal file
12
ansible/roles/harden/nftables/handlers/main.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: validate nftables config
|
||||
ansible.builtin.command:
|
||||
cmd: nft -c -f /etc/nftables.conf
|
||||
listen: apply nftables
|
||||
changed_when: false
|
||||
|
||||
- name: reload nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
listen: apply nftables
|
||||
22
ansible/roles/harden/nftables/tasks/main.yml
Normal file
22
ansible/roles/harden/nftables/tasks/main.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
- name: install nftables
|
||||
ansible.builtin.apt:
|
||||
name: nftables
|
||||
state: present
|
||||
update_cache: true
|
||||
notify: apply nftables
|
||||
|
||||
- name: deploy nftables config
|
||||
ansible.builtin.template:
|
||||
src: "{{ nftables_conf_name }}"
|
||||
dest: /etc/nftables.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: apply nftables
|
||||
|
||||
- name: enable and start nftables service
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
enabled: true
|
||||
state: started
|
||||
36
ansible/roles/harden/nftables/templates/proxmox-nftables.j2
Normal file
36
ansible/roles/harden/nftables/templates/proxmox-nftables.j2
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority 0;
|
||||
policy drop;
|
||||
|
||||
iif "lo" accept
|
||||
ct state established,related accept
|
||||
|
||||
# SSH
|
||||
tcp dport {{ ssh_port }} accept
|
||||
|
||||
# ICMP
|
||||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
|
||||
# Proxmox Web/API (LAN only)
|
||||
ip saddr 192.168.0.0/24 tcp dport 8006 accept
|
||||
|
||||
# NTP
|
||||
ip saddr 192.168.0.0/24 udp dport {{ ntp_port }} accept
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority 0;
|
||||
policy drop;
|
||||
}
|
||||
|
||||
chain output {
|
||||
type filter hook output priority 0;
|
||||
policy accept;
|
||||
}
|
||||
}
|
||||
32
ansible/roles/harden/nftables/templates/vm-nftables.conf.j2
Normal file
32
ansible/roles/harden/nftables/templates/vm-nftables.conf.j2
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority 0;
|
||||
policy drop;
|
||||
|
||||
iif "lo" accept
|
||||
ct state established,related accept
|
||||
|
||||
# SSH
|
||||
tcp dport {{ ssh_port }} accept
|
||||
|
||||
# udp dport {{ ntp_port }} accept
|
||||
|
||||
# ICMP
|
||||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority 0;
|
||||
policy drop;
|
||||
}
|
||||
|
||||
chain output {
|
||||
type filter hook output priority 0;
|
||||
policy accept;
|
||||
}
|
||||
}
|
||||
25
ansible/roles/harden/sshd_config/tasks/main.yml
Normal file
25
ansible/roles/harden/sshd_config/tasks/main.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
- name: ensure sshd_config.d directory exists
|
||||
become: true
|
||||
file:
|
||||
path: "/etc/ssh/sshd_config.d"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: deploy sshd config file
|
||||
become: true
|
||||
template:
|
||||
src: "00-sshd_config-hardening.conf.j2"
|
||||
dest: "/etc/ssh/sshd_config.d/00-sshd_config-hardening.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
validate: "sshd -t -f %s"
|
||||
|
||||
- name: restart SSH service
|
||||
become: true
|
||||
service:
|
||||
name: ssh
|
||||
state: restarted
|
||||
@@ -0,0 +1,107 @@
|
||||
# --- MAIN ---
|
||||
|
||||
# Change default port 22 → {{ ssh_port }} (reduces noise from scanners)
|
||||
Port {{ ssh_port }}
|
||||
|
||||
# Optionally limit interfaces (default is all)
|
||||
# ListenAddress 0.0.0.0 # IPv4
|
||||
# ListenAddress :: # IPv6
|
||||
|
||||
# Allow only SSH protocol version 2 (v1 is insecure)
|
||||
Protocol 2
|
||||
|
||||
|
||||
# --- AUTHENTICATION ---
|
||||
|
||||
# Disable root login (only via sudo)
|
||||
PermitRootLogin prohibit-password
|
||||
|
||||
# Disable password login (keys only)
|
||||
PasswordAuthentication no
|
||||
|
||||
# Disable interactive keyboard auth (OTP, TOTP, etc.)
|
||||
KbdInteractiveAuthentication no
|
||||
|
||||
# Disable challenge-response auth (legacy)
|
||||
ChallengeResponseAuthentication no
|
||||
|
||||
# Enable public key authentication (main method)
|
||||
PubkeyAuthentication yes
|
||||
|
||||
|
||||
# --- ACCESS ---
|
||||
|
||||
# Allow only specific user
|
||||
# AllowUsers adminuser
|
||||
# Or alternatively allow a group:
|
||||
# AllowGroups sshusers
|
||||
|
||||
|
||||
# --- FUNCTION RESTRICTIONS ---
|
||||
|
||||
# Disallow empty passwords
|
||||
PermitEmptyPasswords no
|
||||
|
||||
# Disallow user environment modification (~/.ssh/environment)
|
||||
PermitUserEnvironment no
|
||||
|
||||
# Disable X11 forwarding (no GUI sessions)
|
||||
X11Forwarding no
|
||||
|
||||
# Disable TCP forwarding (no tunnels)
|
||||
AllowTcpForwarding yes
|
||||
|
||||
# Disable gateway ports (no external binding)
|
||||
GatewayPorts no
|
||||
|
||||
# Disable VPN tunnels via SSH
|
||||
PermitTunnel no
|
||||
|
||||
# Disable SSH agent forwarding
|
||||
AllowAgentForwarding yes
|
||||
|
||||
|
||||
# --- ANTI-BRUTEFORCE & STABILITY ---
|
||||
|
||||
# Login timeout (20 seconds)
|
||||
LoginGraceTime 20
|
||||
|
||||
# Max 3 auth attempts per connection
|
||||
MaxAuthTries 3
|
||||
|
||||
# Limit simultaneous connections
|
||||
# Allow 10 new, start dropping at 30, max 60 queued
|
||||
MaxStartups 10:30:60
|
||||
|
||||
|
||||
# --- SESSION ACTIVITY ---
|
||||
|
||||
# Ping client every 300s (5 minutes)
|
||||
ClientAliveInterval 300
|
||||
|
||||
# Disconnect if no response twice
|
||||
ClientAliveCountMax 2
|
||||
|
||||
# Disable TCP keepalive
|
||||
TCPKeepAlive no
|
||||
|
||||
# Skip DNS checks for faster login
|
||||
UseDNS no
|
||||
|
||||
|
||||
# --- SFTP ---
|
||||
|
||||
# Use internal SFTP subsystem
|
||||
Subsystem sftp internal-sftp
|
||||
|
||||
|
||||
# --- CRYPTOGRAPHY (optional) ---
|
||||
|
||||
# Modern key exchange algorithms (if supported)
|
||||
# KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256
|
||||
|
||||
# Modern ciphers
|
||||
# Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
|
||||
|
||||
# Modern MAC algorithms
|
||||
# MACs umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com
|
||||
15
ansible/roles/harden/unattended_upgrades/handlers/main.yml
Normal file
15
ansible/roles/harden/unattended_upgrades/handlers/main.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: restart unattended-upgrades
|
||||
ansible.builtin.service:
|
||||
name: unattended-upgrades
|
||||
state: restarted
|
||||
enabled: true
|
||||
|
||||
- name: restart apt timers
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: restarted
|
||||
enabled: true
|
||||
loop:
|
||||
- apt-daily.timer
|
||||
- apt-daily-upgrade.timer
|
||||
17
ansible/roles/harden/unattended_upgrades/readme.md
Normal file
17
ansible/roles/harden/unattended_upgrades/readme.md
Normal file
@@ -0,0 +1,17 @@
|
||||
```bash
|
||||
## Проверить, что таймеры включены и “тикают”
|
||||
systemctl status apt-daily.timer apt-daily-upgrade.timer
|
||||
systemctl list-timers --all | egrep 'apt-daily|apt-daily-upgrade'
|
||||
|
||||
## Проверить, что unattended-upgrades реально запускался
|
||||
systemctl status unattended-upgrades.service
|
||||
journalctl -u unattended-upgrades --no-pager -n 200
|
||||
|
||||
## Проверить логи и фактические действия
|
||||
ls -l /var/log/unattended-upgrades/
|
||||
tail -n 200 /var/log/unattended-upgrades/unattended-upgrades.log
|
||||
tail -n 200 /var/log/unattended-upgrades/unattended-upgrades-dpkg.log
|
||||
|
||||
## Быстрый “самотест” (прогон в dry-run)
|
||||
unattended-upgrade --dry-run --debug
|
||||
```
|
||||
49
ansible/roles/harden/unattended_upgrades/tasks/main.yml
Normal file
49
ansible/roles/harden/unattended_upgrades/tasks/main.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
- name: ensure required packages are present
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
- gpg
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: ensure debian-security repo is present
|
||||
ansible.builtin.apt_repository:
|
||||
repo: >-
|
||||
deb http://deb.debian.org/debian-security
|
||||
{{ ansible_facts.lsb.codename | default(ansible_facts.distribution_release) }}-security
|
||||
main contrib non-free non-free-firmware
|
||||
state: present
|
||||
filename: debian-security
|
||||
update_cache: true
|
||||
notify: restart apt timers
|
||||
|
||||
- name: deploy /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
ansible.builtin.template:
|
||||
src: 50unattended-upgrades.j2
|
||||
dest: /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: restart unattended-upgrades
|
||||
|
||||
- name: deploy /etc/apt/apt.conf.d/20auto-upgrades
|
||||
ansible.builtin.template:
|
||||
src: 20auto-upgrades.j2
|
||||
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify:
|
||||
- restart unattended-upgrades
|
||||
- restart apt timers
|
||||
|
||||
- name: enable & start apt timers
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: started
|
||||
enabled: true
|
||||
loop:
|
||||
- apt-daily.timer
|
||||
- apt-daily-upgrade.timer
|
||||
@@ -0,0 +1,4 @@
|
||||
APT::Periodic::Update-Package-Lists "1";
|
||||
APT::Periodic::Download-Upgradeable-Packages "1";
|
||||
APT::Periodic::Unattended-Upgrade "1";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
@@ -0,0 +1,10 @@
|
||||
Unattended-Upgrade::Origins-Pattern {
|
||||
"origin=Debian,codename=${distro_codename}-security";
|
||||
};
|
||||
|
||||
Unattended-Upgrade::Automatic-Reboot "false";
|
||||
Unattended-Upgrade::Automatic-Reboot-Time "03:30";
|
||||
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
|
||||
|
||||
Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
Unattended-Upgrade::MinimalSteps "true";
|
||||
Reference in New Issue
Block a user