feat(security): add SSH hardening, fail2ban tasks, connection check helper, and doc
This commit is contained in:
parent
c627f016bf
commit
126a19e282
108
docs/tldr-ssh-security.md
Normal file
108
docs/tldr-ssh-security.md
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
# TLDR: SSH Security & Hardening Playbook
|
||||||
|
|
||||||
|
Quick reference for SSH security hardening, firewall controls, Fail2ban management, and connection checking.
|
||||||
|
|
||||||
|
## 1. SSH Hardening (Key-Only Auth)
|
||||||
|
Password login is completely disabled for all users. Direct root login is restricted to key-only.
|
||||||
|
|
||||||
|
### Configuration file
|
||||||
|
Drop-in config is deployed to:
|
||||||
|
`/etc/ssh/sshd_config.d/00-disable-password.conf`
|
||||||
|
|
||||||
|
```text
|
||||||
|
PasswordAuthentication no
|
||||||
|
PubkeyAuthentication yes
|
||||||
|
KbdInteractiveAuthentication no
|
||||||
|
PermitRootLogin prohibit-password
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apply Changes
|
||||||
|
If you update SSH configurations, reload sshd:
|
||||||
|
```bash
|
||||||
|
# Debian/Ubuntu
|
||||||
|
sudo systemctl reload ssh
|
||||||
|
|
||||||
|
# RedHat/CentOS
|
||||||
|
sudo systemctl reload sshd
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Fail2ban Management
|
||||||
|
Fail2ban monitors SSH authentication failures and bans offensive IPs.
|
||||||
|
|
||||||
|
### Default Settings
|
||||||
|
* **Bantime**: 24 hours (`86400` seconds)
|
||||||
|
* **Findtime**: 10 minutes (`600` seconds)
|
||||||
|
* **Maxretry**: 3 attempts
|
||||||
|
|
||||||
|
### Useful Commands
|
||||||
|
```bash
|
||||||
|
# Check Fail2ban service status
|
||||||
|
sudo systemctl status fail2ban
|
||||||
|
|
||||||
|
# Check sshd jail status (banned IPs)
|
||||||
|
sudo fail2ban-client status sshd
|
||||||
|
|
||||||
|
# Unban a specific IP
|
||||||
|
sudo fail2ban-client set sshd unbanip <IP>
|
||||||
|
|
||||||
|
# Manually ban a specific IP
|
||||||
|
sudo fail2ban-client set sshd banip <IP>
|
||||||
|
|
||||||
|
# View fail2ban logs
|
||||||
|
sudo tail -f /var/log/fail2ban.log
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. SSH Proxy Connection Helper (`ssh_check.exp`)
|
||||||
|
A generic `expect` helper script to verify ProxyJump-ed SSH connectivity.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
To prevent password leaks in shell history (`~/.bash_history` or `~/.zsh_history`), **never** pass the password as a command-line argument. Instead, use one of the secure methods below:
|
||||||
|
|
||||||
|
#### Option A: Read securely from input (Recommended)
|
||||||
|
```bash
|
||||||
|
# Type your password securely (input will not echo on screen)
|
||||||
|
read -s SSH_CHECK_PASSWORD
|
||||||
|
export SSH_CHECK_PASSWORD
|
||||||
|
|
||||||
|
# Run the helper script (picks up password from env var)
|
||||||
|
ssh_check.exp admin@tky-proxy.svc.plus root@167.179.110.129
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option B: Set via env var with leading space
|
||||||
|
If your shell is configured to ignore commands starting with a space (e.g. `HISTCONTROL=ignorespace` in bash or `setopt HIST_IGNORE_SPACE` in zsh), you can set the variable with a leading space:
|
||||||
|
```bash
|
||||||
|
export SSH_CHECK_PASSWORD="your_password"
|
||||||
|
ssh_check.exp admin@tky-proxy.svc.plus root@167.179.110.129
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option C: Legacy/Direct (Not recommended, leaves history trace)
|
||||||
|
```bash
|
||||||
|
ssh_check.exp admin@tky-proxy.svc.plus root@167.179.110.129 "your_password"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Firewall (UFW) quick-ref
|
||||||
|
Used on hosts to manage ports (e.g. 80, 443, 1443).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View firewall rules with line numbers
|
||||||
|
sudo ufw status numbered
|
||||||
|
|
||||||
|
# Allow a port to Anywhere
|
||||||
|
sudo ufw allow 443/tcp
|
||||||
|
|
||||||
|
# Delete a rule by rule number
|
||||||
|
sudo ufw delete <rule_number>
|
||||||
|
|
||||||
|
# Restrict port 22 to a specific IP (e.g. Proxy IP)
|
||||||
|
sudo ufw allow from 43.207.194.92 to any port 22 proto tcp
|
||||||
|
sudo ufw delete allow 22/tcp
|
||||||
|
|
||||||
|
# Reload firewall
|
||||||
|
sudo ufw reload
|
||||||
|
```
|
||||||
45
roles/vhosts/common/files/ssh_check.exp
Normal file
45
roles/vhosts/common/files/ssh_check.exp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/expect -f
|
||||||
|
set timeout 30
|
||||||
|
set proxy [lindex $argv 0]
|
||||||
|
set target [lindex $argv 1]
|
||||||
|
|
||||||
|
# Retrieve password from environment variable (secure)
|
||||||
|
# Fallback to the third argument if environment variable is not set
|
||||||
|
if { [info exists ::env(SSH_CHECK_PASSWORD)] } {
|
||||||
|
set password $::env(SSH_CHECK_PASSWORD)
|
||||||
|
} else {
|
||||||
|
set password [lindex $argv 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
if { $proxy == "" || $target == "" || $password == "" } {
|
||||||
|
send_user "Error: Missing required parameters.\n"
|
||||||
|
send_user "Usage (Recommended): export SSH_CHECK_PASSWORD=\"your_password\"\n"
|
||||||
|
send_user " ssh_check.exp <proxy_user@host> <target_user@host>\n"
|
||||||
|
send_user "Usage (Legacy): ssh_check.exp <proxy_user@host> <target_user@host> <password>\n"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use UserKnownHostsFile=/dev/null to avoid modifying the local known_hosts file
|
||||||
|
spawn ssh -J $proxy -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $target
|
||||||
|
expect {
|
||||||
|
"password:" {
|
||||||
|
# Temporarily disable logging to hide the password from being echoed in stdout/logs
|
||||||
|
log_user 0
|
||||||
|
send "$password\r"
|
||||||
|
log_user 1
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
-re "(#|\\\$)" {
|
||||||
|
send_user "SUCCESS\n"
|
||||||
|
send "exit\n"
|
||||||
|
expect eof
|
||||||
|
}
|
||||||
|
timeout {
|
||||||
|
send_user "TIMEOUT\n"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
eof {
|
||||||
|
send_user "EOF_CLOSED\n"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,3 +12,13 @@
|
|||||||
- name: apt-update-cache
|
- name: apt-update-cache
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
update_cache: true
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Restart SSH
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: "{{ 'ssh' if ansible_facts.os_family == 'Debian' else 'sshd' }}"
|
||||||
|
state: reloaded
|
||||||
|
|
||||||
|
- name: Restart Fail2ban
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: fail2ban
|
||||||
|
state: restarted
|
||||||
|
|||||||
32
roles/vhosts/common/tasks/fail2ban.yml
Normal file
32
roles/vhosts/common/tasks/fail2ban.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
- name: Fail2ban | Install Fail2ban package
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: fail2ban
|
||||||
|
state: present
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Fail2ban | Deploy jail.local configuration
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/fail2ban/jail.local
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
content: |
|
||||||
|
[DEFAULT]
|
||||||
|
bantime = 86400
|
||||||
|
findtime = 600
|
||||||
|
maxretry = 3
|
||||||
|
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = ssh
|
||||||
|
become: true
|
||||||
|
notify: Restart Fail2ban
|
||||||
|
|
||||||
|
- name: Fail2ban | Ensure service is started and enabled
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: fail2ban
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
become: true
|
||||||
|
when: not ansible_check_mode
|
||||||
32
roles/vhosts/common/tasks/harden_ssh.yml
Normal file
32
roles/vhosts/common/tasks/harden_ssh.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
- name: SSH | Ensure sshd drop-in directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/ssh/sshd_config.d
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: SSH | Write sshd hardening drop-in (Disable password authentication for all users)
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/ssh/sshd_config.d/00-disable-password.conf
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
content: |
|
||||||
|
PasswordAuthentication no
|
||||||
|
PubkeyAuthentication yes
|
||||||
|
KbdInteractiveAuthentication no
|
||||||
|
PermitRootLogin prohibit-password
|
||||||
|
become: true
|
||||||
|
notify: Restart SSH
|
||||||
|
|
||||||
|
- name: SSH | Deploy ssh_check.exp helper script
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: files/ssh_check.exp
|
||||||
|
dest: /usr/local/bin/ssh_check.exp
|
||||||
|
mode: "0755"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
become: true
|
||||||
@ -32,6 +32,14 @@
|
|||||||
ansible.builtin.script: files/secure_ssh.sh
|
ansible.builtin.script: files/secure_ssh.sh
|
||||||
become: true
|
become: true
|
||||||
|
|
||||||
|
- name: Base | harden ssh config
|
||||||
|
ansible.builtin.import_tasks: harden_ssh.yml
|
||||||
|
tags: [ssh, security]
|
||||||
|
|
||||||
|
- name: Base | configure fail2ban
|
||||||
|
ansible.builtin.import_tasks: fail2ban.yml
|
||||||
|
tags: [fail2ban, security]
|
||||||
|
|
||||||
- name: Base | file limits
|
- name: Base | file limits
|
||||||
ansible.builtin.import_tasks: limits.yml
|
ansible.builtin.import_tasks: limits.yml
|
||||||
when:
|
when:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user