Container Scanning
Overview
The config-driven matrix scanner allows you to:
- Define multiple containers in a single configuration file
- Scan from public or private registries with authentication
- Use environment variable expansion for dynamic values
- Run multiple scanners against each container
- Generate comprehensive security reports
Configuration File
Container configurations can be written in YAML, JSON, or JavaScript.
Schema Location
# yaml-language-server: $schema=https://raw.githubusercontent.com/huntridge-labs/argus/0.6.7/.github/actions/parse-container-config/schemas/container-config.schema.json
Basic Structure
containers:
# Simple string required for use with dependabot
- image: ghcr.io/myorg/myapp:latest@sha256:abc123...
registry:
host: ghcr.io
username: ${GITHUB_TRIGGERING_ACTOR}
auth_secret: GITHUB_TOKEN
scanners:
- trivy-container
- grype
fail_on_severity: high
# Upload results to GitHub Security tab
enable_code_security: false
Registry Configuration
Registry Authentication
The registry object consolidates all registry-related configuration:
registry:
host: ghcr.io # Registry hostname
username: ${GITHUB_TRIGGERING_ACTOR} # Username (supports env vars)
auth_secret: GITHUB_TOKEN # Name of GitHub secret with auth token
Registry Patterns
GitHub Container Registry:
registry:
host: ghcr.io
username: ${GITHUB_TRIGGERING_ACTOR}
auth_secret: GITHUB_TOKEN
Docker Hub:
registry:
host: docker.io
username: myuser
auth_secret: DOCKERHUB_TOKEN
AWS ECR:
registry:
host: 123456789.dkr.ecr.us-east-1.amazonaws.com
username: AWS
auth_secret: ECR_TOKEN
Azure Container Registry:
registry:
host: myregistry.azurecr.io
username: ${ACR_USERNAME}
auth_secret: ACR_PASSWORD
No Authentication (public registries):
image: nginx:latest
Image Configuration
Structured Format
Provides fine-grained control:
image:
repository: myorg # Optional repository/namespace
name: myapp # Required: image name
tag: v1.2.3 # Optional: defaults to 'latest'
digest: sha256:abc... # Optional: digest pinning
Resulting reference: myorg/myapp:v1.2.3
String Format
Simple inline reference:
image: "nginx:latest"
image: "myorg/myapp:v1.2.3"
image: "nginx:latest@sha256:abc..."
Digest Pinning
Pin to exact image content:
# Structured format
image:
name: nginx
tag: latest
digest: sha256:abc123...
# String format
image: "nginx:latest@sha256:abc123..."
Environment Variables
Use ${VAR_NAME} syntax for dynamic values:
registry:
username: ${GITHUB_TRIGGERING_ACTOR}
host: ${CUSTOM_REGISTRY_HOST}
image:
repository: ${ORG_NAME}
tag: ${IMAGE_TAG}
Available in GitHub Actions:
- GITHUB_TRIGGERING_ACTOR - User who triggered the workflow
- GITHUB_REPOSITORY_OWNER - Repository owner
- GITHUB_REF_NAME - Branch or tag name
- Any custom environment variables
Complete Examples
Multiple Containers with Different Registries
# container-config.yml
containers:
- name: frontend
registry:
host: ghcr.io
username: ${GITHUB_TRIGGERING_ACTOR}
auth_secret: GITHUB_TOKEN
image:
repository: myorg
name: frontend
tag: ${GITHUB_REF_NAME}
scanners:
- trivy-container
- grype
- name: backend
registry:
host: docker.io
username: dockeruser
auth_secret: DOCKERHUB_TOKEN
image:
name: myorg/backend
tag: latest
scanners:
- trivy-container
- sbom
- name: nginx
# Public image, no authentication
image: nginx:alpine
scanners:
- trivy-container
JSON Format
{
"containers": [
{
"name": "api",
"registry": {
"host": "ghcr.io",
"username": "${GITHUB_TRIGGERING_ACTOR}",
"auth_secret": "GITHUB_TOKEN"
},
"image": {
"repository": "myorg",
"name": "api",
"tag": "v2.1.0",
"digest": "sha256:abc123..."
},
"scanners": ["trivy-container", "grype", "sbom"]
}
]
}
JavaScript Format
module.exports = {
containers: [
{
name: 'webapp',
registry: {
host: 'ghcr.io',
username: process.env.GITHUB_TRIGGERING_ACTOR,
auth_secret: 'GITHUB_TOKEN'
},
image: {
repository: 'myorg',
name: 'webapp',
tag: process.env.GITHUB_REF_NAME || 'latest'
},
scanners: ['trivy-container', 'grype']
}
]
};
Workflow Usage
Basic Integration
name: Container Security Scan
on:
push:
paths:
- 'container-config.yml'
workflow_dispatch:
jobs:
scan:
uses: huntridge-labs/argus/.github/workflows/container-scan-from-config.yml@0.6.7
with:
config_file: container-config.yml
enable_code_security: true
fail_on_severity: high
secrets: inherit
Advanced Configuration
jobs:
scan:
uses: huntridge-labs/argus/.github/workflows/container-scan-from-config.yml@0.6.7
with:
config_file: .github/security/containers.yml
enable_code_security: true
post_pr_comment: true
fail_on_severity: critical
secrets: inherit # Required for registry authentication
Matrix Execution
The workflow generates a matrix from your configuration:
- Parser validates configuration against JSON schema
- Generates matrix with one job per container
- Each job:
- Authenticates to registry (if configured)
- Pulls the specified image
- Runs configured scanners sequentially
- Uploads results to GitHub Security tab
Example matrix output:
{
"container": [
{
"name": "frontend",
"image_ref": "ghcr.io/myorg/frontend:main",
"registry_username": "user",
"registry_auth_secret": "GITHUB_TOKEN",
"scanners": "trivy-container,grype"
},
{
"name": "backend",
"image_ref": "docker.io/myorg/backend:latest",
"registry_username": "dockeruser",
"registry_auth_secret": "DOCKERHUB_TOKEN",
"scanners": "trivy-container,sbom"
}
]
}
Automated Updates with Dependabot
Limitation
Dependabot only supports simple string format for image references:
# ✅ Works with Dependabot
containers:
- name: nginx
image: "nginx:1.25.0"
# ❌ Does NOT work with Dependabot
containers:
- name: nginx
image:
name: nginx
tag: 1.25.0
Example Dependabot Configuration
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: docker
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
What Dependabot detects:
- String format: image: "nginx:1.25.0"
- Digest pinning: image: "nginx:latest@sha256:abc..."
What Dependabot cannot detect: - Structured object format - Images split across multiple properties - Environment variable references
See dependabot.example.yml for a complete example.
Troubleshooting
Authentication Failures
Problem: Error: unauthorized: authentication required
Solution: Verify:
- Registry host is correct
- Secret name matches workflow secrets
- Secret contains valid token with read permissions
- secrets: inherit is included in workflow
Invalid Configuration
Problem: Parser fails with validation error
Solution: - Validate against JSON schema - Check YAML syntax (indentation, quotes) - Verify required fields are present - Test with simplified config first
Image Not Found
Problem: Error: image not found
Solution:
- Verify image reference is correct
- Check registry hostname
- Ensure image exists and is accessible
- Test docker pull manually with same credentials
Best Practices
- Use digest pinning for production images to ensure immutability
- Store registry credentials in GitHub secrets, never in config files
- Use environment variables for dynamic values like branch names
- Validate config files in CI before running scans
- Start with public images when testing, then add authentication
- Use string format if you need Dependabot support
- Group related images in same config file for easier management