iron-skillet is the Palo Alto Networks day-1 configuration baseline —
a curated set of Jinja2-templated PAN-OS XML snippets, pre-rendered full
configurations, set-command CLI scripts, and Skillet playlists that
encode hardened defaults for the NGFW and Panorama. There is no runtime,
no daemon, no API: the deliverable is configuration. The repo is a
build-tree of templates and the audience is the engineer who will load
the rendered output onto a Palo Alto Networks device.
This guide is for administrators, security architects, and developers who want to integrate IronSkillet into a device-bring-up pipeline, extend it with site-specific snippets, audit its security posture, or fork it for a custom PAN-OS version.
1. Architecture Overview
IronSkillet is a template package, not a service. It has three artifact tiers — snippets, playlists, and pre-rendered configs — and a small toolchain that converts between them.
1.1 What it does
Given a Palo Alto Networks NGFW or Panorama in factory state, IronSkillet produces XML (or set-command CLI text) that, when imported and committed, configures:
- Management-plane hardening — login banner, password complexity (12-char min, complexity rules, 24-history), 5-attempt admin lockout, 10-minute idle timeout, auto-acquire commit lock, configurable API key lifetime, optional SSH-only management, optional static or DHCP management IP.
- System services — DNS, NTP, timezone (
UTC), hostname, SNMPv3 access setting, dynamic-update schedules (threats every 30 min, antivirus hourly, WildFire real-time, GlobalProtect data-file hourly). - Threat prevention profiles — antivirus, anti-spyware (with DNS sinkhole IPv4 / IPv6), vulnerability protection, file-blocking, URL filtering, WildFire analysis, SSL decryption profile.
- Profile groups — Outbound, Inbound, Internal, Alert-All, Default.
- Custom URL categories —
Block,Allow,Custom-No-Decrypt. - Tags —
Outbound,Inbound,Internal,iron-skillet-version. - Rulebase scaffolding — default security rules, sample security rules, decryption rules, and zone-protection profiles.
- Logging — shared log-settings (email, syslog, log-forwarding, user-id-forwarding, HIP-forwarding, GlobalProtect-forwarding, IP-tag-forwarding, correlation-forwarding) plus log-forwarding profiles for traffic / threat / WildFire / URL / data / tunnel / auth log types.
- Reports & email — simple report set, report group, email scheduler.
- Panorama overlay — same surface, applied to a configurable
template-stack (
STACK) and device-group (DEVICE_GROUP), with shared / not-shared / DG-template / DG-stack variants.
1.2 Architecture diagram
┌──────────────────────────┐
│ templates/panos/ │
│ templates/panorama/ │
│ .meta-cnc.yaml │
│ (snippets + variables) │
└────────────┬─────────────┘
│
│ select subset
│
┌──────────────────▼──────────────────┐
│ playlists/panos/*.skillet.yaml │
│ playlists/panorama/*.skillet.yaml │
│ (curated playlists, "include" + │
│ "when" conditionals) │
└──────────────────┬──────────────────┘
│
│ render
│
┌──────────────────┬─────────▼─────────┬──────────────────┐
│ │ │ │
│ SLI tool │ Panhandler │ pan-cnc / │
│ (CLI, sli) │ (Docker GUI) │ legacy archive │
│ │ │ scripts │
└─────────┬────────┴────────┬──────────┴────────┬─────────┘
│ │ │
└─────────────────┼───────────────────┘
│
│ Jinja2 substitution + md5_hash filter
│
┌───────────────────▼───────────────────┐
│ Rendered output: │
│ • iron_skillet_panos_full.xml │
│ • iron_skillet_panorama_full.xml │
│ • iron_skillet_panos_full.conf │ (set commands)
│ • iron_skillet_panos_full.xlsx │ (variable spreadsheet)
└───────────────────┬───────────────────┘
│
│ load_config / load_set / web UI import
│
┌───────────────────▼───────────────────┐
│ PAN-OS NGFW or Panorama device │
│ (candidate config → commit) │
└───────────────────────────────────────┘
1.3 Component inventory
| Component | Path | Role |
|---|---|---|
| PAN-OS snippet master | templates/panos/snippets/.meta-cnc.yaml |
Embedded-element snippets + variable inventory for the NGFW |
| Panorama snippet master | templates/panorama/snippets/.meta-cnc.yaml |
Same, for Panorama (shared values) |
| Panorama legacy variants | templates/panorama/snippets_dgtemplate_shared/, snippets_dgstack_notshared/, snippets_notshared/ |
Legacy skillet model (separate XML files); deprecated per their READMEs |
| Pre-rendered NGFW XML | templates/panos/full/iron_skillet_panos_full.xml |
Full IronSkillet XML for NGFW |
| Pre-rendered Panorama XML | templates/panorama/full/iron_skillet_panorama_full.xml |
Full IronSkillet XML for Panorama |
| Set-command output | templates/panos/set_commands/, templates/panorama/set_commands/ |
Pre-rendered set commands and .xlsx |
| Baseline | templates/panos/baseline/baseline.xml, templates/panorama/baseline/baseline.xml |
Empty starting config (rollback target) |
| NGFW playlists | playlists/panos/panos_*.skillet.yaml |
Alert-only, device-hardening, full, security-profiles |
| Panorama playlists | playlists/panorama/panorama_*.skillet.yaml |
Device-hardening, notshared (full + dgstack), shared (full + dgtemplate + security-profiles) |
| Loadable configs | loadable_configs/sample-{cloud-AWS,cloud-Azure,cloud-GCP,mgmt-dhcp,mgmt-static,set-commands}/ |
Pre-rendered, ready-to-load XML/CONF for six common scenarios |
| Validations | validations/panos/IronSkillet_assessment_panos/, IronSkillet_v10x_update_panos/ |
Skillet-Validation reports for posture checks |
| Shared report template | validations/shared_report/report.yml |
Validation dashboard (stats_block + table) shared across versions |
| Build script | tools/build_all.sh |
Regenerates full templates, loadable configs, and spreadsheets |
| Submodule helper | tools/update_submodules.sh |
Keeps cross-version submodule pointers in sync |
| Archive scripts | tools/archive/build_all.py, build_full_templates.py, create_loadable_configs.py, create_set_spreadsheet.py, test_full_config.py, test_set_commands.py |
Legacy Python helpers, pre-SLI |
| Sphinx docs | docs/conf.py, docs/index.rst, docs/notice.rst, docs/Makefile |
Hosted at iron-skillet.readthedocs.io |
| Submodule mounts | submodules/, .gitmodules |
Cross-PAN-OS-version overlays |
1.4 Technology stack
| Layer | Technology | Notes |
|---|---|---|
| Templating | Jinja2 (>=2.10.1) |
{{ VAR }} substitution + {% if %}, custom md5_hash filter |
| Skillet schema | .meta-cnc.yaml (legacy) and .skillet.yaml (current) |
Embedded-element model uses the latter |
| Rendering toolchain | SLI (pip install sli), Panhandler (Docker), pan-cnc |
All consume the same .meta-cnc.yaml / .skillet.yaml |
| Output formats | PAN-OS XML, PAN-OS set-command CLI, Excel .xlsx |
Spreadsheet generated via XlsxWriter |
| Documentation | Sphinx (docs/Makefile, docs/conf.py), readthedocs |
Static |
| Submodule layout | git submodules per PAN-OS major.minor | tools/update_submodules.sh |
Note
The repo's design rationale is that every PAN-OS major.minor lives on a parallel branch (e.g.
panos_v10_1) instead of inside a version subdirectory of one branch. This keeps schema changes between PAN-OS versions cleanly bisectable and means a checkout at commit9833373onpanos_v10_1shows exactly the 10.1 templates — no other version's XML mixed in.
1.5 Data flow for the main workflow
The "fresh device bring-up" flow walks left-to-right through the diagram:
- The operator selects a playlist (e.g.
panos_full.skillet.yaml). - They supply variable values (or accept defaults) via SLI / Panhandler.
- The renderer:
- Resolves
include:directives — pulls each named snippet fromtemplates/panos/snippets/.meta-cnc.yaml. - Evaluates
when:clauses (e.g.config_admin_user == 'yes') and drops snippets whose condition is false. - Substitutes
{{ VAR }}placeholders, applying filters such asmd5_hashfor the admin password. - Concatenates the resulting XML elements in playlist order at the correct XPaths.
- Resolves
- The operator imports the rendered XML via the device UI or via
sli load_config -uc. The device places it in the candidate configuration. commitactivates it.
2. System Requirements
2.1 Target devices
| Device | Supported Versions | Notes |
|---|---|---|
| PAN-OS NGFW | Branch panos_v10_1 carries the 10.1 templates |
Older versions live on parallel branches |
| Panorama | Same branch model — panos_v10_1 ships Panorama 10.1 templates |
Variants for shared / not-shared / DG-template / DG-stack topologies |
2.2 Build-host requirements
For administrators who plan to render templates on a build host (CI, operator workstation):
| Requirement | Detail |
|---|---|
| Operating system | Linux, macOS, or Windows (anywhere Python or Docker runs) |
| Python (for SLI and archive scripts) | Python 3.6+ for sli; Python 3.7–3.9 for the legacy tools/archive/ scripts due to MarkupSafe==1.0 and PyYAML==5.4 pins |
| Docker (for Panhandler) | Any modern Docker engine that can pull paloaltonetworks/panhandler |
| Disk | A clone is ~50 MB; rendered output per scenario is <2 MB |
| Network | Outbound to GitHub for clone, optional outbound to the device's management IP for sli load_config / API push |
2.3 Python dependencies (legacy archive only)
tools/archive/requirements.txt — used only by the historical
build_all.py / build_full_templates.py / create_loadable_configs.py
flow:
Jinja2>=2.10.1
MarkupSafe==1.0
oyaml==0.7
passlib==1.7.1
PyYAML==5.4
XlsxWriter==1.1.2
requests==2.22.0
pexpect==4.7.0
pan-python==0.14.0
Note
SLI / Panhandler / pan-cnc do not use this requirements file — their dependencies are managed by their own packaging. Only run
pip install -r tools/archive/requirements.txtif you specifically need the archived scripts.
2.4 Licenses needed on the device
| Skillet feature | Required PAN-OS license |
|---|---|
| Antivirus profiles | Antivirus subscription |
| Anti-spyware + DNS sinkhole | Threat Prevention |
| Vulnerability profiles | Threat Prevention |
| URL filtering profiles | URL Filtering subscription |
| WildFire analysis profiles | WildFire subscription |
| SSL decryption profiles | (Decryption is included; no additional license) |
INCLUDE_PAN_EDL=yes external dynamic lists |
Threat Prevention |
Important
Loading IronSkillet on a device without the matching subscriptions won't break the commit, but the corresponding profile actions become no-ops at traffic time. The default rulebase is built around active subscriptions; treat the licenses as a hard prerequisite.
3. Configuration Reference
This is the complete inventory of variables, files, and snippets at
commit 9833373 on the panos_v10_1 branch. Use it as the canonical
source of truth — when the README, the spreadsheet, or a downstream
clone disagree, the .meta-cnc.yaml files win.
3.1 Configuration files
| File | Format | Location | Purpose |
|---|---|---|---|
| Snippet master (NGFW) | YAML (.meta-cnc.yaml) |
templates/panos/snippets/.meta-cnc.yaml |
Snippet bodies + variable inventory for PAN-OS NGFW |
| Snippet master (Panorama) | YAML | templates/panorama/snippets/.meta-cnc.yaml |
Same, Panorama |
| Full XML output (NGFW) | XML | templates/panos/full/iron_skillet_panos_full.xml |
Rendered full NGFW config |
| Full XML output (Panorama) | XML | templates/panorama/full/iron_skillet_panorama_full.xml |
Rendered full Panorama config |
| Full set-command (NGFW) | text | templates/panos/set_commands/iron_skillet_panos_full.conf |
Rendered full NGFW set-command CLI |
| Full set-command (Panorama) | text | templates/panorama/set_commands/iron_skillet_panorama_full.conf |
Rendered full Panorama set-command CLI |
| Variable spreadsheet (NGFW) | Excel | templates/panos/set_commands/iron_skillet_panos_full.xlsx |
Variable + set-command spreadsheet |
| Variable spreadsheet (Panorama) | Excel | templates/panorama/set_commands/iron_skillet_panorama_full.xlsx |
Same, Panorama |
| Baseline (NGFW) | XML | templates/panos/baseline/baseline.xml |
Rollback target for NGFW |
| Baseline (Panorama) | XML | templates/panorama/baseline/baseline.xml |
Rollback target for Panorama |
| NGFW playlists | YAML | playlists/panos/panos_*.skillet.yaml |
4 playlists |
| Panorama playlists | YAML | playlists/panorama/panorama_*.skillet.yaml |
6 playlists |
| Loadable configs | XML / .conf |
loadable_configs/sample-*/ |
12 ready-to-load files (6 scenarios x 2 device types) |
| Sphinx config | Python | docs/conf.py |
Documentation generator |
| Sphinx Makefile | Make | docs/Makefile (SPHINXPROJ = iron-skillet, BUILDDIR = _build) |
Builds the readthedocs site |
| Build orchestrator | Bash | tools/build_all.sh |
Regenerate full templates / loadable configs / spreadsheets |
| Submodule helper | Bash | tools/update_submodules.sh |
Keep cross-version submodule pointers fresh |
| Archive build | Python | tools/archive/build_all.py |
Legacy build orchestrator |
| Archive sub-builds | Python | tools/archive/build_full_templates.py, create_loadable_configs.py, create_set_spreadsheet.py, test_full_config.py, test_set_commands.py |
Pre-SLI helpers |
| Validation report (shared) | YAML | validations/shared_report/report.yml |
Skillet-Validation dashboard schema |
| NGFW assessment validation | YAML | validations/panos/IronSkillet_assessment_panos/.meta-cnc.yaml |
Posture-check skillet |
| NGFW v10x update validation | YAML | validations/panos/IronSkillet_v10x_update_panos/.meta-cnc.yaml |
Update-pre-flight skillet |
| Repo gitignore | text | .gitignore |
|
| Submodule manifest | text | .gitmodules |
|
| Repo metadata | Markdown | README.md, CONTRIBUTING.md, SUPPORT.md, LICENSE |
3.2 NGFW variables — complete inventory
Every variable defined in templates/panos/snippets/.meta-cnc.yaml at
commit 9833373. The Type column maps to the type_hint: field; the
Toggle column captures toggle_hint: parent gating.
| Variable | Type | Default | Toggle | Description |
|---|---|---|---|---|
config_mgmt_intf |
dropdown (yes/no) |
no |
— | Render management-interface IP settings |
config_admin_user |
dropdown (yes/no) |
no |
— | Create the IronSkillet admin user |
config_dns |
dropdown (yes/no) |
no |
— | Render DNS settings |
MGMT_TYPE |
dropdown (dhcp-client/static) |
dhcp-client |
config_mgmt_intf == yes |
Management IP mode |
MGMT_IP |
ip_address | 192.0.2.6 |
MGMT_TYPE == static |
Management IP |
MGMT_MASK |
ip_address | 255.255.255.0 |
MGMT_TYPE == static |
Management netmask |
MGMT_DG |
ip_address | 192.0.2.7 |
MGMT_TYPE == static |
Management default gateway |
DNS_1 |
ip_address | 8.8.8.8 |
config_dns == yes |
Primary DNS |
DNS_2 |
ip_address | 8.8.4.4 |
config_dns == yes |
Secondary DNS |
FW_NAME |
text | panos-01 |
— | Firewall hostname |
NTP_1 |
text | 0.pool.ntp.org |
— | Primary NTP |
NTP_2 |
text | 1.pool.ntp.org |
— | Secondary NTP |
API_KEY_LIFETIME |
text | 525600 |
— | API key lifetime in minutes (one year) |
ADMINISTRATOR_USERNAME |
text | adminuser |
config_admin_user == yes |
Admin username |
ADMINISTRATOR_PASSWORD |
password | adminuser |
config_admin_user == yes |
Admin password (hashed via md5_hash) |
SYSLOG_SERVER |
text | 192.0.2.2 |
— | Syslog server IP |
EMAIL_PROFILE_GATEWAY |
text | 192.0.2.1 |
— | SMTP gateway for email alerts |
EMAIL_PROFILE_FROM |
text | [email removed] |
— | From-address for email alerts |
EMAIL_PROFILE_TO |
text | [email removed] |
— | To-address for email alerts |
SINKHOLE_IPV4 |
text | sinkhole.paloaltonetworks.com |
— | DNS sinkhole FQDN (anti-spyware) |
SINKHOLE_IPV6 |
ip_address | 2600:5200::1 |
— | DNS sinkhole IPv6 |
INCLUDE_PAN_EDL |
dropdown (yes/no) |
no |
— | Include predefined PAN External Dynamic Lists in the rulebase |
Important
The
ADMINISTRATOR_PASSWORDdefault (adminuser) is a placeholder. Override it before rendering. The Jinjamd5_hashfilter writes its hash into the rendered XML's<phash>element — see Section 7.4 on handling rendered configs.
3.3 Panorama variables — complete inventory
From templates/panorama/snippets/.meta-cnc.yaml:
| Variable | Type | Default | Toggle | Description |
|---|---|---|---|---|
config_admin_panorama |
dropdown (yes/no) |
no |
— | Render Panorama admin user |
config_mgmt_intf_panorama |
dropdown (yes/no) |
no |
— | Render Panorama management-IP settings |
PANORAMA_TYPE |
dropdown (static/cloud) |
static |
config_mgmt_intf_panorama == yes |
Static IP or cloud (DHCP) |
PANORAMA_IP |
ip_address | 192.168.55.7 |
PANORAMA_TYPE == static |
Panorama mgmt IP |
PANORAMA_MASK |
ip_address | 255.255.255.0 |
PANORAMA_TYPE == static |
Panorama netmask |
PANORAMA_DG |
ip_address | 192.168.55.2 |
PANORAMA_TYPE == static |
Panorama default gateway |
PANORAMA_NAME |
text | panorama01 |
— | Panorama hostname |
DNS_1 |
ip_address | 8.8.8.8 |
— | Primary DNS |
DNS_2 |
ip_address | 8.8.4.4 |
— | Secondary DNS |
NTP_1 |
text | 0.pool.ntp.org |
— | Primary NTP |
NTP_2 |
text | 1.pool.ntp.org |
— | Secondary NTP |
CONFIG_EXPORT_IP |
text | 192.0.2.3 |
— | Scheduled-config-export destination |
ADMINISTRATOR_USERNAME |
text | adminuser |
— | Admin user |
ADMINISTRATOR_PASSWORD |
password | adminuser |
— | Admin password (md5-hashed) |
API_KEY_LIFETIME |
text | 525600 |
— | Same as NGFW |
EMAIL_PROFILE_GATEWAY |
text | 192.0.2.1 |
— | SMTP gateway |
EMAIL_PROFILE_FROM |
text | [email removed] |
— | From-address |
EMAIL_PROFILE_TO |
text | [email removed] |
— | To-address |
SYSLOG_SERVER |
text | 192.0.2.2 |
— | Syslog destination |
SINKHOLE_IPV4 |
text | sinkhole.paloaltonetworks.com |
— | DNS sinkhole FQDN |
SINKHOLE_IPV6 |
ip_address | 2600:5200::1 |
— | DNS sinkhole IPv6 |
INCLUDE_PAN_EDL |
dropdown (yes/no) |
no |
— | Include PAN EDLs |
STACK |
text | sample_stack |
— | Template-stack name (interpolated into XPaths) |
FW_NAME |
text | sample |
— | Device name for managed NGFW |
MGMT_TYPE |
dropdown (dhcp-client/static) |
dhcp-client |
— | Managed-firewall mgmt-IP mode |
MGMT_IP |
ip_address | 192.0.2.6 |
MGMT_TYPE == static |
Managed-firewall mgmt IP |
MGMT_MASK |
ip_address | 255.255.255.0 |
MGMT_TYPE == static |
Managed-firewall netmask |
MGMT_DG |
ip_address | 192.0.2.7 |
MGMT_TYPE == static |
Managed-firewall default gateway |
DEVICE_GROUP |
text | sample_devicegroup |
— | Device-group name (interpolated into XPaths) |
3.4 Playlist override variables
Playlist files in playlists/ can re-declare variables. The override
matrix:
| Playlist | Adds / overrides |
|---|---|
playlists/panos/panos_full.skillet.yaml |
config_mgmt_intf, config_admin_user, config_dns (re-declared) |
playlists/panos/panos_alert_only.skillet.yaml |
None — uses snippet defaults |
playlists/panos/panos_device_hardening.skillet.yaml |
None |
playlists/panos/panos_security_profiles.skillet.yaml |
None |
playlists/panorama/panorama_shared_full.skillet.yaml |
config_admin_panorama, config_mgmt_intf_panorama |
playlists/panorama/panorama_notshared_full.skillet.yaml |
config_admin_panorama, config_mgmt_intf_panorama, STACK, DEVICE_GROUP |
playlists/panorama/panorama_notshared_dgstack.skillet.yaml |
STACK, DEVICE_GROUP |
playlists/panorama/panorama_shared_dgtemplate.skillet.yaml |
None |
playlists/panorama/panorama_shared_security_profiles.skillet.yaml |
None |
playlists/panorama/panorama_device_hardening.skillet.yaml |
None |
3.5 Snippet inventory (NGFW)
The named snippets in the master templates/panos/snippets/.meta-cnc.yaml,
each with its target XPath:
| Snippet (parent.element) | Target XPath |
|---|---|
panos_ngfw_device_system_mgmt_ip_10_1.ironskillet_device_system_mgmt_type |
/config/devices/entry/deviceconfig/system |
panos_ngfw_device_system_dns_10_1.ironskillet_device_system_servers |
/config/devices/entry/deviceconfig/system/dns-setting |
panos_ngfw_device_system_10_1.ironskillet_device_system_dynamic_updates |
/config/devices/entry/deviceconfig/system |
panos_ngfw_device_system_10_1.ironskillet_device_system_snmp |
/config/devices/entry/deviceconfig/system |
panos_ngfw_device_system_10_1.ironskillet_device_system_ntp |
/config/devices/entry/deviceconfig/system |
panos_ngfw_device_system_10_1.ironskillet_device_system_login_banner |
/config/devices/entry/deviceconfig/system |
panos_ngfw_device_system_10_1.ironskillet_device_system_timezone |
/config/devices/entry/deviceconfig/system |
panos_ngfw_device_system_10_1.ironskillet_device_system_hostname |
/config/devices/entry/deviceconfig/system |
panos_ngfw_device_setting_10_1.ironskillet_device_setting_ctd |
/config/devices/entry/deviceconfig/setting |
panos_ngfw_device_setting_10_1.ironskillet_device_setting |
/config/devices/entry/deviceconfig/setting (management/admin-lockout/idle/api/commit-lock) |
panos_ngfw_device_setting_10_1.ironskillet_device_setting_wildfire |
/config/devices/entry/deviceconfig/setting (file-size limits + verdict reporting) |
panos_ngfw_device_setting_10_1.ironskillet_device_setting_config |
/config/devices/entry/deviceconfig/setting (rematch=yes) |
panos_ngfw_device_setting_10_1.ironskillet_device_setting_application |
/config/devices/entry/deviceconfig/setting |
panos_ngfw_device_setting_10_1.ironskillet_device_setting_logging |
/config/devices/entry/deviceconfig/setting |
panos_ngfw_device_setting_10_1.ironskillet_device_setting_tcp |
/config/devices/entry/deviceconfig/setting (TCP hardening) |
panos_ngfw_device_setting_10_1.ironskillet_device_setting_packet_buffer |
/config/devices/entry/deviceconfig/setting |
panos_ngfw_mgt_config_users_10_1.ironskillet_mgmt_config_users |
/config/mgt-config/users |
panos_ngfw_password_complexity_10_1.ironskillet_password_complexity |
/config/mgt-config/password-complexity |
panos_ngfw_shared_log_settings_10_1.ironskillet_shared_log_settings |
/config/shared/log-settings |
panos_ngfw_tag_10_1.ironskillet_tag_outbound / _inbound / _internal / _ironskillet_version |
/config/devices/entry/vsys/entry[@name='vsys1']/tag |
panos_ngfw_log_settings_profiles_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/log-settings/profiles |
panos_ngfw_profile_custom_urlFiltering_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/custom-url-category |
panos_ngfw_profile_decryption_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/decryption |
panos_ngfw_profile_antivirus_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/virus |
panos_ngfw_profile_spyware_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/spyware |
panos_ngfw_profile_vulnerability_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/vulnerability |
panos_ngfw_profile_file-blocking_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/file-blocking |
panos_ngfw_profile_urlFiltering_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/url-filtering |
panos_ngfw_profile_wildfire_analysis_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profiles/wildfire-analysis |
panos_ngfw_profile_group_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/profile-group |
panos_ngfw_rulebase_default_security_rules_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/default-security-rules |
panos_ngfw_rulebase_security_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security |
panos_ngfw_rulebase_decryption_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/decryption |
panos_ngfw_zone_protection_10_1.* |
/config/devices/entry/vsys/entry[@name='vsys1']/network/profiles/zone-protection-profile |
panos_ngfw_reports_simple_10_1.* |
/config/shared/reports (or vsys1 reports) |
panos_ngfw_report_group_simple_10_1.* |
/config/shared/report-group |
panos_ngfw_email_scheduler_10_1.* |
/config/shared/email-scheduler |
3.6 Hardened defaults — what the snippets actually write
Selected high-value defaults baked into the rendered XML:
| Setting | Value | Snippet |
|---|---|---|
| Login banner | "You have accessed a protected system. Log off immediately if you are not an authorized user." | ironskillet_device_system_login_banner |
| Timezone | UTC |
ironskillet_device_system_timezone |
| Threats update | every 30 min @ minute 2, threshold 48 | ironskillet_device_system_dynamic_updates |
| Antivirus update | hourly @ minute 4 | same |
| WildFire update | real-time | same |
| GlobalProtect data file | hourly @ minute 40 | same |
| GlobalProtect clientless VPN | hourly @ minute 50 | same |
| SNMP version | v3 only | ironskillet_device_system_snmp |
| Admin lockout | 5 failed attempts, 30-minute lockout | ironskillet_device_setting |
| Idle timeout | 10 minutes | same |
| Auto-acquire commit lock | enabled | same |
| Max rows in CSV export | 1,048,576 | same |
| Password min length | 12 | ironskillet_password_complexity |
| Password complexity | ≥1 upper, ≥1 lower, ≥1 numeric, ≥1 special | same |
| Password history | 24 entries | same |
| New-password differs by | 3 characters | same |
| TCP urgent-data | clear | ironskillet_device_setting_tcp |
| TCP drop-zero-flag | yes | same |
| TCP strip-mptcp-option | yes | same |
| Session packet-buffer protection | enabled | ironskillet_device_setting_packet_buffer |
| Rematch sessions on commit | yes | ironskillet_device_setting_config |
| Log suppression | disabled | ironskillet_device_setting_logging |
| Custom URL categories | Block, Allow, Custom-No-Decrypt |
ironskillet_custom_url_category_* |
| Tags | Outbound, Inbound, Internal, iron-skillet-version (version 0.0.2 for 10.1) |
ironskillet_tag_* |
| Sample syslog profile | Sample_Syslog_Profile → Sample_Syslog, UDP/514, BSD/LOG_USER |
ironskillet_shared_log_settings |
| Sample email profile | Sample_Email_Profile, display Threat_Alerts, SMTP |
same |
3.7 Annotated rendering walkthrough — fresh-NGFW scenario
A typical "fresh greenfield NGFW" render with custom values overrides the toggles and a handful of identity variables:
# my-overrides.yaml — input to SLI / Panhandler
config_mgmt_intf: 'yes'
config_admin_user: 'yes'
config_dns: 'yes'
MGMT_TYPE: static
MGMT_IP: 10.50.1.5
MGMT_MASK: 255.255.255.0
MGMT_DG: 10.50.1.1
DNS_1: 10.50.1.10
DNS_2: 10.50.1.11
FW_NAME: corp-fw-01
NTP_1: ntp1.example.com
NTP_2: ntp2.example.com
ADMINISTRATOR_USERNAME: secadmin
ADMINISTRATOR_PASSWORD: <strong-password-meeting-12-char-policy>
SYSLOG_SERVER: 10.50.1.50
EMAIL_PROFILE_GATEWAY: smtp.example.com
EMAIL_PROFILE_FROM: [email removed]
EMAIL_PROFILE_TO: [email removed]
INCLUDE_PAN_EDL: 'yes'
sli template -sd ./iron-skillet -ad \
-n skillet_full_panos_v10_1 \
./out/
The renderer:
- Loads the snippet master, resolves the
panos_fullplaylist's include list. - Evaluates every
when:clause against the overrides — three toggles are nowyes, sopanos_ngfw_device_system_mgmt_ip_10_1,panos_ngfw_device_system_dns_10_1, andpanos_ngfw_mgt_config_users_10_1all render. - Applies the
md5_hashfilter toADMINISTRATOR_PASSWORDand writes the result into<phash>. - Concatenates the snippet bodies in playlist order into
iron_skillet_panos_full.xml.
4. Deployment & Scaling
4.1 Single-device bring-up
The default deployment unit is one device, one rendered XML, one commit. Steps:
git cloneandgit checkoutthe matchingpanos_v10_1branch.- Render with SLI (
sli template -sd . -ad -n skillet_full_panos_v10_1 ./out/) or use the pre-renderedloadable_configs/sample-*files. - Push:
sli load_config -uc ./out/iron_skillet_panos_full.xml(or import via the web UI). - Commit on the device.
4.2 Fleet rollout via Panorama
The Panorama playlists (ironskillet_panorama_shared_10_1,
ironskillet_panorama_notshared_10_1, etc.) are designed to put
IronSkillet's profiles, tags, custom URL categories, log-forwarding,
and rulebase into a Panorama device-group + template-stack, so
that a single Panorama push applies the baseline to every managed
firewall.
Panorama (post-IronSkillet)
├── template-stack: {{STACK}}
│ ├── deviceconfig/system → DNS, NTP, banner, dynamic-updates, ...
│ ├── deviceconfig/setting → admin-lockout, idle-timeout, TCP hardening
│ └── mgt-config → admin user, password complexity
├── device-group: {{DEVICE_GROUP}}
│ ├── tag → Outbound, Inbound, Internal, iron-skillet-version
│ ├── profiles → virus, spyware, vulnerability, file-blocking,
│ │ url-filtering, wildfire-analysis, decryption,
│ │ custom-url-category
│ ├── profile-group → outbound, inbound, internal, alert_all, default
│ ├── pre-rulebase → security + decryption
│ └── post-rulebase → default-security-rules
└── shared
└── log-settings → email, syslog, log-forwarding, userid, hip,
correlation, gp, ip-tag forwarding
4.3 CI/CD render pipelines
A typical pipeline:
# .gitlab-ci.yml or GitHub Actions equivalent
stages: [render, validate, distribute]
render:
image: python:3.9
script:
- pip install sli
- git submodule update --init
- sli template -sd . -ad -n skillet_full_panos_v10_1 ./out/
artifacts:
paths: [out/]
validate:
image: python:3.9
script:
- apt-get update && apt-get install -y libxml2-utils
- xmllint --noout out/iron_skillet_panos_full.xml
- xmllint --noout out/iron_skillet_panorama_full.xml
distribute:
script:
- sli load_config -uc out/iron_skillet_panos_full.xml
4.4 Multi-version maintenance
When you fork IronSkillet to support a new PAN-OS version:
- Branch from the closest existing version branch
(
git checkout -b panos_v10_2 panos_v10_1). - Update version-suffixed snippet names (
*_10_1→*_10_2) and the skilletname:/label:fields throughout. - Verify XML schema compatibility against a 10.2 device — load the rendered candidate config and capture commit-time errors.
- Patch any newly-required elements (10.2-only features) and remove anything deprecated.
- Regenerate the loadable configs via
tools/build_all.sh. - Update
submodules/pointers viatools/update_submodules.sh.
Important
Issue #178 is the open feature-request for 11.1 / 11.2 / 12.1 support. Until it merges, those PAN-OS versions are not on a maintained branch and you'll need to fork-and-port if you must run them now.
4.5 Scaling considerations
IronSkillet is stateless build artefacts, so "scaling" mostly means:
- Rendering throughput — a single
sli templateinvocation takes seconds. Even a fleet of 1000+ firewalls is bottlenecked by per-device commit time, not render time. - Distribution — the rendered XML is <2 MB. Push via Panorama (one
XML, fan-out to N firewalls) or via per-device
sli load_config. - Variable inventory size — keep variable count under ~50 per scenario; beyond that, operators struggle with the form.
5. Operations
5.1 Day-to-day runbook
| Task | Command / location | Frequency |
|---|---|---|
| Pull upstream changes | git fetch origin && git checkout panos_v10_1 && git pull --ff-only |
Monthly or on advisory |
| Re-render after pull | sli template -sd . -ad -n skillet_full_panos_v10_1 ./out/ |
After every pull |
| Sync submodules | bash tools/update_submodules.sh |
After version-branch updates |
| Regenerate loadable_configs/ | bash tools/build_all.sh |
Before publishing a tag |
| XML lint | xmllint --noout out/*.xml |
After every render |
| Deploy to firewall | sli load_config -uc out/iron_skillet_panos_full.xml |
Per device bring-up |
| Deploy to Panorama | UI import + commit + push to devices | Per fleet change |
| Validate posture | Run IronSkillet_assessment_panos skillet via SLI |
After deploy + quarterly |
| Pre-update check | Run IronSkillet_v10x_update_panos skillet |
Before PAN-OS upgrade |
Note
The two validation skillets in
validations/panos/produce a green/red dashboard viavalidations/shared_report/report.yml. The shared report'spre_processingJinja block talliespass/fail/totalfor the operator.
5.2 Monitoring
There is no IronSkillet runtime to monitor. Monitor the device that IronSkillet was loaded onto:
- Syslog target (the value of
SYSLOG_SERVER) should show traffic, threat, WildFire, URL, data, tunnel, and auth log streams. - Email destination (
EMAIL_PROFILE_TO) should receive theEmail_Critical_System_LogsandEmail_Malicious_Verdictsalerts. - Scheduled config exports (Panorama only,
CONFIG_EXPORT_IP) should land at the SCP/FTP target on schedule.
5.3 Backup & rollback
Always save a snapshot before loading IronSkillet:
Device → Setup → Operations → Save named configuration snapshot
→ name: pre-ironskillet-YYYYMMDD
Roll back via:
Device → Setup → Operations → Load named configuration snapshot
→ pick the pre-ironskillet snapshot → Commit
Or load the in-repo templates/panos/baseline/baseline.xml for a
factory-style empty baseline.
Tip
A two-step rollback is more forgiving: load the snapshot, run Save candidate configuration, then commit. That gives you one more "Revert to running configuration" exit before you're locked into the rollback.
5.4 Audit & evidence
Iron-skillet writes an explicit version tag at
/config/devices/entry/vsys/entry[@name='vsys1']/tag/entry[@name='iron-skillet-version']
with comment version 0.0.2 for 10.1 - version of this IronSkillet template file. Use it as audit evidence:
ssh admin@firewall 'show config running' | grep iron-skillet-version
5.5 Common operations checklist
- [ ] Cloned the repo at the correct branch (
panos_v10_1). - [ ] Submodules updated (
tools/update_submodules.sh). - [ ] Variables overridden — particularly
ADMINISTRATOR_PASSWORD,SYSLOG_SERVER,EMAIL_PROFILE_*,FW_NAME, NTP/DNS. - [ ] Rendered output xmllint-clean.
- [ ] Pre-load snapshot saved on the device.
- [ ] Loaded as candidate config; previewed the diff in the UI.
- [ ] Committed.
- [ ] Validation skillet run and dashboard green.
- [ ] Rendered XML stored in a controlled location (encrypted, ACL'd).
- [ ] Admin password rotated post-commit; API key regenerated.
6. Performance
IronSkillet is a build-time tool — the "performance" axis is render time and the impact the rendered config has on the device.
6.1 Render performance
| Metric | Typical value |
|---|---|
sli template for skillet_full_panos_v10_1 |
<5 seconds on a modern laptop |
sli spreadsheet for skillet_set_command_panos_v10_1 |
<10 seconds (Excel write is the long pole) |
Full tools/build_all.sh rebuild |
<60 seconds (regenerates 12 loadable configs + 2 spreadsheets) |
| Repo clone | <30 seconds on a typical broadband connection |
6.2 Device-side impact
The rendered config has measurable effects on device behavior:
| Setting | Effect |
|---|---|
| Idle timeout = 10 minutes | Web-UI sessions log out faster than the PAN-OS default |
| Auto-acquire commit lock | Two concurrent admins can't both commit |
| Admin lockout 5 attempts / 30 min | Brute-force attempts get locked out quickly; legitimate typo-prone admins also get locked |
| Dynamic updates every 30 min (threats) | Slightly higher mgmt-plane CPU than the PAN-OS default |
| Packet-buffer protection enabled | Slightly higher dataplane CPU; protects against PBP exhaustion |
| Rematch sessions on commit | Existing sessions re-evaluated against the new rulebase — useful for security, but disruptive if a rulebase change blocks a category that was previously allowed |
| Log suppression off | Higher syslog and management-plane log volume |
Note
The "rematch sessions on commit" setting is a deliberate IronSkillet choice: when you push a new rulebase, every existing flow is re-tested. That makes IronSkillet's security stance "fail closed on commit" — but it also means a too-aggressive commit can drop established connections.
6.3 Storage on the device
| Item | Approximate size |
|---|---|
| Loaded XML | <2 MB |
| Per-day log volume (with all profiles + log-forwarding) | Highly site-dependent; typical 10–500 MB/day for a medium branch |
7. Security
IronSkillet's reason for existing is security hardening. This section covers the security controls the rendered config enforces, the secrets the rendered config contains, and the operational steps required to keep both safe.
7.1 Authentication & authorization
The rendered config writes:
- A superuser admin entry under
/config/mgt-config/userswhenconfig_admin_user == 'yes'. The username isADMINISTRATOR_USERNAME, the password is the MD5 hash ofADMINISTRATOR_PASSWORD(Jinja filtermd5_hash), stored in<phash>. - A password-complexity policy at
/config/mgt-config/password-complexity: length 12, at least one upper / lower / numeric / special, blocked username inclusion, 24-entry history, must differ by 3 characters from the previous password. - An admin-lockout policy at
/config/devices/entry/deviceconfig/setting/management/admin-lockout: 5 failed attempts, 30-minute lockout. - An idle-timeout of 10 minutes for management sessions.
- An API-key lifetime of
API_KEY_LIFETIMEminutes (default 525600 = one year). API keys minted by the device after the load are bounded by this lifetime.
7.2 Input validation
IronSkillet's variable forms enforce typing at render time
(type_hint: ip_address rejects non-IP input in Panhandler, dropdowns
constrain values to dd_list members). The renderer fails fast if an
unbound variable appears in a Jinja substitution.
7.3 Secrets handling
The repo itself does not carry production secrets. Defaults are
deliberate placeholders (adminuser / adminuser, 192.0.2.x,
8.8.8.8). The expectation is that the operator overrides them at
render time via SLI / Panhandler.
Important
Never commit a rendered IronSkillet XML file with real credentials or real management IPs back to a public repository. The defaults are public; the moment you override them, the rendered output becomes sensitive.
7.4 Treat rendered configurations as sensitive
IronSkillet's deliverable is a rendered PAN-OS configuration. The rendered XML is not sanitized: it carries the admin
password hash (<phash>, written by the md5_hash filter from ADMINISTRATOR_PASSWORD), and once the device is in
service the saved configuration also holds whatever the operator adds later, such as downstream service credentials
(LDAP, RADIUS, TACACS+, SNMP, syslog) and certificates.
Handle rendered and saved configurations as credential material:
- Set a strong
ADMINISTRATOR_PASSWORDat render time (the defaultadminuseris a placeholder). - Restrict ACLs on saved XML / set-command output (POSIX
0600, NTFS owner-only) and encrypt it at rest if it sits on shared storage. - Rotate API keys minted during the build pipeline at the end of the deployment window, and keep
API_KEY_LIFETIMEno longer than you need. - Never commit or share a rendered configuration with real values outside a need-to-know channel; remove
<phash>, server-profile credentials and certificate blocks first.
7.5 Network security
The rendered config enforces:
- SNMPv3 only —
snmp-setting/access-setting/version/v3. SNMPv1/v2c are disallowed. - TCP hardening —
urgent-data: clear,drop-zero-flag: yes,check-timestamp-option: yes,strip-mptcp-option: yes. - Session packet-buffer protection enabled.
- Default zone-protection profile wired into the zone-protection
snippet at
/config/devices/.../network/profiles/zone-protection-profile. - Default rulebase including the IronSkillet "default-security-rules" override and a curated security/decryption rulebase.
7.6 Logging & audit
The rendered config wires every log type — traffic, threat, WildFire,
URL, data, tunnel, auth — to the Sample_Syslog_Profile (UDP/514, BSD
format, LOG_USER facility) and emails critical system logs and
malicious WildFire verdicts via the Sample_Email_Profile. Log
suppression is disabled (<log-suppression>no</log-suppression>)
so that nothing gets aggregated away. The shared log-settings cover
config, system, user-id, HIP, GlobalProtect, IP-tag, and correlation
forwarding.
7.7 PAN-OS subscriptions
Section 2.4 already lists the licenses required for the threat profiles to act. From a security-control perspective: the rendered profiles exist regardless of license, but the device cannot enforce them without the relevant subscription. Verify subscriptions before relying on the IronSkillet baseline as a security control.
Warning
A profile group attached to a rule on a device without a Threat Prevention license logs a license-warning at commit time but does not block the commit. The rule will pass traffic without threat inspection. Always confirm
request license infoshows the required subscriptions before attesting that IronSkillet is enforced.
7.8 Hardening checklist (operator-side)
- [ ] Restricted ACLs on rendered configurations: every rendered
iron_skillet_*_full.xmlandiron_skillet_*_full.confis stored on owner-only filesystem permissions (POSIX0600/ NTFS owner-only) and never checked into a shared or public repo. Versioned-config storage encrypts at rest. - [ ] Post-deploy API key rotation cadence: every IronSkillet load
triggers an immediate API key rotation for the device, and a
recurring rotation no less often than the configured
API_KEY_LIFETIME. API keys minted during the build pipeline are revoked at the end of the deployment window. - [ ] Downstream credential rotation policy: LDAP bind, RADIUS shared secret, TACACS+ key, SNMP community / SNMPv3 user, and syslog credentials that are added to the IronSkillet-derived configuration are rotated on the same cadence as the rendered config (or on every deploy, whichever is shorter), and old credentials are revoked at their respective directory / IdP / SIEM.
8. Developer Internals
This section is the contributor's view: how the templates are structured, how to add a new snippet, how to add a new PAN-OS version, and how the test pattern works.
8.1 Repository structure
iron-skillet/
├── README.md
├── CONTRIBUTING.md
├── SUPPORT.md
├── LICENSE
├── .gitignore
├── .gitmodules
├── docs/ Sphinx site (readthedocs)
│ ├── conf.py
│ ├── index.rst
│ ├── notice.rst
│ └── Makefile
├── templates/ Snippet masters + pre-rendered output
│ ├── README.md
│ ├── panos/
│ │ ├── snippets/.meta-cnc.yaml (HIGH triage, 112209c)
│ │ ├── full/ (rendered XML)
│ │ ├── set_commands/ (rendered set-command)
│ │ └── baseline/baseline.xml
│ └── panorama/
│ ├── snippets/.meta-cnc.yaml (HIGH, 134929c)
│ ├── snippets_dgtemplate_shared/ (legacy, README marked deprecated)
│ ├── snippets_dgstack_notshared/ (legacy)
│ ├── snippets_notshared/ (legacy)
│ ├── full/
│ ├── set_commands/
│ └── baseline/baseline.xml
├── playlists/
│ ├── README.md
│ ├── panos/ (4 .skillet.yaml files)
│ └── panorama/ (6 .skillet.yaml files)
├── loadable_configs/ 12 ready-to-load files (6 scenarios x 2 device types)
├── validations/ Validation skillets + shared report
│ ├── README.md
│ ├── panos/
│ │ ├── IronSkillet_assessment_panos/
│ │ └── IronSkillet_v10x_update_panos/
│ └── shared_report/report.yml
├── tools/ Build orchestration
│ ├── README.md
│ ├── build_all.sh
│ ├── update_submodules.sh
│ ├── .gitignore
│ └── archive/ Pre-SLI Python helpers (deprecated path)
└── submodules/ Cross-version submodule mounts
8.2 Adding a new snippet to an existing version
- Edit
templates/panos/snippets/.meta-cnc.yaml(or the Panorama master). Append a new entry undersnippets::- name: panos_ngfw_<group>_10_1.ironskillet_<descriptive_name> xpath: /config/devices/entry[@name='localhost.localdomain']/... element: |- <your-element> <child>{{ YOUR_VAR }}</child> </your-element> cmd: set when: <optional Jinja predicate> - If the snippet introduces a new variable, add it to the
variables:block at the top of the same file withname,description,default,type_hint, optionaldd_list, optionaltoggle_hint, andhelp_text. - Add the snippet's
nameto the relevant playlists inplaylists/panos/. Place it in dependency order — after any snippets it depends on, before any that depend on it. - Regenerate the full XML, set-commands, and loadable configs:
bash tools/build_all.sh - Run the validation skillet to confirm the rendered output still passes posture checks.
8.3 The snippet-vs-full-config decision
When deciding whether a new feature should be a snippet or live in a hand-rolled full XML:
- Snippet if it's a reusable unit that any playlist might want to include or exclude (e.g. a new threat profile, a new tag).
- Full config is derived. Don't hand-edit the
full/XML — it's rebuilt by the renderer from the snippets viatools/build_all.sh.
8.4 Adding a new PAN-OS version
- Branch from the closest existing version branch:
git checkout -b panos_v10_2 panos_v10_1. - Find-and-replace the version suffix throughout — every snippet
name, every parent skilletname:/label:, every playlistinclude:reference (*_10_1→*_10_2). - Build a candidate render against a 10.2 device; capture commit errors. PAN-OS 10.2 may have schema additions (new XML elements) or removals.
- Patch each snippet whose schema changed.
- Regenerate via
tools/build_all.sh. - Update
submodules/pointers viatools/update_submodules.sh. - Tag the branch.
Note
The repo's README explicitly does not document a programmatic migration helper between versions — it's a human-driven port. Issue #178 (request for 11.1 / 11.2 / 12.1 branches) is outstanding; contributors who port to those versions can submit the work as a new branch via the standard CONTRIBUTING.md flow.
8.5 How variables resolve at render time
Render-time variable resolution is a layered lookup:
- The renderer reads the master
.meta-cnc.yaml'svariables:block — that's the schema. - It overlays the playlist's
variables:block (if any) — playlist declarations override snippet defaults. - It overlays the operator's input (CLI args, Panhandler form, YAML override file) — operator input wins.
- For each snippet selected by the playlist, it evaluates the
when:clause against the merged variable map. Falsy → snippet skipped. - Surviving snippets have their
element:body run through Jinja2 with the merged variable map. Filters used in the repo:md5_hash(exactly one use, the admin password) and the standard Jinja filter set. - Rendered snippet XML is concatenated at the snippet's
xpath.
8.6 Testing pattern
Two test layers exist in this commit:
- Static:
xmllint --noout out/iron_skillet_panos_full.xmlruns as part of the build to confirm the rendered XML parses. - Validation skillets:
validations/panos/IronSkillet_assessment_panos/andvalidations/panos/IronSkillet_v10x_update_panos/ship as Skillet-Validation packages. Run them via SLI against a candidate configuration; they emit a YAML report consumed byvalidations/shared_report/report.ymlto produce a green/red dashboard withTotal Checks,Checks Passed, andChecks Failedcounters. - Legacy archive tests:
tools/archive/test_full_config.pyandtest_set_commands.pyexist but are part of the deprecated archive pipeline; new contributors should rely on the validation skillets andxmllint.
8.7 Code patterns and conventions
- Snippet naming:
panos_ngfw_<area>_<version>.ironskillet_<thing>. The first half identifies the playlist that owns the snippet; theironskillet_*half identifies the specific element within. The version suffix is load-bearing — the renderer uses it to dispatch to the correct branch's snippet master. - XPath style: Always rooted at
/config/devices/entry[@name='localhost.localdomain']/...for NGFW. Panorama snippets that touch a managed device's config wrap the path in/config/devices/entry[@name='localhost.localdomain']/template-stack/entry[@name='{{STACK}}']/.../localhost.localdomain/.... element:style: YAML literal block (|-) preserves whitespace. Indent the XML body two spaces under the literal-block marker.when:style: Quote string comparisons (config_admin_user == 'yes'). Keep the comparison fully on one line.- Variable names: ALL_CAPS for substituted values, lower_snake for
toggles (
config_*).
8.8 Potential improvements
Areas where contributions are welcome:
- Fix the spreadsheet sinkhole default (issue #177) — the
iron_skillet_panos_full.xlsxships an outdated default for the IPv4 sinkhole; sync it withtemplates/panos/snippets/.meta-cnc.yaml. - Resolve Panhandler ingestion warnings (issue #172) — some
legacy snippet directories still ship
.meta-cnc.yamlreferencing external XML files; either modernize to embedded-element or remove. - Add maintained 11.1 / 11.2 / 12.1 branches (issue #178).
- Expand the validation skillets to cover every IronSkillet element, not just the high-level posture indicators.
- Replace
MarkupSafe==1.0andPyYAML==5.4pins intools/archive/requirements.txtso the archive scripts can run on Python 3.11+.
8.9 Contributing guide
The repo's CONTRIBUTING.md is the canonical contributor onboarding.
At a high level: branch, edit, render, lint, validate, PR against the
relevant version branch (don't PR against main).
9. Integration Points
iron-skillet is a build artifact, so "integration" means: which tools consume it, which devices receive it, and which systems are downstream.
9.1 Skillet-runner integrations
| Tool | Role | Consumes |
|---|---|---|
SLI (gitlab.com/panw-gse/as/sli) |
Maintained CLI runner | templates/*/snippets/.meta-cnc.yaml, playlists/*/*.skillet.yaml |
| Panhandler (live.paloaltonetworks.com) | Docker GUI | Same |
| pan-cnc | Programmatic rendering | Same |
tools/archive/build_full_templates.py |
Legacy in-repo build | templates/*/snippets/.meta-cnc.yaml |
9.2 Device integrations
| Target | Mechanism | Driver |
|---|---|---|
| PAN-OS NGFW | XML import via Device → Setup → Operations |
Manual or sli load_config -uc |
| PAN-OS NGFW | Set-command paste in configure mode |
Manual or sli load_set -uc |
| Panorama | XML import via Panorama → Setup → Operations |
Manual or sli load_config -uc |
| Panorama-managed firewalls | Push from Panorama | Panorama's commit-and-push |
9.3 Downstream system integrations (after device load)
The rendered config wires the device into your environment:
| Downstream system | Variable | Default | Used in |
|---|---|---|---|
| Syslog SIEM | SYSLOG_SERVER |
192.0.2.2 |
Sample_Syslog_Profile UDP/514 BSD/LOG_USER |
| SMTP | EMAIL_PROFILE_GATEWAY, EMAIL_PROFILE_FROM, EMAIL_PROFILE_TO |
192.0.2.1 / [email removed] / [email removed] |
Sample_Email_Profile for critical system + WildFire verdict alerts |
| NTP | NTP_1, NTP_2 |
0.pool.ntp.org / 1.pool.ntp.org |
Device clock |
| DNS | DNS_1, DNS_2 |
8.8.8.8 / 8.8.4.4 |
Device resolution |
| DNS sinkhole | SINKHOLE_IPV4, SINKHOLE_IPV6 |
sinkhole.paloaltonetworks.com / 2600:5200::1 |
Anti-spyware DNS-sinkhole |
| Palo Alto threat content | (implicit) | updates.paloaltonetworks.com |
Dynamic-update schedule |
| Scheduled config export (Panorama) | CONFIG_EXPORT_IP |
192.0.2.3 |
Panorama config snapshot destination |
9.4 Validation reporting integration
The validations/shared_report/report.yml is the integration surface
for Skillet-Validation viewers: it defines a stats block (Total /
Passed / Failed) and a results table that any
Skillet-Validation-compatible viewer (Panhandler, custom dashboards)
can render. The pre_processing Jinja block is the data-shaping
contract: viewers feed it raw data.<check> results and receive a
{checks: [...], stats: {...}} JSON object back.
9.5 Cross-version submodule integration
.gitmodules and tools/update_submodules.sh define how this branch
references sibling PAN-OS version branches. The submodules/ directory
acts as a mount point for cross-version overlays — useful when a
multi-version build job needs all branches reachable from one working
tree.
# typical multi-version setup
git clone https://github.com/PaloAltoNetworks/iron-skillet.git
cd iron-skillet
bash tools/update_submodules.sh
ls submodules/ # version-branch mounts
Tip
If you maintain a private fleet that runs multiple PAN-OS versions simultaneously (mid-upgrade phase), keep one IronSkillet checkout per version branch in CI rather than relying on submodules — the submodule layout shines when one operator workflow needs cross-version visibility, not when CI bakes a single version's artifacts.
9.6 External documentation
The repo links out to:
- Iron-Skillet readthedocs —
the Sphinx-rendered docs site (built from
docs/). - Variables list and descriptions — the upstream variable reference (this document covers the same data in §3).
- Palo Alto Networks Live community — Panhandler installation guide and skillet ecosystem.
- SLI repository (GitLab) — upstream for the maintained skillet runner.