feat(windows): put a Windows Server under castrum #42

Merged
claudiomaradonna merged 3 commits from feat/windows-trusted-sources into main 2026-10-17 18:57:25 +02:00
Collaborator

Why

A Windows Server whose application relies on its built-in Administrator
should answer only a few fixed addresses: RDP, its web front end and the
application's client ports. The Windows roles assumed a Windows 11 PC.
The firewall role admits only SSH and the scanner, leaves installer
rules open to every source, and the hardening baseline disables the
built-in Administrator and turns Remote Desktop off.

What changed

  • windows-firewall-basic can admit a host's
    windows_firewall_trusted_sources on every protocol and port, through
    one castrum: all from trusted rule written before default-deny. The
    rule is removed when the list is empty.
  • It can switch off inbound rules outside any built-in group, such as an
    installer's own rule, by exact display name
    (windows_firewall_disabled_rules). A name that matches nothing, the
    bootstrap's SSH rule, or a castrum: rule fails the run before
    anything changes, and --check --diff names the rules it will switch
    off.
  • Both address lists are checked read-only before anything changes. The
    timed SSH restore is now armed before any rule is written or removed,
    so every rule change is covered by it.
  • windows-hardening-basic gains windows_hardening_sshd_enabled, which
    follows windows_hardening_enabled by default. With the baseline off
    and this flag on, the role only installs its key-only sshd_config:
    no account, group, registry or service task runs.
  • docs/ADVANCED_USAGE.md gains "A Windows Server", and both role
    READMEs, the role catalogue and the README scope now cover it.

Existing Windows 11 PCs get no new rule. They see the same results in a
new order, plus one read-only ok task. Out of scope: the rest of the
baseline on Windows Server, port-by-port Windows rules, and a provider's
edge firewall.

How to verify

  1. make check passes, including check_windows_catalogue.sh, which
    now requires both new firewall lists to default to [].
  2. For a Windows host in windows with windows_hardening_enabled: false, windows_hardening_sshd_enabled: true and a trusted source,
    run ansible-playbook -i inventory/<tenant> fortify.yml -e target=<host> --check --diff. It reports castrum: all from trusted
    and the listed rules being switched off. From windows-hardening-basic
    it reports only the sshd render.
  3. Put a malformed address (192.0.2.1/33) in the trusted list and run
    step 2 again. It fails at "Check that every address list parses
    before touching anything", and nothing changes.
  4. List a rule name the host does not have. It fails at "Resolve every
    named rule before touching anything".

Run so far:

  • make check passed on every commit.
  • A check-mode probe on a stand-in host showed the hardening flag split:
    with the baseline on, the task list is the same as on main.
  • make test and make verify were not run, because the sandbox has no
    podman.
  • Nothing has run on a Windows host yet.

Considerations

  • A trusted source reaches every listening service, SMB and RPC
    included. List only addresses whose whole network is trusted.
  • App-package firewall groups (@{Microsoft...}) stay out of a host's
    list on purpose. Their strings carry package versions that updates
    change, which would fail the run's no-match check.
  • Arming the timer earlier makes the window between arming and the proof
    a few seconds longer. A run interrupted in that window leaves SSH open
    from any source a few minutes later, the trade-off the firewall README
    already documents.
## Why A Windows Server whose application relies on its built-in Administrator should answer only a few fixed addresses: RDP, its web front end and the application's client ports. The Windows roles assumed a Windows 11 PC. The firewall role admits only SSH and the scanner, leaves installer rules open to every source, and the hardening baseline disables the built-in Administrator and turns Remote Desktop off. ## What changed - `windows-firewall-basic` can admit a host's `windows_firewall_trusted_sources` on every protocol and port, through one `castrum: all from trusted` rule written before default-deny. The rule is removed when the list is empty. - It can switch off inbound rules outside any built-in group, such as an installer's own rule, by exact display name (`windows_firewall_disabled_rules`). A name that matches nothing, the bootstrap's SSH rule, or a `castrum:` rule fails the run before anything changes, and `--check --diff` names the rules it will switch off. - Both address lists are checked read-only before anything changes. The timed SSH restore is now armed before any rule is written or removed, so every rule change is covered by it. - `windows-hardening-basic` gains `windows_hardening_sshd_enabled`, which follows `windows_hardening_enabled` by default. With the baseline off and this flag on, the role only installs its key-only `sshd_config`: no account, group, registry or service task runs. - `docs/ADVANCED_USAGE.md` gains "A Windows Server", and both role READMEs, the role catalogue and the README scope now cover it. Existing Windows 11 PCs get no new rule. They see the same results in a new order, plus one read-only `ok` task. Out of scope: the rest of the baseline on Windows Server, port-by-port Windows rules, and a provider's edge firewall. ## How to verify 1. `make check` passes, including `check_windows_catalogue.sh`, which now requires both new firewall lists to default to `[]`. 2. For a Windows host in `windows` with `windows_hardening_enabled: false`, `windows_hardening_sshd_enabled: true` and a trusted source, run `ansible-playbook -i inventory/<tenant> fortify.yml -e target=<host> --check --diff`. It reports `castrum: all from trusted` and the listed rules being switched off. From `windows-hardening-basic` it reports only the sshd render. 3. Put a malformed address (`192.0.2.1/33`) in the trusted list and run step 2 again. It fails at "Check that every address list parses before touching anything", and nothing changes. 4. List a rule name the host does not have. It fails at "Resolve every named rule before touching anything". Run so far: - `make check` passed on every commit. - A check-mode probe on a stand-in host showed the hardening flag split: with the baseline on, the task list is the same as on `main`. - `make test` and `make verify` were not run, because the sandbox has no podman. - Nothing has run on a Windows host yet. ## Considerations - A trusted source reaches every listening service, SMB and RPC included. List only addresses whose whole network is trusted. - App-package firewall groups (`@{Microsoft...}`) stay out of a host's list on purpose. Their strings carry package versions that updates change, which would fail the run's no-match check. - Arming the timer earlier makes the window between arming and the proof a few seconds longer. A run interrupted in that window leaves SSH open from any source a few minutes later, the trade-off the firewall README already documents.
A host can list trusted sources, admitted on every protocol and port
by one castrum rule written before default-deny and removed when the
list is empty, and inbound rules to switch off by exact display name.
Group names, rule names and address lists are checked read-only first,
so a typo fails the run before anything changes; the timed SSH restore
is now armed before any rule is written or removed. Both lists default
to empty; the catalogue gate keeps them so.
windows_hardening_sshd_enabled, following windows_hardening_enabled by
default, gates the key-only sshd render, swap and reconnect proof, so a
host that keeps the baseline off, such as a Windows Server whose
built-in Administrator an application uses, still gets password login
off without any account or group task running.
The Windows section says how a server that relies on its built-in
Administrator joins the windows group with the PC baseline off, the
key-only sshd on, trusted sources and its own groups and rules, and the
windows group row now names servers too.
claudiomaradonna deleted branch feat/windows-trusted-sources 2026-10-17 18:57:33 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
claudiomaradonna/castrum!42
No description provided.