OSCR operates in a threat environment where:
- Workflows are untrusted code - CI jobs can execute arbitrary commands
- Secrets are high-value targets - PATs and credentials must be protected
- The host is the trust boundary - Container escape = full compromise
| Asset | Impact if Compromised |
|---|---|
| Personal Access Tokens | Full repository/organization access |
| Job secrets | Credential theft, lateral movement |
| Host system | Complete infrastructure compromise |
| Other runners | Cross-job contamination |
| Actor | Capability | Mitigation |
|---|---|---|
| Malicious PR author | Arbitrary code in workflow | Block fork PRs |
| Compromised dependency | Supply chain attack | Ephemeral workspace |
| Insider threat | Direct access to runner | Non-root execution, audit logs |
All runner processes execute as non-root users:
- GitHub:
runneruser - Azure DevOps:
agentuser
This limits the impact of container escape vulnerabilities.
Verification:
# In a job step
whoami # Should output: runner (or agent)
id # Should show non-root UIDSelf-hosted runners must not execute untrusted fork PRs by default.
Add this condition to your workflow:
jobs:
build:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, linux]Or configure at the repository level:
- Go to Settings > Actions > General
- Under "Fork pull request workflows", select "Require approval for all outside collaborators"
- Configure branch policies to require PR approval
- Use environment approvals for sensitive pipelines
- Restrict pool access to trusted projects
Each job executes in a clean workspace:
- Previous job artifacts are not accessible
- No state persists between runs (unless explicitly configured)
- Workspace is wiped on job completion
This prevents:
- Cross-job data leakage
- Persistent malware
- Credential caching attacks
OSCR does not implement secret management. Secrets are:
- Defined in GitHub/Azure DevOps
- Injected by the official runner/agent
- Masked in logs by the provider
Do not:
- Store secrets in
.envfiles (except PATs for registration) - Pass secrets via custom environment variables
- Log secret values
Default configuration uses a bridge network:
- Containers cannot access host-only services
- Inter-container traffic is isolated
- External network access is permitted (for package downloads, etc.)
For stricter isolation, consider:
- Firewall rules on the host
- Network policies (if using Kubernetes)
- Proxy configuration for egress control
Rotate Personal Access Tokens regularly:
- Create new PAT with required scopes
- Update
.envfile - Restart runner:
./select-provider.sh stop && ./select-provider.sh start - Revoke old PAT
The Docker socket mount (/var/run/docker.sock) is required for Docker-in-Docker workflows but grants significant privileges.
Risks:
- Container can create privileged containers
- Container can mount host filesystem
- Effectively equivalent to root on host
Mitigations:
- Use rootless Docker if possible
- Consider Sysbox or similar runtimes
- Restrict which repositories can use self-hosted runners
Prevent resource exhaustion attacks:
# In docker-compose.yml
services:
github-runner:
deploy:
resources:
limits:
cpus: '4'
memory: 8GEnable audit logging on the host:
# Example: auditd rules for Docker
-w /var/run/docker.sock -k docker
-w /etc/docker -k dockerMonitor runner logs for suspicious activity:
./select-provider.sh logs | grep -E "(error|fail|denied)"If building images locally, verify the Dockerfile hasn't been tampered with:
# Check for unexpected changes
git diff providers/github/Dockerfile
git diff providers/azure-devops/DockerfileUse signed images when available:
docker pull oddessentials/oscr-github:latest
docker trust inspect oddessentials/oscr-github:latestIf you suspect a runner has been compromised:
- Stop immediately:
./select-provider.sh stop - Revoke PATs: Invalidate all tokens used by the runner
- Rotate secrets: Regenerate any secrets the runner had access to
- Review logs: Check for unauthorized job executions
- Rebuild: Use fresh container images
If secrets are exposed in logs or artifacts:
- Rotate immediately: Generate new credentials
- Audit access: Review who/what accessed the exposed secret
- Update workflows: Fix the leak source
- Notify: Inform affected parties per your incident response plan
These rules are non-negotiable (see INVARIANTS.md):
- Runner containers run as non-root
- Fork PRs are blocked by default
- Secrets are injected via provider-native mechanisms only
- Workspace is ephemeral
Any change violating these invariants requires explicit architectural review.
For regulated environments:
| Requirement | OSCR Approach |
|---|---|
| Audit trail | Use provider-native audit logs |
| Access control | Configure at provider level |
| Encryption at rest | Use encrypted Docker volumes |
| Encryption in transit | Provider uses TLS |
| Data residency | Runner executes on your infrastructure |
OSCR does not implement its own compliance controls. Use provider features and host-level security for compliance requirements.