Self-Hosted Runners
Run Controlinfra scans in your own infrastructure for enhanced security and compliance.
Overview
Self-hosted runners allow you to:
- Keep credentials local: AWS credentials never leave your infrastructure
- Meet compliance requirements: Run scans within your security perimeter
- Use IAM roles: No access keys needed—use instance profiles
- Control the environment: Your infrastructure, your rules
How It Works
┌─────────────────────────────────────────────────────────────┐
│ Your Infrastructure │
│ ┌─────────────────┐ ┌───────────────────────────────┐ │
│ │ Self-Hosted │ │ Your AWS Resources │ │
│ │ Runner │───▶│ (EC2, RDS, S3, etc.) │ │
│ │ │ └───────────────────────────────┘ │
│ │ • Terraform │ │
│ │ • AWS CLI │ ┌───────────────────────────────┐ │
│ │ • Git │───▶│ Terraform State (S3) │ │
│ └────────┬────────┘ └───────────────────────────────┘ │
│ │ │
└───────────┼─────────────────────────────────────────────────┘
│ Results only
▼
┌─────────────────────────────────────────────────────────────┐
│ Controlinfra Cloud │
│ • Receives scan results │
│ • Displays in dashboard │
│ • AI analysis (using your API key) │
└─────────────────────────────────────────────────────────────┘Prerequisites
- An EC2 instance (or similar compute) in your AWS account
- Network access to your AWS resources
- IAM role with read permissions
- Outbound internet access (HTTPS to Controlinfra)
Setup Guide
Step 1: Create IAM Role
Create an IAM role for the runner:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TerraformReadAccess",
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"s3:GetObject",
"s3:ListBucket",
"rds:Describe*",
"iam:Get*",
"iam:List*",
"lambda:Get*",
"lambda:List*"
],
"Resource": "*"
},
{
"Sid": "TerraformStateAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::your-terraform-state-bucket",
"arn:aws:s3:::your-terraform-state-bucket/*"
]
},
{
"Sid": "DynamoDBLocking",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/terraform-locks"
}
]
}Step 2: Launch EC2 Instance
Launch an EC2 instance with:
| Setting | Recommendation |
|---|---|
| AMI | Amazon Linux 2023 or Ubuntu 22.04 |
| Instance Type | t3.small or larger |
| IAM Role | The role created in Step 1 |
| Security Group | Outbound HTTPS (443) only |
| Storage | 20GB+ (for Terraform providers) |
Step 3: Install Dependencies
SSH into your instance and run:
#!/bin/bash
# Update system
sudo yum update -y # Amazon Linux
# sudo apt update && sudo apt upgrade -y # Ubuntu
# Install Git
sudo yum install -y git
# sudo apt install -y git
# Install Terraform
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
sudo yum install -y terraform
# Verify installations
git --version
terraform --version
aws --versionStep 4: Register Runner in Controlinfra
- Go to Settings → Runners
- Click "Add Runner"
- Copy the registration token
Step 5: Install Runner Agent
On your EC2 instance:
# Download runner agent
curl -fsSL https://controlinfra.com/runner/install.sh | bash
# Configure with your token
controlinfra-runner configure --token YOUR_TOKEN
# Start the runner
controlinfra-runner startStep 6: Verify Connection
In Controlinfra:
- Go to Settings → Runners
- Your runner should show as "Online"
Runner Configuration
Configuration File
The runner configuration is stored at ~/.controlinfra/runner.yml:
token: "your-runner-token"
name: "production-runner"
labels:
- production
- aws
- us-east-1
workDir: "/tmp/controlinfra"
logLevel: "info"Environment Variables
Configure the runner using environment variables:
export CONTROLINFRA_TOKEN="your-token"
export CONTROLINFRA_RUNNER_NAME="production-runner"
export CONTROLINFRA_WORK_DIR="/tmp/controlinfra"
export CONTROLINFRA_LOG_LEVEL="debug"Labels
Use labels to target specific runners:
labels:
- production
- aws
- us-east-1Then in repository settings, specify which runner to use:
Runner: production, awsRunning as a Service
systemd Service
Create /etc/systemd/system/controlinfra-runner.service:
[Unit]
Description=Controlinfra Runner
After=network.target
[Service]
Type=simple
User=controlinfra
ExecStart=/usr/local/bin/controlinfra-runner start
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable controlinfra-runner
sudo systemctl start controlinfra-runnerDocker
Run the runner in Docker:
docker run -d \
--name controlinfra-runner \
--restart always \
-e CONTROLINFRA_TOKEN=your-token \
-v /var/run/docker.sock:/var/run/docker.sock \
controlinfra/runner:latestSecurity Best Practices
1. Network Isolation
- Place runner in a private subnet
- Use NAT Gateway for outbound access
- Restrict inbound access completely
2. IAM Least Privilege
- Only grant read permissions needed
- Use resource-level permissions where possible
- Regularly audit IAM policies
3. Secrets Management
# Don't store secrets in environment variables
# Use AWS Secrets Manager or Parameter Store
aws secretsmanager get-secret-value --secret-id controlinfra/token4. Logging and Monitoring
# Enable CloudWatch logging
sudo yum install -y amazon-cloudwatch-agent5. Regular Updates
# Update runner agent regularly
controlinfra-runner updateMultiple Runners
For high availability or multi-environment setups:
Environment-Specific Runners
production-runner (labels: production, us-east-1)
└── Scans production repositories
staging-runner (labels: staging, us-east-1)
└── Scans staging repositories
eu-runner (labels: production, eu-west-1)
└── Scans EU region resourcesLoad Balancing
Multiple runners with the same labels will automatically load balance:
runner-1 (labels: production)
runner-2 (labels: production)
runner-3 (labels: production)Assigning Runners to Repositories
In Repository Settings
- Navigate to your repository
- Go to Settings → Runner
- Select runner type:
- Cloud Runner: Use Controlinfra's hosted runner
- Self-Hosted: Use your own runner
- If self-hosted, select the runner or specify labels
Using Labels
Repository: my-terraform-repo
Runner Type: Self-Hosted
Runner Labels: production, us-east-1Troubleshooting
Runner Shows "Offline"
Check the runner service is running:
bashsystemctl status controlinfra-runnerCheck network connectivity:
bashcurl -v https://api.controlinfra.com/healthVerify the token:
bashcontrolinfra-runner verify
Scans Failing on Runner
Check runner logs:
bashjournalctl -u controlinfra-runner -fVerify Terraform is installed:
bashterraform versionCheck IAM permissions:
bashaws sts get-caller-identity
"Permission Denied" Errors
- Verify IAM role is attached to the instance
- Check IAM policy has required permissions
- Ensure the work directory is writable
Terraform Init Failures
- Check network access to Terraform registries
- Verify provider credentials
- Check disk space for provider downloads
Maintenance
Updating the Runner
# Stop the runner
sudo systemctl stop controlinfra-runner
# Update
controlinfra-runner update
# Start the runner
sudo systemctl start controlinfra-runnerRotating Tokens
- Generate new token in Controlinfra Settings
- Update runner configuration
- Restart the runner
- Revoke old token
Log Rotation
Configure log rotation in /etc/logrotate.d/controlinfra:
/var/log/controlinfra/*.log {
daily
rotate 7
compress
missingok
notifempty
}Next Steps
- Configure AWS Credentials - IAM setup details
- Repository Configuration - Assign runners to repos
- Run Your First Scan - Test your runner