Policy recipes¶
Concrete .policy examples covering the patterns operators (and the LLMs they ask to draft policies for them) reach for most often. Each recipe is a complete, drop-in file: copy it into /etc/enforcegate/rules.d/, run eghost policy reload, and the rule is live.
The pages it draws on:
- Policies — file location, evaluation order, comment styles.
- Policy reference — full grammar: every match attribute, every action.
- Policy rollback — snapshot and recovery after a bad reload.
How LLMs should use this page
Recipes are full file contents, not snippets — each block has a name, at least one match attribute, an application, an action, and a description. When adapting one, change the name, the match value, and the description to your case; the rest of the shape is usually correct as-is.
.policy is a permissive JSON-like grammar. Unquoted keys and string values are fine, but a syntactically-broken file is rejected at compile time — always run eghost policy reload after editing and read the validation output.
Allow specific package mirrors¶
Default-permit deployments still benefit from explicit allow rules early in the file: the engine logs the matched rule name on every verdict, which makes audit grep significantly easier.
allow-debian-mirror: {
match-uri: https://deb.debian.org/debian/
application: https
action: permit
description: Debian package mirror
}
allow-ubuntu-mirror: {
match-uri-regex: ^https?://[^/]*\.ubuntu\.com/(ubuntu|releases)/
application: https
action: permit
description: Ubuntu archives and releases (any mirror)
}
allow-python-pypi: {
match-domain-list: /etc/enforcegate/rules.d/lists/pypi-mirrors.txt
application: https
action: permit
description: PyPI and trusted mirrors
}
Why: match-uri is a literal prefix — cheap and unambiguous for a single host. match-uri-regex covers a host pattern. match-domain-list lets ops curate a long allow-list of mirrors in a separate file without touching the policy.
Block social media (warn-with-proceed pattern)¶
Friendlier than a hard deny. The user lands on the captive portal, reads the explanation, and can click Proceed anyway — the engine records the acknowledgement and permits subsequent requests from the same (rule_name, client_ip) pair for a configured ack window.
warn-social-media: {
match-domain-list: /etc/enforcegate/rules.d/lists/social-media.txt
application: https
action: warn
description: Social media — proceed with caution
}
Companion list file (one domain per line, comments with #):
facebook.com
instagram.com
tiktok.com
twitter.com
x.com
# add more as needed
Why: match-domain-list auto-handles the www. prefix and TLD dot-escaping; hand-written regex doesn't. The ack window is set on the engine side via [captive_portal].ack_session_duration_s — see the engine reference.
Acceptable Use Policy gate on large download services¶
The aup action redirects through the captive portal to a separate page where the visitor must explicitly accept an AUP (typically a checkbox + a longer policy text) before proceeding. Useful for streaming, cloud storage, and large download sites where bandwidth is the concern.
aup-cloud-storage: {
match-domain-list: /etc/enforcegate/rules.d/lists/cloud-storage.txt
application: https
action: aup
description: Cloud storage — acknowledge fair-use policy before continuing
}
Why: AUP is a distinct flow from warn — different page, different rendered copy, but the same operator ergonomics. The captive portal renders the page in the visitor's language (English, French, German, Italian — see captive portal).
Block known malware / phishing domains¶
Hard deny — no captive-portal click-through. The visitor lands on the block page with the rule's description as the explanation.
block-malware-domains: {
match-domain-list: /etc/enforcegate/rules.d/lists/malware-domains.txt
application: https
action: deny
description: Known malware distribution domain — blocked
}
block-phishing-domains: {
match-domain-list: /etc/enforcegate/rules.d/lists/phishing-domains.txt
application: https
action: deny
description: Known phishing host — blocked
}
Why: Two separate rule names so the audit log distinguishes malware blocks from phishing blocks at a glance. The companion lists are typically refreshed from a threat-intel feed — drop the new file in place and run eghost policy reload.
Allow only listed internal services (default-deny posture)¶
The strict pattern. Set the engine's no-match verdict to deny via [policy].default_action, then ship explicit permit rules for everything operators want to allow.
allow-internal-corp-domain: {
match-uri-regex: ^https?://[^/]*\.corp\.example\.com/
application: https
action: permit
description: Internal corp services
}
allow-saas-tools: {
match-domain-list: /etc/enforcegate/rules.d/lists/approved-saas.txt
application: https
action: permit
description: Approved SaaS tools
}
Why: default_action is the engine-synthesised no-match verdict — a request that no rule matched is denied without consuming a rule id, so there is no catch-all rule to shadow lower-precedence [policy].shared_path rules. Flipping posture is a one-line config change; no policy-file surgery needed. The captive-portal block page reports the rule name as default-deny for matched-by-default verdicts. See [policy].default_action for the full knob reference and Policies for the rule-id precedence model.
Restrict access by client subnet¶
Useful for segregating guest networks, admin-only access to internal services, or carve-outs for printer subnets that shouldn't browse.
guest-deny-corp-domain: {
match-client-ip: 10.20.30.0/24
match-uri-regex: ^https?://[^/]*\.corp\.example\.com/
application: https
action: deny
description: Guest subnet may not reach internal corp services
}
Why: Multiple match-* attributes in one block are an AND — the rule matches only when both the client IP is in the guest subnet and the URI is a corp host. For OR semantics, write two separate rules.
Allow a specific User-Agent everywhere (CI / automation carve-out)¶
Common request: a CI runner needs unrestricted egress without weakening the policy for users. Match by User-Agent and place the rule early.
allow-ci-runner: {
match-user-agent: ^my-ci-runner/[0-9.]+$
application: https
action: permit
description: Internal CI runner — unrestricted egress
}
Why: 03- puts this before the threat-block rules at 20-, so the CI runner isn't accidentally caught by them. The regex anchors with ^ and $ so unrelated agents that happen to contain the string my-ci-runner somewhere don't match.
Force HTTPS-only on a sensitive domain¶
Block plain HTTP to a particular host (useful when you're confident the host is HTTPS-capable and want to refuse clients that don't upgrade).
deny-http-bank: {
match-uri-regex: ^http://[^/]*\.bank\.example\.com/
application: http
action: deny
description: Bank domain must be reached over HTTPS
}
Why: application: http scopes the rule to the plain-HTTP listener. The companion permit for HTTPS is the default (no explicit rule needed if your default is permit).
Companion CLI¶
After dropping any of the above into /etc/enforcegate/rules.d/, validate and apply:
# Dry-run parse + compile without activating
eghost policy reload --dry-run
# Apply
eghost policy reload
# Verify a specific URL against the live policy
eghost cli
> show policy match https://facebook.com/
show policy match returns the matched rule name and verdict — the fastest way to verify a recipe is doing what you intended. See egctl for the full diagnostic verb.
When the engine refuses to reload¶
If policy reload fails, the engine keeps the previously-loaded policy live and reports the validation error. Common causes:
- Missing
,between attributes inside a block. - Hand-written regex with un-escaped
.— usematch-domain-listinstead, which auto-escapes. - Reference to a list file that doesn't exist at the path given to
match-domain-listormatch-url-list. - Catch-all without
match-*— every block needs at least one match attribute, even the default.
The validation output names the offending file and the line; see troubleshooting for the pitfalls list.