The Script That Blocked the Security Team
A junior analyst on your team wrote a Python script that reads /var/log/auth.log every five minutes, extracts source IPs of failed SSH logins, and calls a firewall API to block them. It runs as root via cron on the SIEM server. Last week it blocked the corporate office NAT address and the vulnerability scanner's IP, taking the security team offline for 90 minutes and blinding the next scheduled scan. The script also dies with a traceback whenever a log line does not match its regular expression, and repeated runs keep adding duplicate block entries. The analyst wants to extend it to block more sources and roll it out to every server fleet-wide.
1. Identify the design flaws and state the order in which you would fix them. Justify the order.
Read the worked answer
Flaws, in order of consequence:
- No trusted-network allowlist. The outage was self-inflicted, not caused by an attacker. A blocking tool without an exclusion list for corporate egress, scanners, monitoring, and partners will eventually block something it must not.
- No idempotency or state. Duplicate entries mean the firewall rule table grows unbounded and the operation is not safely repeatable - the single most important property of an automated remediator.
- No error handling. One malformed log line kills the whole run; partial processing silently stops detection.
- Runs as root with broad firewall API rights. A parsing bug becomes an infrastructure incident. Least privilege limits the blast radius.
- No dry-run, no logging, no rollback. You cannot audit what it did or undo it quickly.
- No rate limit or cap. A burst of spoofed failures could block an entire range.
Why this order: fix what caused the outage first (allowlist), then the properties that make it safe to run repeatedly (idempotency, error handling), then the properties that make it safe to run at all (privilege, observability, rate limiting). Do not expand scope - 'more blocklists, all fleets' - until the tool has run cleanly in its current scope for a full cycle.
The reframe: this is not a parsing script, it is an automated change agent with write access to production infrastructure. It should be reviewed and tested like one.
2. Describe concretely how you would restructure the parsing and blocking logic, including how the allowlist is applied.
Read the worked answer
Allowlist as a first-class data source, not an afterthought:
- Keep a version-controlled allowlist file containing corporate egress ranges, scanner and monitoring IPs, partner ranges, and RFC1918/loopback/link-local space.
- Load it once at start-up; check membership before any API call, and log skips so you can see the tool declining to act.
- Fail closed on a broken allowlist: if the file cannot be parsed, exit non-zero and alert, rather than blocking without protection.
Parsing loop:
for line in fh:
try:
m = PATTERN.search(line)
if m:
counts[m.group('ip')] = counts.get(m.group('ip'), 0) + 1
except Exception:
parse_failures += 1
continue
Per-line exception handling with a start/end summary of parse failures - a sudden jump in failures is itself a detection signal.
Blocking logic:
- Threshold before acting (for example, 10 failures in the window), so a single typo does not trigger a block.
- Query current firewall state or keep a state file; skip IPs already blocked.
- Cap the number of new blocks per run; log every action with timestamp, IP, count, and rule ID.
- Emit a metrics line each run: lines parsed, IPs seen, IPs skipped by allowlist, blocks added, errors.
Privilege: run as a dedicated service account with permission to manage only the specific blocklist object - not root, not a full firewall-admin API key.
3. How would you test the revised script before it runs in production, and what is the first thing you do in response to last week's outage?
Read the worked answer
Outage response first - in this order:
- Revert the blocks for the office NAT and scanner, confirm connectivity and that scanning resumes.
- Restore service, then investigate. Do not debug while the team is offline.
- Write the incident up as a change-caused outage: what happened, detection time, recovery time, and the control that failed (no allowlist, no dry-run). Add the allowlist entries permanently before the tool runs again.
Testing the revision:
- Dry-run mode as a first-class flag: compute and print intended blocks, change nothing. Run it against the real log for several days and compare its output to what an analyst would have done by hand.
- Unit tests with a saved sample log covering: normal failures, malformed lines, an allowlisted IP with 500 failures, and a duplicate IP across runs.
- Static checks: secret scanning if the API key is embedded, and linting for bare excepts.
- Staged rollout: one non-critical host for one full cycle, with a named person watching the metrics line each run.
- Kill switch: documented one-command stop and revert that anyone on the team can execute at 03:00.
Judgement point: the analyst's instinct to scale up is the wrong instinct. A tool that has already caused one outage earns trust one host at a time, with a dry run and a rollback proven before the next increment.