浏览代码

fix: reorder UFW tasks to allow ports before enabling deny policy

Enabling UFW with default-deny before adding SSH allow rules created
a window where the active SSH connection could be dropped, locking
out the user after the first initialization stage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
kotoyuuko 3 周之前
父节点
当前提交
24c3285545

+ 2 - 0
openspec/changes/archive/2026-04-22-fix-ufw-ssh-ordering/.openspec.yaml

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

+ 30 - 0
openspec/changes/archive/2026-04-22-fix-ufw-ssh-ordering/design.md

@@ -0,0 +1,30 @@
+## Context
+
+The base role's UFW tasks in `roles/base/tasks/main.yml` currently run in this order:
+1. Enable UFW with `policy: deny` (default deny all incoming)
+2. Allow SSH port through UFW
+3. Allow other configured ports through UFW
+
+Between steps 1 and 2, UFW is active with zero allow rules. While UFW typically doesn't drop established connections, this ordering violates the best practice of "allow before deny" and can cause issues depending on how UFW handles the policy change mid-session.
+
+## Goals / Non-Goals
+
+**Goals:**
+- Ensure all allow rules are in place before UFW's deny policy is activated
+- Prevent SSH disconnection during Ansible playbook execution
+
+**Non-Goals:**
+- No changes to which ports are allowed or denied
+- No changes to SSH hardening, fail2ban, or other base role functionality
+
+## Decisions
+
+Swap the task order: allow SSH and other ports first, then enable UFW with the deny policy. This ensures the firewall is enabled with all necessary rules already in place.
+
+Alternatives considered:
+- Add a `route` rule to allow the current Ansible connection before enabling UFW: overcomplicated, UFW's rule ordering is sufficient.
+- Use `ufw reset` before enabling: unnecessary and would remove any existing custom rules.
+
+## Risks / Trade-offs
+
+- [Brief window between allow rules and UFW enable where no firewall is active] → Mitigation: this is acceptable since the server is in the middle of an Ansible provisioning run and was previously unprotected anyway.

+ 19 - 0
openspec/changes/archive/2026-04-22-fix-ufw-ssh-ordering/proposal.md

@@ -0,0 +1,19 @@
+## Why
+
+The base role enables UFW with a default-deny policy before adding the SSH port allow rule. This creates a window where the firewall is active with no allow rules, which can drop the active Ansible SSH connection and lock out the user after the first initialization stage.
+
+## What Changes
+
+- Reorder UFW tasks in `roles/base/tasks/main.yml`: allow SSH and other ports **before** enabling UFW with the deny policy
+
+## Capabilities
+
+### New Capabilities
+<!-- none -->
+
+### Modified Capabilities
+<!-- none - this is an implementation ordering fix, no spec-level behavior changes -->
+
+## Impact
+
+- `roles/base/tasks/main.yml`: task reordering only, no functional changes

+ 4 - 0
openspec/changes/archive/2026-04-22-fix-ufw-ssh-ordering/specs/NOTE.md

@@ -0,0 +1,4 @@
+## Note
+
+No capability changes. This is an internal task ordering fix within the base role.
+The firewall behavior (deny incoming, allow SSH, allow configured ports) is unchanged.

+ 3 - 0
openspec/changes/archive/2026-04-22-fix-ufw-ssh-ordering/tasks.md

@@ -0,0 +1,3 @@
+## 1. Reorder UFW tasks in base role
+
+- [x] 1.1 Move SSH and port allow rules before UFW enable in `roles/base/tasks/main.yml`

+ 6 - 6
roles/base/tasks/main.yml

@@ -19,12 +19,6 @@
     validate: "sshd -t -f %s"
   notify: restart sshd
 
-- name: Enable UFW
-  community.general.ufw:
-    state: enabled
-    policy: deny
-    direction: incoming
-
 - name: Allow SSH through UFW
   community.general.ufw:
     rule: allow
@@ -38,6 +32,12 @@
     proto: tcp
   loop: "{{ allowed_ports | default([]) }}"
 
+- name: Enable UFW
+  community.general.ufw:
+    state: enabled
+    policy: deny
+    direction: incoming
+
 - name: Configure fail2ban SSH jail
   ansible.builtin.copy:
     dest: /etc/fail2ban/jail.local