浏览代码

feat: support per-host custom SSH port via ansible_port

ssh_port now derives from ansible_port (default 22). Users set
ansible_port in inventory; sshd, UFW, and fail2ban auto-sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kotoyuuko 3 周之前
父节点
当前提交
13b6345f20

+ 2 - 1
README.md

@@ -119,4 +119,5 @@ cp -r credentials/ /path/to/backup/
 | `trojan_fallback_port` | 8080 | Fallback port for non-Trojan traffic |
 | `trojan_version` | 0.10.6 | trojan-go release version |
 | `certbot_email` | — | Email for Let's Encrypt |
-| `ssh_port` | 22 | SSH listen port |
+| `ansible_port` | 22 | SSH port per host (set in inventory) |
+| `ssh_port` | `ansible_port` | Derived from `ansible_port`; used in sshd_config, UFW, fail2ban |

+ 1 - 1
group_vars/all.yml.example

@@ -8,4 +8,4 @@ base_packages:
   - fail2ban
   - unattended-upgrades
 
-ssh_port: 22
+ssh_port: "{{ ansible_port | default(22) }}"

+ 2 - 0
inventory/hosts.yml.example

@@ -4,10 +4,12 @@ all:
       hosts:
         relay-server:
           ansible_host: "YOUR_RELAY_IP"
+          ansible_port: 22  # SSH port (change if non-standard)
           # SSH user: set to "root" or a non-root user with passwordless sudo (e.g., "ubuntu")
           ansible_user: "YOUR_SSH_USER"
     landing:
       hosts:
         landing-server:
           ansible_host: "YOUR_LANDING_IP"
+          ansible_port: 22
           ansible_user: "YOUR_SSH_USER"

+ 2 - 0
openspec/changes/archive/2026-04-22-custom-ssh-port/.openspec.yaml

@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-04-22

+ 24 - 0
openspec/changes/archive/2026-04-22-custom-ssh-port/design.md

@@ -0,0 +1,24 @@
+## Context
+
+`ssh_port` is used in three places: `sshd_config.j2` (Port directive), `base/tasks/main.yml` (UFW SSH allow, fail2ban jail). It's currently global in `all.yml`. Ansible uses `ansible_port` to know which port to connect on.
+
+## Goals / Non-Goals
+
+**Goals:**
+- Per-host SSH port via inventory (`ssh_port` + `ansible_port`)
+- Global default remains 22
+
+**Non-Goals:**
+- Changing SSH port automatically on the server (user sets this beforehand)
+
+## Decisions
+
+### 1. Both `ansible_port` and `ssh_port` in inventory
+
+`ansible_port` is Ansible's built-in variable for connection port. `ssh_port` is our variable used in templates. Both should match. Set them together in the inventory host vars.
+
+Keep the default `ssh_port: 22` in `group_vars/all.yml` so it works if not overridden per-host.
+
+## Risks / Trade-offs
+
+- **[Port mismatch]** → If `ansible_port` and `ssh_port` don't match, Ansible connects on wrong port. Mitigation: document clearly, could DRY up by setting `ssh_port: "{{ ansible_port | default(22) }}"` in `all.yml`.

+ 24 - 0
openspec/changes/archive/2026-04-22-custom-ssh-port/proposal.md

@@ -0,0 +1,24 @@
+## Why
+
+Servers may run SSH on non-standard ports for security. Each server could have a different SSH port. Currently `ssh_port` is a single global variable in `group_vars/all.yml` — it needs to be configurable per host.
+
+## What Changes
+
+- Add `ansible_port` and `ssh_port` as per-host variables in the inventory template
+- Keep the global default of 22 in `group_vars/all.yml`
+- Per-host `ssh_port` overrides the global default for sshd_config, UFW, and fail2ban
+- `ansible_port` tells Ansible which port to connect on
+
+## Capabilities
+
+### New Capabilities
+
+(none)
+
+### Modified Capabilities
+- `server-base`: SSH port becomes per-host configurable via inventory
+
+## Impact
+
+- `inventory/hosts.yml.example` updated with `ansible_port` and `ssh_port` variables
+- README updated to document per-host SSH port configuration

+ 16 - 0
openspec/changes/archive/2026-04-22-custom-ssh-port/specs/server-base/spec.md

@@ -0,0 +1,16 @@
+## MODIFIED Requirements
+
+### 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

+ 5 - 0
openspec/changes/archive/2026-04-22-custom-ssh-port/tasks.md

@@ -0,0 +1,5 @@
+## 1. Per-Host SSH Port
+
+- [x] 1.1 Update `group_vars/all.yml.example` — derive `ssh_port` from `ansible_port` to keep them in sync: `ssh_port: "{{ ansible_port | default(22) }}"`
+- [x] 1.2 Update `inventory/hosts.yml.example` — add `ansible_port` per host with a comment
+- [x] 1.3 Update README — document per-host SSH port configuration

+ 8 - 1
openspec/specs/server-base/spec.md

@@ -27,13 +27,20 @@ The base role SHALL install essential packages: `curl`, `wget`, `vim`, `htop`, `
 - **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 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).