Skip to content
Notifications
Clear all

Just built a script to compare running config to a gold standard.

1 Posts
1 Users
0 Reactions
1 Views
(@derekf)
Trusted Member
Joined: 4 days ago
Posts: 38
Topic starter   [#19858]

I've been working on a project to standardize our branch office SRX configurations (we're running a mix of SRX300 and SRX345 devices) and found the built-in `show | compare` operational command useful, but insufficient for our needs. The primary limitation is that it's a live, point-in-time comparison against the candidate configuration. I needed a way to programmatically compare the *running* configuration of a device against a version-controlled gold standard template, especially to audit configurations post-change or after an unplanned incident.

To address this, I built a Python script that leverages Juniper's PyEZ library to fetch the running config, normalize it for comparison (stripping certain dynamic elements), and perform a structured diff against a local template. The goal is to produce a human-readable and machine-parsable report highlighting deviations, which is crucial for our FinOps and SRE compliance checks.

Key features of the script include:
* **Template Normalization:** It removes lines that are inherently unique or transient (e.g., `root-authentication` hashed passwords, `last-login` timestamps, security session identifiers).
* **Context-Aware Diffing:** It goes beyond line-by-line comparison and can identify moved or modified stanzas within hierarchies like security policies or firewall address books.
* **Severity Tagging:** Deviations are categorized. For example, an extra, permissive security policy is flagged as `HIGH`, while a deviation in a syslog server's IP address might be `MEDIUM`.
* **Integration Ready:** Outputs JSON for ingestion into our observability platform (Grafana) and a plaintext summary for quick engineer reviews.

Here is the core comparison function, which handles the normalization and uses the `difflib` library for the actual diff generation:

```python
def normalize_config(config_lines):
"""Strip out non-deterministic lines from config output."""
normalized = []
skip_patterns = [
r'^s*encrypted-password',
r'^s*last-login',
r'^s*## Last commit:',
r'^s*secret',
r'^s*session-'
]
for line in config_lines:
if not any(re.search(p, line) for p in skip_patterns):
normalized.append(line)
return normalized

def generate_config_diff(device, gold_template_path):
with open(gold_template_path, 'r') as f:
gold_lines = normalize_config(f.readlines())

running_config = device.rpc.get_config(options={'format': 'text'})
running_text = etree.tostring(running_config, encoding='unicode')
running_lines = normalize_config(running_text.splitlines())

diff = difflib.unified_diff(
gold_lines, running_lines,
fromfile='gold_template',
tofile='running_config',
lineterm=''
)
return list(diff)
```

The main challenges encountered were:
* Handling the hierarchical nature of Junos configs effectively. A simple line diff can be noisy if a block is merely reordered.
* Deciding which elements to normalize without compromising security. We exclude password hashes but must ensure any change to a `security policy` rule is caught, even if it's just a reorder.
* Performance on large configurations. For our scale (~50 devices), it's acceptable, but for larger deployments, we'd need to consider more efficient data structures.

This tool has been integrated into a weekly audit job via our Kubernetes-based platform engineering stack, and the results are surfaced in a dashboard alongside cost metrics. This correlation is insightful; for instance, we found a misconfigured `set system services` stanza that was causing unnecessary log volume to an external SIEM, which had both a security observability and a direct cost impact due to increased egress data processing fees.

I'm interested if others in the community have tackled similar problems. Specifically, how do you handle the comparison of hierarchical configuration where context (like position in a policy list) can be semantically important? Are there alternative libraries or approaches (perhaps using `jq` or `jd` on JSON-formatted configs) that you've found more robust than a text-based `difflib` approach?


No free lunch in cloud.


   
Quote