Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 17:51:02 +08:00
commit ff1f4bd119
252 changed files with 72682 additions and 0 deletions

View File

@@ -0,0 +1,431 @@
# Checkov Suppression and Exception Handling Guide
Best practices for suppressing false positives and managing policy exceptions in Checkov.
## Suppression Methods
### Inline Suppression (Recommended)
#### Terraform
```hcl
# Single check suppression with justification
resource "aws_s3_bucket" "public_site" {
# checkov:skip=CKV_AWS_18:Public bucket for static website hosting
bucket = "my-public-website"
acl = "public-read"
}
# Multiple checks suppression
resource "aws_security_group" "legacy" {
# checkov:skip=CKV_AWS_23:Legacy app requires open access
# checkov:skip=CKV_AWS_24:IPv6 not supported by application
name = "legacy-sg"
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
```
#### Kubernetes
```yaml
# Annotation-based suppression
apiVersion: v1
kind: Pod
metadata:
name: legacy-app
annotations:
checkov.io/skip: CKV_K8S_16=Legacy application requires elevated privileges
spec:
containers:
- name: app
image: myapp:1.0
securityContext:
privileged: true
```
#### CloudFormation
```yaml
Resources:
PublicBucket:
Type: AWS::S3::Bucket
Metadata:
checkov:
skip:
- id: CKV_AWS_18
comment: "Public bucket for CDN origin"
Properties:
BucketName: my-public-bucket
PublicAccessBlockConfiguration:
BlockPublicAcls: false
```
### Configuration File Suppression
#### .checkov.yaml
```yaml
# .checkov.yaml (project root)
skip-check:
- CKV_AWS_8 # Ensure CloudWatch log groups encrypted
- CKV_K8S_43 # Image pull policy Always
# Skip specific paths
skip-path:
- .terraform/
- node_modules/
- vendor/
# Severity-based soft fail
soft-fail-on:
- LOW
- MEDIUM
# Hard fail on critical/high only
hard-fail-on:
- CRITICAL
- HIGH
```
### CLI-Based Suppression
```bash
# Skip specific checks
checkov -d ./terraform --skip-check CKV_AWS_8,CKV_AWS_21
# Skip entire frameworks
checkov -d ./infra --skip-framework secrets
# Skip paths
checkov -d ./terraform --skip-path .terraform/ --skip-path vendor/
```
## Suppression Governance
### Approval Workflow
```yaml
# .github/workflows/checkov-review.yml
name: Review Checkov Suppressions
on:
pull_request:
paths:
- '**.tf'
- '**.yaml'
- '**.yml'
jobs:
check-suppressions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check for New Suppressions
run: |
# Count suppressions in PR
SUPPRESSIONS=$(git diff origin/main | grep -c "checkov:skip" || true)
if [ "$SUPPRESSIONS" -gt 0 ]; then
echo "::warning::PR contains $SUPPRESSIONS new suppression(s)"
echo "Security team review required"
# Request review from security team
fi
```
### Suppression Documentation Template
```hcl
resource "aws_security_group" "example" {
# checkov:skip=CKV_AWS_23:TICKET-1234 - Business justification here
# Approved by: security-team@example.com
# Review date: 2024-01-15
# Expiration: 2024-06-15 (review quarterly)
#
# Compensating controls:
# - WAF rule blocks malicious traffic
# - Application-level authentication required
# - IP allow-listing at load balancer
# - 24/7 monitoring and alerting
name = "approved-exception"
# ... configuration
}
```
## Suppression Best Practices
### 1. Always Provide Justification
```hcl
# ❌ BAD: No justification
resource "aws_s3_bucket" "example" {
# checkov:skip=CKV_AWS_18
bucket = "my-bucket"
}
# ✅ GOOD: Clear business justification
resource "aws_s3_bucket" "example" {
# checkov:skip=CKV_AWS_18:Public bucket required for static website hosting.
# Content is non-sensitive marketing materials. CloudFront restricts direct access.
bucket = "marketing-website"
}
```
### 2. Document Compensating Controls
```hcl
resource "aws_security_group" "app" {
# checkov:skip=CKV_AWS_23:Office IP range access required for developers
#
# Compensating controls:
# 1. IP range limited to corporate /24 subnet (203.0.113.0/24)
# 2. MFA required for VPN access to corporate network
# 3. Additional application-level authentication
# 4. Session timeout of 15 minutes
# 5. All access logged to SIEM
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["203.0.113.0/24"]
}
}
```
### 3. Set Expiration Dates
```hcl
resource "aws_instance" "temp" {
# checkov:skip=CKV_AWS_8:Temporary instance for POC
# EXPIRES: 2024-03-31
# After expiration: Remove or apply encryption
ami = "ami-12345678"
instance_type = "t3.micro"
}
```
### 4. Use Granular Suppressions
```hcl
# ❌ BAD: Suppress entire file or directory
# checkov:skip=* (Don't do this!)
# ✅ GOOD: Suppress specific checks on specific resources
resource "aws_s3_bucket" "example" {
# checkov:skip=CKV_AWS_18:Specific reason for this resource only
bucket = "specific-bucket"
}
```
## Exception Categories
### Legitimate Exceptions
#### 1. Public Resources by Design
```hcl
resource "aws_s3_bucket" "website" {
# checkov:skip=CKV_AWS_18:Public bucket for static website
# checkov:skip=CKV_AWS_93:Public access required by design
# Content: Marketing materials (non-sensitive)
# Access: Read-only via CloudFront
bucket = "company-website"
}
```
#### 2. Legacy System Constraints
```yaml
apiVersion: v1
kind: Pod
metadata:
name: legacy-app
annotations:
checkov.io/skip: CKV_K8S_16=Legacy app built before containers, requires host access
# Migration plan: TICKET-5678
# Target date: Q2 2024
spec:
hostNetwork: true
containers:
- name: legacy
image: legacy-app:1.0
```
#### 3. Development/Testing Environments
```hcl
resource "aws_db_instance" "dev_db" {
# checkov:skip=CKV_AWS_17:Dev environment - backups not required
# checkov:skip=CKV_AWS_61:Dev environment - encryption overhead not needed
# Environment: Non-production only
# Data: Synthetic test data (no PII/PHI)
identifier = "dev-database"
backup_retention_period = 0
storage_encrypted = false
tags = {
Environment = "development"
}
}
```
### Temporary Exceptions
```hcl
resource "aws_rds_cluster" "temp_unencrypted" {
# checkov:skip=CKV_AWS_96:Temporary exception during migration
# TICKET: INFRA-1234
# EXPIRES: 2024-02-15
# PLAN: Enable encryption at rest in Phase 2 migration
# OWNER: platform-team@example.com
cluster_identifier = "migration-temp"
storage_encrypted = false
}
```
## Suppression Anti-Patterns
### ❌ Don't: Blanket Suppressions
```yaml
# BAD: Suppress all checks
skip-check:
- "*"
```
### ❌ Don't: Suppress Without Documentation
```hcl
# BAD: No explanation
resource "aws_s3_bucket" "example" {
# checkov:skip=CKV_AWS_18
bucket = "my-bucket"
}
```
### ❌ Don't: Permanent Suppressions for Production
```hcl
# BAD: Permanent suppression of critical security control
resource "aws_rds_cluster" "prod" {
# checkov:skip=CKV_AWS_96:Too expensive
# ^ This is unacceptable for production!
cluster_identifier = "production-db"
storage_encrypted = false
}
```
### ❌ Don't: Suppress High/Critical Without Review
```hcl
# DANGEROUS: Suppressing critical finding without security review
resource "aws_security_group" "prod" {
# checkov:skip=CKV_AWS_23:Need access from anywhere
# ^ Requires security team approval!
ingress {
cidr_blocks = ["0.0.0.0/0"]
}
}
```
## Monitoring Suppressions
### Track Suppression Metrics
```bash
# Count suppressions by type
grep -r "checkov:skip" ./terraform | \
sed 's/.*checkov:skip=\([^:]*\).*/\1/' | \
sort | uniq -c | sort -rn
# Find suppressions without justification
grep -r "checkov:skip=" ./terraform | \
grep -v "checkov:skip=.*:.*"
```
### Suppression Audit Report
```python
#!/usr/bin/env python3
"""Generate suppression audit report."""
import re
import sys
from pathlib import Path
from datetime import datetime
def find_suppressions(directory):
"""Find all Checkov suppressions."""
suppressions = []
for file_path in Path(directory).rglob('*.tf'):
with open(file_path) as f:
content = f.read()
# Find suppressions
matches = re.findall(
r'#\s*checkov:skip=([^:]+):(.*)',
content
)
for check_id, reason in matches:
suppressions.append({
'file': str(file_path),
'check_id': check_id.strip(),
'reason': reason.strip()
})
return suppressions
def generate_report(suppressions):
"""Generate markdown report."""
print("# Checkov Suppression Audit Report")
print(f"\nGenerated: {datetime.now().isoformat()}")
print(f"\nTotal Suppressions: {len(suppressions)}\n")
print("## Suppressions by Check")
check_counts = {}
for s in suppressions:
check_counts[s['check_id']] = check_counts.get(s['check_id'], 0) + 1
for check_id, count in sorted(check_counts.items(), key=lambda x: -x[1]):
print(f"- {check_id}: {count}")
print("\n## All Suppressions")
for s in suppressions:
print(f"\n### {s['file']}")
print(f"**Check:** {s['check_id']}")
print(f"**Reason:** {s['reason'] or '(no justification provided)'}")
if __name__ == '__main__':
directory = sys.argv[1] if len(sys.argv) > 1 else './terraform'
suppressions = find_suppressions(directory)
generate_report(suppressions)
```
## Quarterly Review Process
1. **Generate Suppression Report**: List all active suppressions
2. **Review Expirations**: Check for expired temporary suppressions
3. **Validate Justifications**: Ensure reasons still apply
4. **Verify Compensating Controls**: Confirm controls are still in place
5. **Update or Remove**: Update suppressions or fix underlying issues
## Additional Resources
- [Checkov Suppression Documentation](https://www.checkov.io/2.Basics/Suppressing%20and%20Skipping%20Policies.html)
- [Security Exception Management Best Practices](https://owasp.org/www-community/Security_Exception_Management)