extract-fw-host-vars.py: abort if a conf file cannot be read; add --from-dir. Migrate cl-test.

Previously an unreadable file (e.g. /etc/ipt-firewall is 0750 after the
role ran) silently produced almost empty host_vars. The script now exits
with an error and writes nothing. --from-dir reads the conf files from a
local directory, e.g. fetched with ansible using the vaulted become
password.

Add host_vars for cl-test.oopen.de (fw_manage_config: true).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-03 01:00:55 +01:00
co-authored by Claude Opus 5.5
parent ec62f54ab4
commit 3e6486c5b5
3 changed files with 83 additions and 13 deletions
+30 -12
View File
@@ -6,7 +6,7 @@ Reads /etc/ipt-firewall/{interfaces,main}_ipv{4,6}.conf via SSH,
maps all variables to Ansible fw_* names, and writes a host_vars file. maps all variables to Ansible fw_* names, and writes a host_vars file.
Usage: Usage:
./extract-fw-host-vars.py <hostname> [--user USER] [--port PORT] [--dry-run] ./extract-fw-host-vars.py <hostname> [--user USER] [--port PORT] [--sudo] [--from-dir DIR] [--output FILE] [--dry-run]
Example: Example:
./extract-fw-host-vars.py cl-01.oopen.de ./extract-fw-host-vars.py cl-01.oopen.de
@@ -749,6 +749,9 @@ def main():
parser.add_argument("--output", "-o", default=None, help="Output file (default: stdout)") parser.add_argument("--output", "-o", default=None, help="Output file (default: stdout)")
parser.add_argument("--sudo", "-s", action="store_true", parser.add_argument("--sudo", "-s", action="store_true",
help="Read files via sudo (prompts for sudo password once)") help="Read files via sudo (prompts for sudo password once)")
parser.add_argument("--from-dir", "-d", default=None,
help="Read the conf files from this local directory instead of via SSH "
"(e.g. fetched with: ansible HOST -b -m fetch ...)")
parser.add_argument("--dry-run", action="store_true", help="Print SSH commands without executing") parser.add_argument("--dry-run", action="store_true", help="Print SSH commands without executing")
args = parser.parse_args() args = parser.parse_args()
@@ -767,18 +770,33 @@ def main():
print(f"ssh {args.user}@{hostname} {cmd} {path}") print(f"ssh {args.user}@{hostname} {cmd} {path}")
return return
sudo_password = None
if args.sudo:
import getpass
sudo_password = getpass.getpass(f"sudo password for {args.user}@{hostname}: ")
print(f"Connecting to {hostname} as {args.user} ...", file=sys.stderr)
contents = {} contents = {}
for key, path in files.items(): if args.from_dir:
print(f" Reading {path} ...", file=sys.stderr) for key, path in files.items():
content = ssh_cat(hostname, args.user, args.port, path, sudo_password=sudo_password) local = Path(args.from_dir) / Path(path).name
contents[key] = content or "" print(f" Reading {local} ...", file=sys.stderr)
try:
contents[key] = local.read_text()
except OSError as e:
print(f" WARNING: could not read {local}: {e}", file=sys.stderr)
contents[key] = None
else:
sudo_password = None
if args.sudo:
import getpass
sudo_password = getpass.getpass(f"sudo password for {args.user}@{hostname}: ")
print(f"Connecting to {hostname} as {args.user} ...", file=sys.stderr)
for key, path in files.items():
print(f" Reading {path} ...", file=sys.stderr)
contents[key] = ssh_cat(hostname, args.user, args.port, path, sudo_password=sudo_password)
# An unreadable file would silently produce (nearly) empty host_vars
if any(c is None for c in contents.values()):
print("ERROR: not all config files could be read — nothing written "
"(try --sudo or --from-dir)", file=sys.stderr)
sys.exit(1)
all_warnings = [] all_warnings = []
parsed = {} parsed = {}
@@ -0,0 +1,38 @@
---
# ipt-firewall configuration for cl-test.oopen.de
# Generated by extract-fw-host-vars.py - review before committing!
# Place in: host_vars/<hostname>/<short-hostname>-ipt-server.yml
fw_manage_config: true
# --- Network
fw_ext_interfaces:
- "eth0"
fw_ext_ips_v4:
- "159.69.74.143"
fw_ext_ips_v6:
- "2a01:4f8:231:19a7::143"
# --- Munin
munin_remote_ipv4: 37.27.121.227
munin_remote_ipv6: "2a01:4f9:3070:2bda::227"
# --- Access control IPv4
fw_allow_ext_service_v4: "159.69.74.156:3478:tcp 159.69.74.156:3478:udp 159.69.74.156:3479:tcp 159.69.74.156:3479:udp 159.69.74.156:5349:tcp 159.69.74.156:5350:udp"
fw_allow_local_service_from_networks_v4: "46.90.86.195:80:tcp 46.90.86.195:443:tcp"
# --- Access control IPv6
fw_allow_ext_service_v6: "2a01:4f8:231:19a7::156,3478,tcp 2a01:4f8:231:19a7::156,3478,udp 2a01:4f8:231:19a7::156,3479,tcp 2a01:4f8:231:19a7::156,3479,udp 2a01:4f8:231:19a7::156,5349,tcp 2a01:4f8:231:19a7::156,5349,udp 2a01:4f8:231:19a7::156,5350,tcp 2a01:4f8:231:19a7::156,5350,udp"
fw_allow_local_service_from_networks_v6: "2003:ec:df11:4400::/56,80,tcp 2003:ec:df11:4400::/56,443,tcp"
# --- HTTP
fw_http_server_ips: $ext_1_ip
# --- Mail
fw_mail_client_ips: $ext_1_ip
# --- TURN / STUN
fw_nc_turn_server_ips: $ext_1_ip
# --- Block
fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
+15 -1
View File
@@ -60,7 +60,21 @@ HOSTNAME=<hostname>
-o host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml -o host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
``` ```
Das Skript fragt einmalig nach dem `sudo`-Passwort. Das Skript fragt einmalig nach dem `sudo`-Passwort. Kann eine der Dateien nicht
gelesen werden, bricht es ab, ohne etwas zu schreiben.
Alternativ ohne Passwortabfrage (sudo über `ansible_become_password` aus dem Vault):
die Dateien mit Ansible holen und lokal einlesen:
```bash
for f in interfaces_ipv4 interfaces_ipv6 main_ipv4 main_ipv6 ; do
ansible ${HOSTNAME} -b -m fetch \
-a "src=/etc/ipt-firewall/${f}.conf dest=/tmp/fw-${HOSTNAME}/ flat=yes"
done
./extract-fw-host-vars.py ${HOSTNAME} --from-dir /tmp/fw-${HOSTNAME} \
-o host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
rm -rf /tmp/fw-${HOSTNAME}
```
**Ergebnis prüfen:** **Ergebnis prüfen:**