{"id":46781552,"url":"https://github.com/portswigger-tim/safer-runner-action","last_synced_at":"2026-03-10T00:12:47.129Z","repository":{"id":285998569,"uuid":"960030397","full_name":"portswigger-tim/safer-runner-action","owner":"portswigger-tim","description":"A GitHub action to lockdown DNS, Network, sudo and Docker ","archived":false,"fork":false,"pushed_at":"2025-12-19T13:36:59.000Z","size":1234,"stargazers_count":2,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-21T19:11:55.560Z","etag":null,"topics":["github-actions","github-hosted-runners","security-automation","ubuntu"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/portswigger-tim.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-04-03T18:40:57.000Z","updated_at":"2025-12-19T13:36:42.000Z","dependencies_parsed_at":"2025-10-06T10:16:05.074Z","dependency_job_id":"3ac6be1c-8ce3-4659-93f8-4ceb22007d90","html_url":"https://github.com/portswigger-tim/safer-runner-action","commit_stats":null,"previous_names":["portswigger-tim/safer-runner-action"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/portswigger-tim/safer-runner-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portswigger-tim%2Fsafer-runner-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portswigger-tim%2Fsafer-runner-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portswigger-tim%2Fsafer-runner-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portswigger-tim%2Fsafer-runner-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/portswigger-tim","download_url":"https://codeload.github.com/portswigger-tim/safer-runner-action/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portswigger-tim%2Fsafer-runner-action/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30317863,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T20:05:46.299Z","status":"ssl_error","status_checked_at":"2026-03-09T19:57:04.425Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["github-actions","github-hosted-runners","security-automation","ubuntu"],"created_at":"2026-03-10T00:12:44.057Z","updated_at":"2026-03-10T00:12:47.119Z","avatar_url":"https://github.com/portswigger-tim.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Safer Runner Action\n\nMulti-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.\n\n## Who This Is For\n\n### ✅ Use This Action If You:\n\n- **Run untrusted code** in GitHub Actions (open source projects accepting PRs from external contributors)\n- **Use third-party actions** and want visibility into their network activity\n- **Accept community contributions** and need supply chain attack protection\n- **Build open source software** with dependencies from npm, PyPI, or other package registries\n- **Need compliance evidence** showing network access controls and audit trails\n- **Use GitHub-hosted Ubuntu runners** (ubuntu-latest, ubuntu-22.04, ubuntu-20.04)\n- **Want security observability** before committing to enforcement (analyze mode)\n\n### ❌ This Action Is NOT For You If:\n\n- **You only run trusted, first-party code** with no external dependencies\n- **You use Windows or macOS runners** (Linux/Ubuntu only)\n- **Your jobs run in containers** (`container:` in workflow - sudo access conflicts)\n- **You use self-hosted non-Ubuntu runners** (RHEL, Debian, etc. - not currently supported)\n- **Your workflow requires unrestricted network access** to arbitrary domains\n- **You need sub-second performance** (adds ~2-5s overhead for security setup)\n- **You want protection against sophisticated attackers** using allowed domain abuse (this is first-line defense only)\n\n### 💡 Common Use Cases\n\n**Open Source Maintainers**: Protect against malicious PRs that install compromised dependencies attempting to exfiltrate repository secrets or tokens.\n\n**Enterprise CI/CD**: Add network observability and control to GitHub Actions workflows handling sensitive data or credentials.\n\n**Security Compliance**: Generate audit trails showing network access controls were enforced during builds and deployments.\n\n**Dependency Analysis**: Use analyze mode to understand what external services your build dependencies are contacting.\n\n## Features\n\n- **Dual modes**: `analyze` (monitoring) or `enforce` (blocking)\n- **DNS filtering**: DNSMasq with Quad9 upstream resolver\n- **Firewall rules**: iptables prevents DNS bypass via direct IP connections\n- **Sudo logging**: All sudo usage logged to `/var/log/safer-runner/main-sudo.log`\n- **Sudo disabling**: Optionally disable sudo access after setup (prevents privilege escalation)\n- **Docker disabling**: Optionally disable Docker access (prevents container escape attacks)\n- **Custom domains**: Add trusted domains via input parameter\n- **Automatic reporting**: Network access provenance in job summaries\n- **Risky subdomain blocking**: Blocks gist.github.com and raw.githubusercontent.com by default in enforce mode\n\n## Usage\n\n⚠️ **Important**: Always place this action as the **first step** in your workflow to maximize security coverage.\n\nℹ️ **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).\n\n### Analyze mode (default)\n\n```yaml\nsteps:\n  - uses: portswigger-tim/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2\n  - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0\n  - run: |\n      curl https://example.com  # Logged but not blocked\n```\n\n### Enforce mode\n\n```yaml\nsteps:\n  - uses: portswigger-tim/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2\n    with:\n      mode: 'enforce'\n      allowed-domains: |\n        example.com\n        api.trusted-service.com\n  - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0\n  - run: |\n      curl https://api.trusted-service.com  # ✅ Allowed\n      curl https://malicious.com  # ❌ Blocked\n```\n\n## Inputs\n\n| Input | Description | Default |\n|-------|-------------|---------|\n| `mode` | `analyze` (log only) or `enforce` (block) | `analyze` |\n| `allowed-domains` | Additional domains to allow | `''` |\n| `primary-dns-server` | Primary DNS server for allowed domains | `9.9.9.9` (Quad9) |\n| `secondary-dns-server` | Secondary DNS server for redundancy | `149.112.112.112` (Quad9) |\n| `fail-on-tampering` | Fail workflow if security config is tampered | `false` |\n| `block-risky-github-subdomains` | Block gist.github.com and raw.githubusercontent.com in enforce mode | `true` |\n| `disable-sudo` | Disable sudo access for runner user after setup | `false` |\n| `sudo-config` | Custom sudoers configuration for runner user (multi-line string) | `''` |\n| `disable-docker` | Remove runner user from docker group (prevents container usage) | `false` |\n\n## Network Access Reports\n\nJob summaries include a Network Access Provenance table showing:\n\n- Domain/IP addresses accessed\n- Ports used\n- Status (✅ Allowed, ❌ Denied, 📊 Analyzed)\n- Source (GitHub Required, User Defined)\n\nIn analyze mode, the report suggests an `allowed-domains` configuration based on non-GitHub domains accessed, making it easy to transition to enforce mode.\n\n## Security Model\n\nThis 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.\n\n### Privilege Control (Optional)\n\nControl sudo and Docker access to prevent privilege escalation and container escape attacks:\n\n```yaml\nsteps:\n  - uses: portswigger-tim/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2\n    with:\n      mode: 'enforce'\n      disable-sudo: 'true'    # Prevents sudo usage after setup\n      disable-docker: 'true'  # Prevents Docker/container usage\n      allowed-domains: |\n        registry.npmjs.org\n  - run: npm ci \u0026\u0026 npm test   # ✅ Works without elevated privileges\n  - run: sudo apt install x   # ❌ Fails (sudo disabled)\n  - run: docker build .       # ❌ Fails (Docker disabled)\n```\n\n**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.\n\n### Custom DNS Servers\n\nConfigure alternative DNS providers while maintaining the same security guarantees:\n\n```yaml\nsteps:\n  - uses: portswigger-tim/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2\n    with:\n      mode: 'enforce'\n      allowed-domains: 'npmjs.org pypi.org'\n      primary-dns-server: '1.1.1.1'      # Cloudflare DNS\n      secondary-dns-server: '1.0.0.1'    # Cloudflare secondary\n  - run: npm install  # DNS queries use Cloudflare instead of Quad9\n```\n\n**Default DNS**: Quad9 (`9.9.9.9` / `149.112.112.112`) with 98% malware blocking\n\n**Disable secondary DNS** by passing an empty string (single DNS server only):\n\n```yaml\nsteps:\n  - uses: portswigger-tim/safer-runner-action@b2208f653b6bf422e08501155f4df82bad008184 # v1.2.2\n    with:\n      mode: 'enforce'\n      allowed-domains: 'example.com'\n      primary-dns-server: '8.8.8.8'\n      secondary-dns-server: ''  # Disable secondary DNS\n```\n\n**Note**: 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.\n\n## Limitations\n\n### Platform Support\n\n- **GitHub-hosted runners**: Only Ubuntu runners are supported. Windows and macOS runners are not supported.\n- **Self-hosted runners**: Only Ubuntu runners with sudo access and iptables support. Other Linux distributions (RHEL, Debian, etc.) and Windows/macOS runners are not supported.\n- **Containerized jobs**: Not supported when the job runs in a container due to sudo access requirements.\n\n### Security Limitations\n\nNetwork filtering provides a first line of defense but has limitations:\n\n#### Timing Window\n- **Pre-hook ordering**: This action's `pre:` hook establishes analyze mode monitoring before the main action step runs, providing early visibility\n- **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\n- **Actions before this one**: Any actions placed before this action in the workflow will have their `pre:` hooks run before monitoring is established\n- **Mitigation**: Place this action as the first step in your workflow and carefully vet all actions used\n\n#### Attack Vectors\n- **Data exfiltration via allowed domains**: Abuse GitHub/npm/PyPI to upload secrets\n- **Local file system attacks**: Stage data for later exfiltration\n- **Process/system call abuse**: Container escapes, privilege escalation\n\n### Defense in Depth\n\nCombine with additional security layers:\n\n- **Runtime security**: Falco, Tracee\n- **Container security**: Distroless images, read-only filesystems, non-root users\n- **Secrets management**: GitHub secrets, secure credential handling\n- **Dependency scanning**: Snyk, Dependabot, GitHub native scanning\n- **Action security**: Pin to commits, use trusted publishers\n\n## Debugging\n\nView DNS and firewall logs:\n\n```bash\n# DNS logs (no sudo required)\ncat /var/log/safer-runner/pre-dns.log      # Pre-hook DNS activity\ncat /var/log/safer-runner/main-dns.log     # Main action DNS activity\n\n# Network logs (no sudo required)\ncat /var/log/safer-runner/pre-iptables.log  # Pre-hook network activity\ncat /var/log/safer-runner/main-iptables.log # Main action network activity\n\n# Sudo logs (no sudo required)\ncat /var/log/safer-runner/pre-sudo.log      # Pre-hook sudo commands\ncat /var/log/safer-runner/main-sudo.log     # Main action sudo commands\n```\n\n## License\n\nThis action is provided as-is for defensive security purposes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fportswigger-tim%2Fsafer-runner-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fportswigger-tim%2Fsafer-runner-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fportswigger-tim%2Fsafer-runner-action/lists"}