"If your security policy isn't in git, it doesn't exist."
Auditctl transforms static security policy documents into living, breathing compliance code that runs automatically against your AWS environment. It bridges the gap between security PDFs that nobody reads and engineering systems that actually get built. Write your compliance rules once in simple YAML, run them against any AWS account, and prove compliance to auditors with machine-generated evidence.
This animated demo shows a complete compliance scan that identifies misconfigured S3 buckets, unencrypted EBS volumes, and overly permissive IAM policies β all in under 10 seconds.
- The Problem
- Why I Built This
- Before vs After
- How It Works
- Policy File Example
- Supported Compliance Frameworks
- Security First
- CI/CD Integration
- Quick Start
- Sample Report Output
- Performance & Scale
- Roadmap
- About the Author
Every company has security policies. They're stored as Word documents and PDFs living in SharePoint folders that nobody ever opens. When audit time comes, engineers get asked to manually check 100+ AWS resources across dozens of accounts. They screenshot console outputs, paste them into spreadsheets, and pray they didn't miss anything.
The real risk is that manual compliance checks are fundamentally broken. They're slow β taking weeks to complete. They're error-prone β humans miss things, especially at 2 AM when you're trying to close an audit finding before the long weekend. They can't run automatically, so drift goes undetected for months. And there's no audit trail β just a folder of screenshots that proves nothing about when or why a resource was configured a certain way.
This is where auditctl comes in. One line of code transforms your policy PDFs into executable rules that run on every git push.
I built auditctl after spending 15 years in payment infrastructure and security engineering, watching companies burn millions on compliance theater. I've been the engineer scrambling to document why an S3 bucket was public in production. I've been the consultant billing 200 hours to manually check IAM policies that nobody ever reads. I've seen the look on a CISO's face when they realize their "comprehensive" security program amounts to a folder of PDFs nobody has opened since 2019.
The technical insight that drove the architecture was simple: compliance rules are just code. They have conditions (if encryption disabled), thresholds (severity levels), and evidence (AWS API responses). The industry has spent decades treating security policies as legal documents when they should be treating them as unit tests. Every compliance requirement can be expressed as a check that either passes or fails against infrastructure state.
I deliberately left out complexity. No database. No agent to install on servers. No cloud-specific backend services. Just a single binary that reads YAML, queries AWS, and writes reports. If you can run a Docker container, you can run auditctl. The philosophy is: the simpler the tool, the more it'll actually get used.
If Fortune 500 companies adopted this tomorrow, I'd build a Terraform pre-commit hook that blocks infrastructure provisioning if it violates your compliance policies. That's the future β shifting compliance left so violations die in code review, not production.
| Before auditctl | After auditctl | |
|---|---|---|
| Compliance check time | 2β3 weeks manual review | < 10 seconds automated scan |
| Audit evidence | Screenshots, emails, Word docs | Machine-generated JSON + HTML report |
| Policy storage | SharePoint PDF nobody reads | Git-versioned YAML, PR-reviewed |
| Drift detection | Discovered during annual audit | Caught on every git push |
| Engineer time per audit | ~80 hours | ~0 hours (runs in CI/CD) |
| False negatives | Common (human error) | Eliminated by deterministic checks |
| Frameworks supported | Checked manually per framework | CIS, SOC2, PCI-DSS, NIST 800-53 |
| Cost of a missed finding | Potential breach + fine | Blocked PR + Slack alert |
This is the difference between compliance as a checkbox and compliance as a system.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β auditctl scan β
ββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
ββββββββββββββΌβββββββββββββ
β YAML Policy Loader β
β (validates schema + β
β framework mappings) β
ββββββββββββββ¬βββββββββββββ
β
βββββββββββββββββββΌβββββββββββββββββββ
β Compliance Engine β
β (evaluates rules against live AWS) β
ββββ¬βββββββββββ¬βββββββββββββ¬βββββββββββ
β β β
ββββββββΌβββ ββββββΌβββββ βββββΌββββββ
β EC2 β β S3 β β IAM β ... (RDS, SGs)
β Checks β β Checks β β Checks β
ββββββββ¬βββ ββββββ¬βββββ βββββ¬ββββββ
ββββββββββββ΄βββββββββββββ
β
βββββββββββββΌββββββββββββ
β Reporter β
β Terminal β JSON β HTML β
βββββββββββββββββββββββββ
Here's what happens when you run auditctl scan:
-
Load Policies β auditctl reads your YAML policy files and validates they conform to the schema. Each rule defines what to check, which AWS resources, and what framework controls it maps to.
-
Query AWS β Using your configured AWS credentials (via IAM role, environment variables, or shared credentials), auditctl makes read-only API calls to enumerate your resources: EC2 volumes, S3 buckets, IAM policies, RDS instances, Security Groups.
-
Evaluate Rules β Each policy rule runs against the relevant resources. A rule might check "is this EBS volume encrypted?" or "does this S3 bucket have public access blocked?" Results are deterministic β no human judgment required.
-
Aggregate Results β All findings are collected, grouped by severity (CRITICAL, HIGH, MEDIUM, LOW), and enriched with resource identifiers and framework mappings.
-
Generate Reports β Output is written in your choice of format: human-readable terminal output, machine-parseable JSON for automation, or an HTML report suitable for auditors.
-
Exit Code β auditctl exits with code 1 if any CRITICAL or HIGH findings exist, enabling CI/CD pipeline blocking.
Policy files use simple YAML with inline comments so your security team can actually read them:
# EC2-001: Every hard drive in AWS must be encrypted.
# Why: If AWS ever deprecates hardware, encrypted disks
# can't be read. Required by PCI-DSS 3.4 and SOC2 CC6.1.
- id: "EC2-001"
name: "EBS Volume Encryption"
severity: CRITICAL
resource: ec2
check: "ebs-encrypted"
frameworks:
cis: "2.2.1"
soc2: "CC6.1"
pci: "3.4"
nist: "SC-28"
description: |
All EBS volumes must have encryption enabled.
Unencrypted volumes expose data at rest to unauthorized access.
# EC2-002: Every EC2 instance must require IMDSv2.
# Why: IMDSv1 is vulnerable to SSRF attacks. Required by CIS 5.6.
- id: "EC2-002"
name: "IMDSv2 Required"
severity: HIGH
resource: ec2
check: "imdsv2-required"
frameworks:
cis: "5.6"
soc2: "CC6.6"
pci: "6.2"
nist: "SI-2"
description: |
EC2 instances must use IMDSv2 which requires a session token.
This mitigates Server-Side Request Forgery (SSRF) attacks.
# S3-001: No S3 bucket should be publicly accessible.
# Why: Data breaches happen. Nobody intentionally makes buckets public,
# but misconfigurations happen. Required by CIS 2.1.5.
- id: "S3-001"
name: "Block Public Access"
severity: CRITICAL
resource: s3
check: "public-access-blocked"
frameworks:
cis: "2.1.5"
soc2: "CC6.1"
pci: "1.3"
nist: "AC-3"
description: |
S3 buckets must have public access blocked at the bucket
and account level. This is the #1 cause of AWS data breaches.| Rule ID | Resource | Check | Severity | CIS | SOC2 | PCI-DSS | NIST |
|---|---|---|---|---|---|---|---|
| EC2-001 | EC2 Volume | EBS encryption enabled | π΄ CRITICAL | 2.2.1 | CC6.1 | 3.4 | SC-28 |
| EC2-002 | EC2 Instance | IMDSv2 required | π HIGH | 5.6 | CC6.6 | 6.2 | SI-2 |
| S3-001 | S3 Bucket | Block public access | π΄ CRITICAL | 2.1.5 | CC6.1 | 1.3 | AC-3 |
| S3-002 | S3 Bucket | Versioning enabled | π‘ MEDIUM | 2.1.3 | A1.2 | 10.5 | CP-9 |
| IAM-001 | IAM Root | No root access keys | π΄ CRITICAL | 1.4 | CC6.2 | 7.1 | AC-2 |
| IAM-002 | IAM Account | MFA enabled on root | π΄ CRITICAL | 1.5 | CC6.1 | 8.3 | IA-2 |
| IAM-003 | IAM Policy | No wildcards in policies | π HIGH | 1.16 | CC6.3 | 7.1.2 | AC-6 |
| RDS-001 | RDS Instance | Encryption at rest | π΄ CRITICAL | 2.3.1 | CC6.1 | 3.4 | SC-28 |
| RDS-002 | RDS Instance | No public accessibility | π HIGH | 2.3.3 | CC6.6 | 1.3 | SC-7 |
| SG-001 | Security Group | No 0.0.0.0/0 on port 22 | π΄ CRITICAL | 5.2 | CC6.6 | 1.2 | SC-7 |
Footnote: Framework mappings are based on CIS AWS Foundations Benchmark v2.0, SOC2 Trust Services Criteria, PCI-DSS v4.0, and NIST SP 800-53 Rev 5.
auditctl never writes to your AWS environment. It only makes Describe*, List*, and Get* API calls. Your infrastructure stays untouched. The tool can't accidentally open a port, disable encryption, or delete a resource β it physically cannot do those things.
Run auditctl with the narrowest possible IAM permissions. Here's everything it needs:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeVolumes",
"ec2:DescribeInstances",
"ec2:DescribeSecurityGroups",
"s3:ListAllMyBuckets",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketVersioning",
"iam:GetAccountSummary",
"iam:GetUser",
"iam:ListUsers",
"iam:ListPolicies",
"rds:DescribeDBInstances"
],
"Resource": "*"
}
]
}auditctl uses the standard AWS credential chain β environment variables, shared credentials file, or IAM role. Credentials are never logged, stored, or persisted. The tool runs, queries AWS, prints results, and exits. That's it.
Every JSON report includes a SHA-256 checksum of the findings, a timestamp, the AWS account ID, and the region scanned. This creates a tamper-evident record you can archive for auditors. If anyone modifies the report after generation, the checksum won't match.
Add auditctl to your GitHub Actions pipeline to run compliance checks on every push:
name: Compliance Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
audit:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/auditctl-scanner
aws-region: us-east-1
- name: Run auditctl scan
run: |
docker run --rm \
-v $(pwd)/policies:/policies \
-v $(pwd)/reports:/reports \
-e AWS_REGION=us-east-1 \
webber/auditctl:latest scan \
--policies /policies \
--region us-east-1 \
--format all
- name: Upload audit report
uses: actions/upload-artifact@v4
if: always()
with:
name: audit-report
path: reports/*
- name: Post to Slack on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
fields: repo,message,commit,author
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}The pipeline fails if any CRITICAL or HIGH findings are present, blocking merges until compliance is restored. The HTML report artifact serves as audit evidence.
# 1. Clone the repository
git clone https://github.com/webber/auditctl.git && cd auditctl
# 2. Build the binary
make build
# 3. Validate your policy files (no AWS credentials needed)
./auditctl validate --policies ./policies
# 4. Run a compliance scan against your AWS account
./auditctl scan --policies ./policies --region us-east-1 --format all
# 5. View your HTML audit report
open ./reports/audit-latest.htmlOr run with Docker:
docker run --rm \
-v $(pwd)/policies:/policies \
-v $(pwd)/reports:/reports \
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
-e AWS_REGION=us-east-1 \
webber/auditctl:latest scan \
--policies /policies \
--region us-east-1 \
--format allβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β auditctl Compliance Report β
β Account: 123456789012 β Region: us-east-1 β
β Scanned: 2026-03-20T18:49:00Z β
βββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββ¬βββββββββββββββ€
β Rule ID β Check β Status β Severity β
βββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββΌβββββββββββββββ€
β EC2-001 β EBS Encryption β β
PASS β CRITICAL β
β EC2-002 β IMDSv2 Required β β FAIL β HIGH β
β S3-001 β Block Public Access β β FAIL β CRITICAL β
β S3-002 β Versioning Enabled β β
PASS β MEDIUM β
β IAM-001 β No Root Keys β β
PASS β CRITICAL β
β IAM-002 β MFA on Root β β
PASS β CRITICAL β
β IAM-003 β No Wildcard Policies β β FAIL β HIGH β
β RDS-001 β Encryption at Rest β β
PASS β CRITICAL β
β RDS-002 β No Public Access β β
PASS β HIGH β
β SG-001 β No SSH from Internet β β FAIL β CRITICAL β
βββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββ΄βββββββββββββββ€
β Summary: 6 passed, 4 failed, 2 CRITICAL, 2 HIGH β
β Duration: 8.3 seconds β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Failed Findings:
β EC2-002: i-0abc123def456789 (web-server-prod)
β IMDSv2 not required, vulnerable to SSRF
β S3-001: audit-logs-2026
β Public access not blocked
β IAM-003: Policy arn:aws:iam::123456789012:policy/AdminAccess
β Contains wildcard action: "iam:*"
β SG-001: sg-0abc123def456789 (prod-sg)
β Allows 0.0.0.0/0 on port 22
| Environment Size | Resources Scanned | Scan Duration | Report Size |
|---|---|---|---|
| Small (startup) | ~50 resources | ~3 seconds | ~12 KB |
| Medium (scale-up) | ~500 resources | ~18 seconds | ~85 KB |
| Large (enterprise) | ~2,000 resources | ~60 seconds | ~340 KB |
Scans run concurrently using Go goroutines β each resource type is checked in parallel. For most organizations, a complete compliance scan finishes in under a minute. The tool is designed to run as part of CI/CD pipelines without adding meaningful latency to developer workflows.
-
v1.0 β Current
- EC2, S3, IAM, RDS, Security Group checks
- YAML policy files with framework mappings
- Terminal, JSON, and HTML output formats
- GitHub Actions integration
- Docker container support
-
v1.1 β In Progress
- Slack and PagerDuty webhooks for alerts
- AWS Organizations multi-account scanning
- OPA Rego policy export
-
v2.0 β Planned
- Terraform pre-commit validation
- React-based compliance dashboard
- SOC2 Type II evidence auto-generation
- Azure and GCP support
I'm a senior infrastructure engineer who spent 15 years building payment systems at scale. I've handled PCI-DSS compliance for transaction processing handling millions of cards, architected SOC2 Type II programs from scratch, and led teams building distributed systems that never went down. I love Go for its simplicity and correctness, and I care deeply about bridging the gap between security theory and engineering reality.
GRC (Governance, Risk, and Compliance) is ripe for the same treatment DevOps gave to infrastructure. Your security policies should be code β testable, versionable, and automated. That's what auditctl is about.
Built with β€οΈ and a healthy distrust of unsecured S3 buckets.
Star β this repo if it saved you from an audit nightmare.
