Secure and configure the SG2428LP managed switch (10.0.0.10) as repeatable Ansible, driven from the control node over SSH by an expect engine. - tplink_switch_harden role (verified JetStream CLI syntax): HTTP off/ HTTPS-only, Telnet off, SSH v2, SNMP off, RSTP + loopback-detection, then copy running-config startup-config (persist). - playbooks/harden_switch.yml + [switches] inventory group; dry by default, -e switch_apply=true to apply, -e switch_apply_network=true for addressing. - Static mgmt IP 10.0.0.10/24 (off DHCP, below the .20-.199 pool), gw 10.0.0.1, hostname sg2428lp. - op -> ansible-vault bridge (etc/set-switch-1password.sh); admin password in group_vars/switches/vault.yml. - DNS: A/PTR sg2428lp.prole.org -> 10.0.0.10 (records added to dns.yml/ptr_records). - Fix: tag the samba_reverse_dns zone-list set_fact so tag-limited runs define samba_reverse_zones. Engine notes (hard-won): forces password-only SSH auth (the switch drops publickey-first logins); always saves config (unsaved changes revert on reboot). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| defaults | ||
| tasks | ||
| templates | ||
| README.md | ||
tplink_switch_harden
Repeatable security hardening for TP-Link JetStream managed switches
(developed for the SG2428LP at 10.0.0.153), driven from the control node
over SSH by a rendered expect engine.
Secret pipeline
1Password ──op read──▶ ENV ──ansible-vault──▶ group_vars/switches/vault.yml ──▶ playbook
op://knoey/TP-Link SG2428LP/password (vault_sg2428lp_admin_password)
Refresh the vault from 1Password whenever the password changes:
etc/set-switch-1password.sh
The admin password is passed to the engine only via the SWITCH_PW environment
variable (no_log), so it never lands in inventory, the rendered script, or
process arguments.
Usage
# Dry run — log in, capture `show system-info`, change nothing:
ansible-playbook infrastructure/playbooks/harden_switch.yml
# Apply hardening and save to startup-config:
ansible-playbook infrastructure/playbooks/harden_switch.yml -e switch_apply=true
switch_apply defaults to false; nothing is changed until you pass
-e switch_apply=true.
What it hardens
Config-mode commands live in defaults/main.yml
(switch_harden_commands): disable plaintext HTTP, ensure HTTPS, disable
Telnet, SSH v2 only, ensure SNMP off, enable RSTP, enable loopback-detection,
then copy running-config startup-config.
The command list was verified against SG2428LP firmware via on-device ?
help. JetStream tokens vary by model/firmware — reconfirm with ? if you point
this at a different model.
Management IP access-control and CLI idle-timeout are intentionally opt-in
(commented in defaults/main.yml) because a wrong ACL can lock you out. Apply
those last, from a host inside the permitted range.
Static-IP cutover (DHCP → static)
switch_network_commands (toggled by switch_apply_network) sets the static
mgmt IP. In steady state — when the host's ansible_host already equals the
static IP — re-asserting it is a no-op. The one-time DHCP→static cutover is
different: the ip address line drops the session, so it cannot save afterward
over the same connection. Do the cutover deliberately (connect on the DHCP
address, set the static IP, reconnect on the new IP, then save), and afterward
set the host's ansible_host to the static IP. Same-subnet control hosts reach
the new IP directly, so gateway correctness isn't required to regain access.
Gotchas (these cost real debugging time)
- SSH publickey auth breaks login. The JetStream SSH server drops the
connection when the client offers
publickeyfirst (the default). The engine forces-o PubkeyAuthentication=no -o PreferredAuthentications=password. Symptom without it: "connection closed after KEX, before password prompt", intermittently (depends on which keys your agent offers). - Unsaved config reverts on reboot. Web-UI changes (incl. the first-login
password) live in running-config until you explicitly save. A reboot with no
save silently reverts to factory. The engine always ends with
copy running-config startup-config(look for "Saving user config OK!"). - One management session at a time. A logged-in web-UI session occupies the single mgmt slot and blocks SSH login. Log out of the web UI before running.
no ip http serverdoesn't close port 80. It disables HTTP management; port 80 stays open serving only a JS redirect to HTTPS. That's expected/secure.