Add reusable Debian and Ubuntu hardening playbooks
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Host hardening
|
||||
|
||||
The playbooks target Debian or Ubuntu hosts using systemd. They create a separate admin account with password-required sudo and SSH public key access, configure Fail2ban and UFW, enable unattended security updates, disable SSH root and password login, and move SSH to a configurable port. The default replacement port is `2222`; changing it reduces scan noise but is not a security boundary.
|
||||
|
||||
## Configure inventory
|
||||
|
||||
Copy the tracked inventory examples as shown in [README.md](../README.md). Add each server under the `debian_servers` group in `inventory/hosts.yml`. Set `ansible_user` to an existing account with sudo access and `ansible_port` to its current SSH port. Put the admin public key in `inventory/host_vars/<alias>.yml`. Set `admin_user`, `ssh_port`, and `ssh_legacy_port` in the shared settings or override them per host. Every active inventory `.yml` file is ignored by Git.
|
||||
|
||||
Create a SHA-512 crypt password hash with `openssl passwd -6`, which prompts for the password. Copy `inventory/group_vars/all/vault.yml.example` to `inventory/group_vars/all/vault.yml`, insert the hash, and encrypt the file:
|
||||
|
||||
```sh
|
||||
ansible-vault encrypt inventory/group_vars/all/vault.yml
|
||||
```
|
||||
|
||||
Keep the Vault password outside the repository. The password hash is encrypted in Vault and its Ansible task output is suppressed.
|
||||
|
||||
## Configure ingress
|
||||
|
||||
Add only required service ports to `ufw_allowed_rules` in a host's inventory file:
|
||||
|
||||
```yaml
|
||||
ufw_allowed_rules:
|
||||
- port: "80"
|
||||
proto: tcp
|
||||
- port: "443"
|
||||
proto: tcp
|
||||
- port: "51820"
|
||||
proto: udp
|
||||
```
|
||||
|
||||
SSH ports are handled by `ssh_port`, `ssh_legacy_port`, and `ssh_keep_legacy_port`. A rule may set `from_ip` to restrict its source. To remove an existing rule, move its port, protocol, and source into `ufw_remove_rules`, run the playbook once, then remove that cleanup entry. UFW defaults to denying inbound and routed traffic and allowing outbound traffic.
|
||||
|
||||
Docker-published ports can bypass UFW's normal host INPUT filtering. Publish only intended host ports and keep application and database ports on Docker networks or bind them locally.
|
||||
|
||||
## Roll out one host
|
||||
|
||||
Replace `server1` with the host alias from `inventory/hosts.yml` in these commands. Keep a working SSH session open during the port change.
|
||||
|
||||
1. Inspect the host and its current listeners:
|
||||
|
||||
```sh
|
||||
ansible-playbook playbooks/discover.yml -l server1
|
||||
```
|
||||
|
||||
2. Bootstrap the admin account through the existing SSH account:
|
||||
|
||||
```sh
|
||||
ansible-playbook playbooks/bootstrap-admin.yml -l server1 --ask-become-pass --ask-vault-pass
|
||||
```
|
||||
|
||||
Omit `--ask-become-pass` if the existing account already has passwordless sudo. Verify a fresh SSH login as `admin_user` on the old port and check that `sudo` asks for that account's password.
|
||||
|
||||
3. Change `ansible_user` in the host inventory to `admin_user`. Keep `ansible_port` on the old port, and leave `ssh_keep_legacy_port: true`. Apply hardening:
|
||||
|
||||
```sh
|
||||
ansible-playbook playbooks/harden.yml -l server1 -K --ask-vault-pass
|
||||
```
|
||||
|
||||
UFW allows both SSH ports before it is enabled. SSH then listens on both ports. On hosts using `ssh.socket`, the playbook updates the systemd socket listeners as well.
|
||||
|
||||
4. Verify a fresh SSH login as the admin on `ssh_port`, plus UFW and Fail2ban status. Change `ansible_port` to `ssh_port`, set `ssh_keep_legacy_port: false`, and rerun the hardening command. The old SSH listener and its UFW rule are then removed.
|
||||
|
||||
5. Add or retire future service ports through that host's `ufw_allowed_rules` and `ufw_remove_rules`, then rerun the hardening playbook with `-l <alias>`.
|
||||
|
||||
## Ongoing work
|
||||
|
||||
- Review native listeners before closing a service port.
|
||||
- Review container port publishing separately from UFW rules.
|
||||
- Keep secrets in Vault and runtime secret files, and verify a backup restore.
|
||||
- Review container updates after DIUN alerts; they are not applied automatically.
|
||||
Reference in New Issue
Block a user