Multi-layer security for GitHub Actions runners with network filtering (DNS + iptables), privilege control (sudo management), and integrity validation (tampering detection). Implements default-deny policy with comprehensive security reporting.
- Run code you did not write: external PRs, third-party actions, or dependencies from npm, PyPI and similar registries
- Need compliance evidence showing network access controls and audit trails
- Want observability before enforcement: analyze mode reports what your build reaches without blocking anything
- You only run trusted, first-party code with no external dependencies
- Your workflow needs unrestricted network access to arbitrary domains
- Your runner is not Ubuntu, or your job runs in a container: see Platform Support
- Setup time matters more than the coverage: the action installs
dnsmasqandipsetfrom the Ubuntu archive before your first step runs, so the overhead is dominated by apt and is measured in tens of seconds, not milliseconds - You need protection against a determined attacker abusing an allowed domain: this is a first line of defense, not a complete one
Open Source Maintainers: Protect against malicious PRs that install compromised dependencies attempting to exfiltrate repository secrets or tokens.
Enterprise CI/CD: Add network observability and control to GitHub Actions workflows handling sensitive data or credentials.
Security Compliance: Generate audit trails showing network access controls were enforced during builds and deployments.
Dependency Analysis: Use analyze mode to understand what external services your build dependencies are contacting.
- Dual modes:
analyze(monitoring) orenforce(blocking) - DNS filtering: DNSMasq with Quad9 upstream resolver
- Firewall rules: iptables prevents DNS bypass via direct IP connections
- Sudo logging: All sudo usage logged to
/var/log/safer-runner/main-sudo.log - Sudo disabling: Optionally disable sudo access after setup (prevents privilege escalation)
- Docker disabling: Optionally disable Docker access (prevents container escape attacks)
- Custom domains: Add trusted domains via input parameter
- Automatic reporting: Network access provenance in job summaries
- Risky subdomain blocking: Blocks gist.github.com and raw.githubusercontent.com by default in enforce mode
ℹ️ How it works: This action uses a pre hook to establish security monitoring in analyze mode before any workflow steps run, then the main action applies your desired configuration (analyze or enforce mode).
steps:
- uses: PortSwigger/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- run: |
curl https://example.com # Logged but not blockedsteps:
- uses: PortSwigger/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2
with:
mode: 'enforce'
allowed-domains: |
example.com
api.trusted-service.com
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- run: |
curl https://api.trusted-service.com # ✅ Allowed
curl https://malicious.com # ❌ Blocked| Input | Description | Default |
|---|---|---|
enabled |
Whether to apply protection at all. Set false on runners that cannot support it - see Platform Support |
true |
mode |
analyze (log only) or enforce (block). Case-insensitive; any other value fails the job |
analyze |
allowed-domains |
Additional domains to allow | '' |
primary-dns-server |
Primary DNS server for allowed domains | 9.9.9.9 (Quad9) |
secondary-dns-server |
Secondary DNS server for redundancy | 149.112.112.112 (Quad9) |
fail-on-tampering |
Fail workflow if security config is tampered | false |
block-risky-github-subdomains |
Block gist.github.com and raw.githubusercontent.com in enforce mode | true |
disable-sudo |
Disable sudo access for runner user after setup | false |
sudo-config |
Custom sudoers configuration for runner user (multi-line string) | '' |
disable-docker |
Remove runner user from docker group (prevents container usage) | false |
Job summaries include a Network Access Provenance table showing:
- Domain/IP addresses accessed
- Ports used
- Status (✅ Allowed, ❌ Denied, 📊 Analyzed)
- Source (GitHub Required, User Defined)
In analyze mode, the report suggests an allowed-domains configuration based on non-GitHub domains accessed, making it easy to transition to enforce mode.
This action implements multi-layer security with DNS filtering, firewall rules, and privilege control. GitHub Actions required domains are automatically allowed. Sudo usage is always logged for auditability.
Control sudo and Docker access to prevent privilege escalation and container escape attacks:
steps:
- uses: PortSwigger/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2
with:
mode: 'enforce'
disable-sudo: 'true' # Prevents sudo usage after setup
disable-docker: 'true' # Prevents Docker/container usage
allowed-domains: |
registry.npmjs.org
- run: npm ci && npm test # ✅ Works without elevated privileges
- run: sudo apt install x # ❌ Fails (sudo disabled)
- run: docker build . # ❌ Fails (Docker disabled)Note: Only disable sudo/Docker if your workflow doesn't require them. These are advanced security features that prevent malicious code from bypassing security controls.
Configure alternative DNS providers while maintaining the same security guarantees:
steps:
- uses: PortSwigger/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2
with:
mode: 'enforce'
allowed-domains: 'npmjs.org pypi.org'
primary-dns-server: '1.1.1.1' # Cloudflare DNS
secondary-dns-server: '1.0.0.1' # Cloudflare secondary
- run: npm install # DNS queries use Cloudflare instead of Quad9Default DNS: Quad9 (9.9.9.9 / 149.112.112.112), which filters known-malicious domains upstream of the action's own allowlist
Disable secondary DNS by passing an empty string (single DNS server only):
steps:
- uses: PortSwigger/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2
with:
mode: 'enforce'
allowed-domains: 'example.com'
primary-dns-server: '8.8.8.8'
secondary-dns-server: '' # Disable secondary DNSNote: Both DNS servers must be explicitly allowed in firewall rules. The action automatically configures iptables to allow DNS traffic only from the dnsmasq process to the configured servers.
-
GitHub-hosted runners: Ubuntu only. Windows and macOS are not supported.
-
Self-hosted runners: Ubuntu only, on a VM with passwordless sudo and systemd.
-
Container-based runners: not supported. Actions Runner Controller pods and Docker executors cannot run this action, and no configuration of the action changes that. The blocker is the privilege model: such pods usually set
securityContext.allowPrivilegeEscalation: false, which sets the kernel'sno_new_privsbit; the kernel then ignores the setuid bit on/usr/bin/sudo, and sudo exits before it has read sudoers, so no sudoers rule helps. There is also no systemd to restartdnsmasqandrsyslog, and/etc/resolv.confis a bind mount owned by the kubelet.The action detects this and says so, rather than retrying
apt-getand then running the job unprotected. Because GitHub defaultspre-ifandpost-iftoalways(), a step-levelif:does not prevent the hooks from running - setenabled: falseinstead.Restricting egress on runners you own is better done at the platform layer, where the control sits outside the job and cannot be tampered with by workflow code.
Network filtering provides a first line of defense but has limitations:
- Pre-hook ordering: This action's
pre:hook establishes analyze mode monitoring before the main action step runs, providing early visibility - Other actions' pre-hooks: Actions that appear later in the workflow will have their
pre:hooks run after this action's pre-hook, so they are monitored - Actions before this one: Any actions placed before this action in the workflow will have their
pre:hooks run before monitoring is established - Mitigation: Place this action as the first step in your workflow and carefully vet all actions used
- Data exfiltration via allowed domains: Abuse GitHub/npm/PyPI to upload secrets
- Local file system attacks: Stage data for later exfiltration
- Process/system call abuse: Container escapes, privilege escalation
Combine with additional security layers:
- Runtime security: Falco, Tracee
- Container security: Distroless images, read-only filesystems, non-root users
- Secrets management: GitHub secrets, secure credential handling
- Dependency scanning: Snyk, Dependabot, GitHub native scanning
- Action security: Pin to commits, use trusted publishers
View DNS and firewall logs:
# DNS logs (no sudo required)
cat /var/log/safer-runner/pre-dns.log # Pre-hook DNS activity
cat /var/log/safer-runner/main-dns.log # Main action DNS activity
# Network logs (no sudo required)
cat /var/log/safer-runner/pre-iptables.log # Pre-hook network activity
cat /var/log/safer-runner/main-iptables.log # Main action network activity
# Sudo logs (no sudo required)
cat /var/log/safer-runner/pre-sudo.log # Pre-hook sudo commands
cat /var/log/safer-runner/main-sudo.log # Main action sudo commandsMIT. See LICENSE.