## ADDED Requirements ### Requirement: Ansible inventory defines relay and landing server groups The inventory SHALL define two host groups: `relay` and `landing`, each containing the respective server's connection details (IP, SSH user, SSH key). The `ansible_user` SHALL be a configurable placeholder supporting both root and non-root users. The repository SHALL ship `inventory/hosts.yml.example` as a template; the actual `inventory/hosts.yml` SHALL be gitignored and created by the user. #### Scenario: Inventory is valid - **WHEN** the user copies `hosts.yml.example` to `hosts.yml` and fills in their values - **THEN** two groups `relay` and `landing` are available, each with at least one host #### Scenario: Non-root user with sudo - **WHEN** `ansible_user` is set to a non-root user (e.g., `ubuntu`) - **THEN** Ansible connects as that user and uses `become` for privilege escalation #### Scenario: Root user - **WHEN** `ansible_user` is set to `root` - **THEN** Ansible connects as root directly and `become` is a no-op #### Scenario: Missing inventory - **WHEN** the user has not copied `hosts.yml.example` to `hosts.yml` - **THEN** Ansible fails with an error indicating the inventory file is missing ### Requirement: Base packages are installed on all servers The base role SHALL install essential packages: `curl`, `wget`, `vim`, `htop`, `unzip`, `ufw`, `fail2ban`, `unattended-upgrades`. #### Scenario: Fresh server provisioning - **WHEN** the base role runs on a fresh Ubuntu/Debian server - **THEN** all listed packages are installed and available ### Requirement: SSH is hardened The base role SHALL configure SSH to disable password authentication, disable root login, and only allow key-based authentication. The SSH port SHALL be configurable per host via `ssh_port`, defaulting to 22. #### Scenario: SSH hardening applied - **WHEN** the base role completes - **THEN** `/etc/ssh/sshd_config` has `PasswordAuthentication no`, `PermitRootLogin no`, and `PubkeyAuthentication yes` - **THEN** the sshd Port directive uses the host's `ssh_port` value - **THEN** the sshd service is restarted #### Scenario: Custom SSH port per host - **WHEN** a host defines `ssh_port: 2222` in inventory - **THEN** sshd listens on port 2222 - **THEN** UFW allows port 2222 instead of 22 - **THEN** fail2ban monitors port 2222 ### Requirement: UFW firewall is configured with default deny The base role SHALL enable UFW with a default deny incoming policy and allow SSH (port 22). #### Scenario: Firewall base rules - **WHEN** the base role completes - **THEN** UFW is active with default deny incoming - **THEN** SSH port 22 is allowed #### Scenario: Proxy ports are allowed per server role - **WHEN** the base role runs on a relay server - **THEN** the relay proxy port is allowed through UFW - **WHEN** the base role runs on a landing server - **THEN** both the chained and direct proxy ports are allowed through UFW ### Requirement: fail2ban protects SSH The base role SHALL configure fail2ban to monitor SSH login attempts and ban IPs after repeated failures. #### Scenario: fail2ban is active - **WHEN** the base role completes - **THEN** fail2ban is running with an SSH jail enabled ### Requirement: Automatic security updates are enabled The base role SHALL enable unattended-upgrades for security patches. #### Scenario: Unattended upgrades configured - **WHEN** the base role completes - **THEN** unattended-upgrades is configured to auto-install security updates