Credential Protection
Block Metadata Endpoints
Cloud metadata endpoints expose temporary credentials to any process running on the runner. Block 169.254.169.254 with firewall rules and use IMDSv2 when metadata access is unavoidable.
Block Cloud Metadata Endpoints¶
Audit Logging¶
Comprehensive logging to detect and investigate runner compromise.
auditd Configuration¶
Configure Linux audit daemon to log runner security events.
# /etc/audit/rules.d/99-runner-security.rules
# Audit rules for GitHub Actions runner
# Monitor runner binary execution
-w /opt/github-runner/bin/Runner.Listener -p x -k runner_exec
# Monitor runner configuration changes
-w /opt/github-runner/.runner -p wa -k runner_config
-w /opt/github-runner/.credentials -p wa -k runner_creds
# Monitor runner workspace
-w /opt/github-runner/_work -p wa -k runner_workspace
# Monitor privileged operations
-a always,exit -F arch=b64 -S execve -F euid=0 -k privileged_exec
-a always,exit -F arch=b32 -S execve -F euid=0 -k privileged_exec
# Monitor network connections
-a always,exit -F arch=b64 -S connect -k network_connect
-a always,exit -F arch=b32 -S connect -k network_connect
# Monitor file deletions (cover-up attempts)
-a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -k file_deletion
-a always,exit -F arch=b32 -S unlink -S unlinkat -S rename -S renameat -k file_deletion
# Monitor changes to authentication files
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k identity
# Make configuration immutable (prevent tampering)
-e 2
# Apply audit rules
auditctl -R /etc/audit/rules.d/99-runner-security.rules
# Restart auditd
systemctl restart auditd
# Verify rules loaded
auditctl -l
Centralized Log Aggregation¶
Send logs to centralized system for analysis and retention.
#!/bin/bash
# Configure rsyslog to forward runner logs
cat > /etc/rsyslog.d/99-runner-logs.conf <<'EOF'
# Forward runner logs to centralized logging
# Runner service logs
:programname, isequal, "Runner.Listener" @@logs.example.com:514
# Audit logs
:programname, isequal, "audispd" @@logs.example.com:514
# UFW firewall logs
:msg, contains, "[UFW" @@logs.example.com:514
EOF
systemctl restart rsyslog
Workflow Job Logging¶
Capture metadata for every job executed on the runner.
#!/bin/bash
# /opt/github-runner/log-job-start.sh
# Log job execution metadata
set -euo pipefail
LOG_FILE="/var/log/github-runner-jobs.log"
log_entry() {
echo "$(date -Iseconds) | $*" >> "$LOG_FILE"
}
log_entry "JOB_START | Repo: ${GITHUB_REPOSITORY:-unknown} | Run: ${GITHUB_RUN_ID:-unknown} | SHA: ${GITHUB_SHA:-unknown} | Actor: ${GITHUB_ACTOR:-unknown}"
Call from workflow:
jobs:
deploy:
runs-on: self-hosted
steps:
- name: Log job start
run: /opt/github-runner/log-job-start.sh
Anomaly Detection¶
Monitor logs for suspicious patterns indicating compromise.
#!/bin/bash
# /opt/github-runner/detect-anomalies.sh
# Detect suspicious runner behavior
set -euo pipefail
ALERT_EMAIL="security-team@example.com"
# Check for metadata endpoint access attempts
if ausearch -k network_connect | grep -q "169.254.169.254"; then
echo "ALERT: Metadata endpoint access detected" | mail -s "Runner Security Alert" "$ALERT_EMAIL"
fi
# Check for privileged command execution
if ausearch -k privileged_exec | grep -q "SUCCESS"; then
echo "ALERT: Privileged command executed by runner" | mail -s "Runner Security Alert" "$ALERT_EMAIL"
fi
# Check for authentication file modifications
if ausearch -k identity | grep -q "SYSCALL"; then
echo "ALERT: Authentication files modified" | mail -s "Runner Security Alert" "$ALERT_EMAIL"
fi
# Check for unexpected network destinations
ALLOWED_DESTINATIONS=(
"140.82.112.0/20"
"143.55.64.0/20"
"185.199.108.0/22"
"192.30.252.0/22"
)
# Parse network connections and alert on unexpected destinations
# Implementation depends on log format
Run as cron job:
Quick Reference: Hardening Checklist¶
Use this checklist when deploying or auditing runner security.
OS Hardening¶
- [ ] Minimal OS installation (unnecessary packages removed)
- [ ] Automatic security updates enabled
- [ ] CIS benchmarks applied
- [ ] Dedicated non-root runner user created
- [ ] No sudo access for runner user
- [ ] Restrictive filesystem mount options (noexec, nosuid, nodev)
- [ ] AppArmor profile configured and enforced
Network Hardening¶
- [ ] Deny-by-default firewall rules
- [ ] Outbound allow-list for GitHub, package registries
- [ ] Cloud metadata endpoints blocked
- [ ] Private network ranges denied (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
- [ ] Network namespace isolation (optional, advanced)
- [ ] IMDSv2 enforced if metadata access required
Credential Hardening¶
- [ ] OIDC federation configured (no stored secrets)
- [ ] Environment variable injection for required secrets
- [ ] No secrets written to disk
- [ ] Secrets masking verified
- [ ] Short-lived credentials only (max 1 hour)
Audit Hardening¶
- [ ] auditd installed and configured
- [ ] Runner execution logged
- [ ] Network connections logged
- [ ] Privileged operations logged
- [ ] Centralized log aggregation configured
- [ ] Log retention policy enforced (minimum 90 days)
- [ ] Anomaly detection monitoring active
- [ ] Security alerts configured
Next Steps¶
- Ephemeral Runners: Deploy VM and container-based ephemeral runners for maximum isolation
- Runner Groups: Organize runners by trust level and security requirements
- Runner Security Overview: Review threat model and deployment strategies
Related Documentation¶
- OIDC Federation: Secretless authentication patterns
- Secret Management: Handling credentials securely
- Workflow Triggers: Understanding which events execute on runners