Feuille de chaleur Terrascan
Aperçu général
Terrascan est un analyseur de code statique pour Infrastructure as Code (IaC) qui aide à détecter la conformité et les violations de sécurité dans les infrastructures natives du cloud. Il prend en charge plusieurs fournisseurs IaC dont Terraform, Kubernetes, Helm, Kustomize, Dockerfiles et des modèles de formation de cloud. Terrascan fournit plus de 500 polices et soutient l'OPA, le langage Rego et JSON/YAML pour la rédaction de politiques personnalisées.
C'est-à-dire Caractéristiques principales: Multi-Ia C soutien, plus de 500 politiques intégrées, création de politiques personnalisées, intégration de l'IC/DC, cadres de conformité (CIS, NIST, norme de sécurité fondamentale AWS) et rapports complets.
Installation et configuration
Installation binaire
# Download latest release for Linux
curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz
# Extract and install
tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz
sudo install terrascan /usr/local/bin && rm terrascan
# Verify installation
terrascan version
# Download for macOS
curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Darwin_x86_64.tar.gz")" > terrascan.tar.gz
# Download for Windows
curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Windows_x86_64.zip")" > terrascan.zip
Installation du gestionnaire de paquets
# Homebrew (macOS/Linux)
brew install terrascan
# Chocolatey (Windows)
choco install terrascan
# Scoop (Windows)
scoop install terrascan
# Arch Linux
yay -S terrascan
# Verify installation
terrascan version
```_
### Installation Docker
```bash
# Pull Docker image
docker pull tenable/terrascan:latest
# Create alias for easier usage
echo 'alias terrascan="docker run --rm -it -v $(pwd):/iac -w /iac tenable/terrascan"' >> ~/.bashrc
source ~/.bashrc
# Test installation
terrascan version
# Run with volume mount
docker run --rm -it -v $(pwd):/iac -w /iac tenable/terrascan scan
# Create Docker wrapper script
cat > terrascan-docker.sh << 'EOF'
#!/bin/bash
docker run --rm -it \
-v $(pwd):/iac \
-w /iac \
tenable/terrascan "$@"
EOF
chmod +x terrascan-docker.sh
sudo mv terrascan-docker.sh /usr/local/bin/terrascan-docker
```_
### Installation de la source
```bash
# Install Go (if not already installed)
wget https://golang.org/dl/go1.19.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
# Clone repository
git clone https://github.com/tenable/terrascan.git
cd terrascan
# Build from source
make build
# Install binary
sudo cp bin/terrascan /usr/local/bin/
# Verify installation
terrascan version
# Build with specific version
git checkout v1.18.0
make build
Configuration et configuration
# Initialize Terrascan configuration
terrascan init
# Create configuration directory
mkdir -p ~/.terrascan
# Create default configuration file
cat > ~/.terrascan/config.toml << 'EOF'
[scan]
# Scan configuration
iac-type = ""
iac-version = ""
cloud-provider = ["aws", "azure", "gcp", "github"]
severity = "high"
confidence = "high"
[rules]
# Rule configuration
skip-rules = []
include-rules = []
[output]
# Output configuration
format = "human"
output-file = ""
[logging]
# Logging configuration
level = "info"
[notifications]
# Notification configuration
webhook = ""
[policy]
# Policy configuration
rego-subdir = ""
policy-path = []
[server]
# Server configuration
port = 9010
cert-file = ""
private-key-file = ""
EOF
# Set environment variables
export TERRASCAN_CONFIG=~/.terrascan/config.toml
export TERRASCAN_LOG_LEVEL=info
# Create policy directory
mkdir -p ~/.terrascan/policies
# Download additional policies
git clone https://github.com/tenable/terrascan.git /tmp/terrascan-policies
cp -r /tmp/terrascan-policies/pkg/policies/opa/rego/* ~/.terrascan/policies/
Utilisation de base et commandes
Analyse simple
# Scan current directory (auto-detect IaC type)
terrascan scan
# Scan specific directory
terrascan scan -d /path/to/iac/files
# Scan specific file
terrascan scan -f main.tf
# Scan with specific IaC type
terrascan scan -t terraform
# Scan with specific cloud provider
terrascan scan -c aws
# Scan with specific IaC version
terrascan scan -i terraform -v v14
Options avancées de numérisation
# Scan with specific severity levels
terrascan scan --severity high
terrascan scan --severity medium,high
# Scan with specific confidence levels
terrascan scan --confidence high
# Scan with custom policy path
terrascan scan --policy-path /path/to/custom/policies
# Scan with rule filtering
terrascan scan --skip-rules AC_AWS_0001,AC_AWS_0002
terrascan scan --include-rules AC_AWS_0001,AC_AWS_0002
# Scan with output formatting
terrascan scan --output json
terrascan scan --output xml
terrascan scan --output yaml
terrascan scan --output junit-xml
terrascan scan --output sarif
# Scan with output file
terrascan scan --output json --output-file results.json
# Scan with verbose output
terrascan scan --verbose
# Scan with configuration file
terrascan scan --config-path ~/.terrascan/config.toml
Scannage de type multi-IaC
# Terraform scanning
terrascan scan -t terraform -d ./terraform/
# Kubernetes scanning
terrascan scan -t k8s -d ./k8s/
# Helm chart scanning
terrascan scan -t helm -d ./helm-charts/
# Kustomize scanning
terrascan scan -t kustomize -d ./kustomize/
# Dockerfile scanning
terrascan scan -t docker -f Dockerfile
# CloudFormation scanning
terrascan scan -t cfn -d ./cloudformation/
# ARM template scanning
terrascan scan -t arm -d ./arm-templates/
# Scan multiple IaC types in one command
find . -name "*.tf" -exec terrascan scan -t terraform -f {} \;
find . -name "*.yaml" -exec terrascan scan -t k8s -f {} \;
Gestion avancée des politiques
Création de politiques personnalisées
# Create custom policy directory
mkdir -p ~/.terrascan/custom-policies
# Create custom Rego policy
cat > ~/.terrascan/custom-policies/custom_s3_policy.rego << 'EOF'
package accurics
# Custom S3 bucket policy
s3BucketShouldHaveCustomTags[retVal] {
resource := input.aws_s3_bucket[_]
not resource.config.tags.Environment
retVal := {
"Id": "CUSTOM_S3_001",
"RuleID": "CUSTOM_S3_001",
"Severity": "HIGH",
"Description": "S3 bucket should have Environment tag",
"Category": "S3",
"ResourceType": "aws_s3_bucket",
"File": resource.file,
"LineNumber": resource.line,
"IssueType": "MissingAttribute",
"SearchKey": sprintf("aws_s3_bucket[%s].tags", [resource.name]),
"ExpectedValues": ["Environment tag should be present"],
"ActualValues": [resource.config.tags],
"References": ["https://docs.aws.amazon.com/s3/latest/userguide/object-tagging.html"]
}
}
# Custom EC2 instance policy
ec2InstanceShouldUseApprovedAMIs[retVal] {
resource := input.aws_instance[_]
approved_amis := ["ami-12345678", "ami-87654321", "ami-11111111"]
not resource.config.ami in approved_amis
retVal := {
"Id": "CUSTOM_EC2_001",
"RuleID": "CUSTOM_EC2_001",
"Severity": "MEDIUM",
"Description": "EC2 instance should use approved AMIs only",
"Category": "EC2",
"ResourceType": "aws_instance",
"File": resource.file,
"LineNumber": resource.line,
"IssueType": "WrongValue",
"SearchKey": sprintf("aws_instance[%s].ami", [resource.name]),
"ExpectedValues": approved_amis,
"ActualValues": [resource.config.ami],
"References": ["https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html"]
}
}
EOF
# Use custom policies
terrascan scan --policy-path ~/.terrascan/custom-policies
# Create JSON-based custom policy
cat > ~/.terrascan/custom-policies/custom_policy.json << 'EOF'
{
"rules": [
{
"id": "CUSTOM_JSON_001",
"description": "RDS instances should have backup retention period >= 7 days",
"severity": "HIGH",
"category": "RDS",
"resourceType": "aws_db_instance",
"conditions": {
"and": [
{
"attribute": "backup_retention_period",
"operator": "lt",
"value": 7
}
]
}
}
]
}
EOF
# Validate custom policies
terrascan scan --policy-path ~/.terrascan/custom-policies --verbose
Scripts de gestion des politiques
#!/usr/bin/env python3
# Advanced policy management for Terrascan
import json
import yaml
import os
import subprocess
import argparse
from pathlib import Path
class TerrascanPolicyManager:
"""Advanced policy management for Terrascan"""
def __init__(self, policy_dir="~/.terrascan/policies"):
self.policy_dir = Path(policy_dir).expanduser()
self.policy_dir.mkdir(parents=True, exist_ok=True)
def create_policy_from_template(self, policy_name, resource_type, severity="HIGH"):
"""Create a new policy from template"""
template = f'''package accurics
# Custom policy: {policy_name}
{policy_name.lower().replace(" ", "_")}[retVal] {{
resource := input.{resource_type}[_]
# Add your policy logic here
condition := true # Replace with actual condition
condition
retVal := {{
"Id": "CUSTOM_{resource_type.upper()}_001",
"RuleID": "CUSTOM_{resource_type.upper()}_001",
"Severity": "{severity}",
"Description": "{policy_name}",
"Category": "{resource_type.split('_')[1].upper()}",
"ResourceType": "{resource_type}",
"File": resource.file,
"LineNumber": resource.line,
"IssueType": "MissingAttribute",
"SearchKey": sprintf("{resource_type}[%s]", [resource.name]),
"ExpectedValues": ["Expected value"],
"ActualValues": ["Actual value"],
"References": ["https://example.com/reference"]
}}
}}
'''
policy_file = self.policy_dir / f"{policy_name.lower().replace(' ', '_')}.rego"
with open(policy_file, 'w') as f:
f.write(template)
print(f"Policy created: {policy_file}")
return policy_file
def validate_policies(self):
"""Validate all policies in the policy directory"""
print("Validating policies...")
for policy_file in self.policy_dir.glob("*.rego"):
try:
# Use opa fmt to validate syntax
result = subprocess.run(
["opa", "fmt", str(policy_file)],
capture_output=True,
text=True
)
if result.returncode == 0:
print(f"✅ {policy_file.name}: Valid")
else:
print(f"❌ {policy_file.name}: Invalid - {result.stderr}")
except FileNotFoundError:
print("⚠️ OPA not found. Install OPA to validate Rego policies.")
break
def list_policies(self):
"""List all available policies"""
print("Available policies:")
print("-" * 50)
for policy_file in self.policy_dir.glob("*.rego"):
with open(policy_file, 'r') as f:
content = f.read()
# Extract policy description
lines = content.split('\n')
description = "No description"
for line in lines:
if "Description" in line and '"' in line:
description = line.split('"')[1]
break
print(f"📄 {policy_file.name}")
print(f" Description: {description}")
print()
def test_policy(self, policy_file, test_data_file):
"""Test a policy against test data"""
try:
# Run terrascan with the specific policy
result = subprocess.run([
"terrascan", "scan",
"--policy-path", str(self.policy_dir),
"-f", test_data_file,
"--output", "json"
], capture_output=True, text=True)
if result.returncode == 0:
output = json.loads(result.stdout)
violations = output.get("results", {}).get("violations", [])
print(f"Policy test results for {policy_file}:")
print(f"Violations found: {len(violations)}")
for violation in violations:
print(f"- {violation.get('rule_name', 'Unknown')}: {violation.get('description', 'No description')}")
else:
print(f"Error testing policy: {result.stderr}")
except Exception as e:
print(f"Error testing policy: {e}")
def generate_policy_report(self):
"""Generate a comprehensive policy report"""
report = {
"policy_summary": {
"total_policies": 0,
"custom_policies": 0,
"policy_categories": {}
},
"policies": []
}
for policy_file in self.policy_dir.glob("*.rego"):
with open(policy_file, 'r') as f:
content = f.read()
# Parse policy information
policy_info = {
"file": policy_file.name,
"path": str(policy_file),
"size": policy_file.stat().st_size,
"rules": []
}
# Extract rules from content
lines = content.split('\n')
current_rule = None
for line in lines:
if '[retVal]' in line:
rule_name = line.split('[')[0].strip()
current_rule = {"name": rule_name}
elif current_rule and '"Id"' in line:
rule_id = line.split('"')[3]
current_rule["id"] = rule_id
elif current_rule and '"Severity"' in line:
severity = line.split('"')[3]
current_rule["severity"] = severity
elif current_rule and '"Category"' in line:
category = line.split('"')[3]
current_rule["category"] = category
policy_info["rules"].append(current_rule)
# Update category count
if category in report["policy_summary"]["policy_categories"]:
report["policy_summary"]["policy_categories"][category] += 1
else:
report["policy_summary"]["policy_categories"][category] = 1
current_rule = None
report["policies"].append(policy_info)
report["policy_summary"]["total_policies"] += 1
if "custom" in policy_file.name.lower():
report["policy_summary"]["custom_policies"] += 1
# Save report
report_file = self.policy_dir / "policy_report.json"
with open(report_file, 'w') as f:
json.dump(report, f, indent=2)
print(f"Policy report generated: {report_file}")
return report
def main():
parser = argparse.ArgumentParser(description='Terrascan Policy Manager')
parser.add_argument('action', choices=['create', 'validate', 'list', 'test', 'report'])
parser.add_argument('--name', help='Policy name for creation')
parser.add_argument('--resource-type', help='Resource type for policy')
parser.add_argument('--severity', default='HIGH', help='Policy severity')
parser.add_argument('--policy-file', help='Policy file for testing')
parser.add_argument('--test-data', help='Test data file')
args = parser.parse_args()
manager = TerrascanPolicyManager()
if args.action == 'create':
if not args.name or not args.resource_type:
print("Error: --name and --resource-type required for create action")
return
manager.create_policy_from_template(args.name, args.resource_type, args.severity)
elif args.action == 'validate':
manager.validate_policies()
elif args.action == 'list':
manager.list_policies()
elif args.action == 'test':
if not args.policy_file or not args.test_data:
print("Error: --policy-file and --test-data required for test action")
return
manager.test_policy(args.policy_file, args.test_data)
elif args.action == 'report':
manager.generate_policy_report()
if __name__ == "__main__":
main()
Intégration CI/CD
Intégration des actions GitHub
# .github/workflows/terrascan.yml
name: Terrascan IaC Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
terrascan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Terrascan
uses: tenable/terrascan-action@main
with:
iac_type: terraform
iac_version: v14
policy_type: aws
only_warn: true
sarif_upload: true
- name: Run Terrascan Scan
run: |
terrascan scan \
--iac-type terraform \
--cloud-provider aws,azure,gcp \
--severity high,medium \
--output sarif \
--output-file terrascan-results.sarif
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: terrascan-results.sarif
- name: Generate detailed report
run: |
terrascan scan \
--iac-type terraform \
--output json \
--output-file terrascan-detailed.json
- name: Upload scan results
uses: actions/upload-artifact@v3
with:
name: terrascan-results
path: |
terrascan-results.sarif
terrascan-detailed.json
terrascan-multiple-iac:
runs-on: ubuntu-latest
strategy:
matrix:
iac_type: [terraform, k8s, helm, dockerfile]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Terrascan
run: |
curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz
tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz
sudo install terrascan /usr/local/bin && rm terrascan
- name: Scan ${{ matrix.iac_type }}
run: |
if [ -d "./${{ matrix.iac_type }}" ]; then
terrascan scan \
--iac-type ${{ matrix.iac_type }} \
--directory ./${{ matrix.iac_type }} \
--output json \
--output-file ${{ matrix.iac_type }}-results.json
fi
- name: Upload results
uses: actions/upload-artifact@v3
if: always()
with:
name: ${{ matrix.iac_type }}-scan-results
path: ${{ matrix.iac_type }}-results.json
Intégration GitLab CI
# .gitlab-ci.yml
stages:
- security-scan
- report
variables:
TERRASCAN_VERSION: "latest"
terrascan-scan:
stage: security-scan
image: tenable/terrascan:${TERRASCAN_VERSION}
script:
- |
# Scan Terraform files
if [ -d "./terraform" ]; then
terrascan scan \
--iac-type terraform \
--directory ./terraform \
--output json \
--output-file terraform-scan.json
fi
# Scan Kubernetes files
if [ -d "./k8s" ]; then
terrascan scan \
--iac-type k8s \
--directory ./k8s \
--output json \
--output-file k8s-scan.json
fi
# Scan Helm charts
if [ -d "./helm" ]; then
terrascan scan \
--iac-type helm \
--directory ./helm \
--output json \
--output-file helm-scan.json
fi
# Generate combined report
python3 -c "
import json
import glob
results = []
for file in glob.glob('*-scan.json'):
with open(file, 'r') as f:
data = json.load(f)
results.append({
'file': file,
'violations': data.get('results', {}).get('violations', [])
})
with open('combined-results.json', 'w') as f:
json.dump(results, f, indent=2)
"
artifacts:
reports:
junit: "*-scan.json"
paths:
- "*-scan.json"
- "combined-results.json"
expire_in: 1 week
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
terrascan-report:
stage: report
image: python:3.9-slim
dependencies:
- terrascan-scan
script:
- |
pip install jinja2
# Generate HTML report
python3 << 'EOF'
import json
import jinja2
from datetime import datetime
# Load results
with open('combined-results.json', 'r') as f:
results = json.load(f)
# Calculate statistics
total_violations = sum(len(r['violations']) for r in results)
high_severity = sum(1 for r in results for v in r['violations'] if v.get('severity') == 'HIGH')
medium_severity = sum(1 for r in results for v in r['violations'] if v.get('severity') == 'MEDIUM')
# HTML template
template = jinja2.Template('''
<!DOCTYPE html>
<html>
<head>
<title>Terrascan Security Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background: #f4f4f4; padding: 20px; border-radius: 5px; }
.stats { display: flex; gap: 20px; margin: 20px 0; }
.stat { background: #e9e9e9; padding: 15px; border-radius: 5px; text-align: center; }
.violation { border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 5px; }
.high { border-left: 5px solid #d32f2f; }
.medium { border-left: 5px solid #f57c00; }
.low { border-left: 5px solid #388e3c; }
</style>
</head>
<body>
<div class="header">
<h1>Terrascan Security Report</h1>
<p>Generated on: {{ timestamp }}</p>
</div>
<div class="stats">
<div class="stat">
<h3>{{ total_violations }}</h3>
<p>Total Violations</p>
</div>
<div class="stat">
<h3>{{ high_severity }}</h3>
<p>High Severity</p>
</div>
<div class="stat">
<h3>{{ medium_severity }}</h3>
<p>Medium Severity</p>
</div>
</div>
{% for result in results %}
<h2>{{ result.file }}</h2>
{% for violation in result.violations %}
<div class="violation {{ violation.severity.lower() }}">
<h4>{{ violation.rule_name }}</h4>
<p><strong>Severity:</strong> {{ violation.severity }}</p>
<p><strong>Description:</strong> {{ violation.description }}</p>
<p><strong>File:</strong> {{ violation.file }}:{{ violation.line }}</p>
<p><strong>Resource:</strong> {{ violation.resource_name }}</p>
</div>
{% endfor %}
{% endfor %}
</body>
</html>
''')
# Render report
html_content = template.render(
results=results,
total_violations=total_violations,
high_severity=high_severity,
medium_severity=medium_severity,
timestamp=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
)
with open('terrascan-report.html', 'w') as f:
f.write(html_content)
print("HTML report generated: terrascan-report.html")
EOF
artifacts:
paths:
- "terrascan-report.html"
expire_in: 1 month
Intégration du pipeline Jenkins
// Jenkinsfile
pipeline {
agent any
environment {
TERRASCAN_VERSION = 'latest'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install Terrascan') {
steps {
script {
sh '''
# Download and install Terrascan
curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz
tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz
chmod +x terrascan
sudo mv terrascan /usr/local/bin/
# Verify installation
terrascan version
'''
}
}
}
stage('Terrascan Security Scan') {
parallel {
stage('Terraform Scan') {
when {
expression { fileExists('terraform/') }
}
steps {
script {
sh '''
terrascan scan \
--iac-type terraform \
--directory ./terraform \
--output json \
--output-file terraform-results.json \
--severity high,medium
'''
}
}
}
stage('Kubernetes Scan') {
when {
expression { fileExists('k8s/') }
}
steps {
script {
sh '''
terrascan scan \
--iac-type k8s \
--directory ./k8s \
--output json \
--output-file k8s-results.json \
--severity high,medium
'''
}
}
}
stage('Dockerfile Scan') {
when {
expression { fileExists('Dockerfile') }
}
steps {
script {
sh '''
terrascan scan \
--iac-type docker \
--file ./Dockerfile \
--output json \
--output-file dockerfile-results.json \
--severity high,medium
'''
}
}
}
}
}
stage('Process Results') {
steps {
script {
// Process scan results
sh '''
python3 << 'EOF'
import json
import glob
import os
# Combine all results
all_results = []
total_violations = 0
high_violations = 0
medium_violations = 0
for result_file in glob.glob('*-results.json'):
if os.path.getsize(result_file) > 0:
with open(result_file, 'r') as f:
try:
data = json.load(f)
violations = data.get('results', {}).get('violations', [])
all_results.extend(violations)
total_violations += len(violations)
for violation in violations:
if violation.get('severity') == 'HIGH':
high_violations += 1
elif violation.get('severity') == 'MEDIUM':
medium_violations += 1
except json.JSONDecodeError:
print(f"Error parsing {result_file}")
# Create summary
summary = {
'total_violations': total_violations,
'high_violations': high_violations,
'medium_violations': medium_violations,
'violations': all_results
}
with open('terrascan-summary.json', 'w') as f:
json.dump(summary, f, indent=2)
print(f"Total violations: {total_violations}")
print(f"High severity: {high_violations}")
print(f"Medium severity: {medium_violations}")
# Set build status based on violations
if high_violations > 0:
print("FAILURE: High severity violations found")
exit(1)
elif medium_violations > 10: # Threshold for medium violations
print("UNSTABLE: Too many medium severity violations")
exit(2)
else:
print("SUCCESS: No critical violations found")
EOF
'''
}
}
}
}
post {
always {
// Archive results
archiveArtifacts artifacts: '*-results.json, terrascan-summary.json',
allowEmptyArchive: true
// Publish results
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'terrascan-summary.json',
reportName: 'Terrascan Security Report'
])
}
failure {
// Send notification on failure
emailext (
subject: "Terrascan Security Scan Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "High severity security violations found in IaC. Check the build logs for details.",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
unstable {
// Send notification on unstable build
emailext (
subject: "Terrascan Security Scan Unstable: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Medium severity security violations found in IaC. Review recommended.",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
}
}
Configuration et automatisation avancées
Gestion globale de la configuration
#!/bin/bash
# Advanced Terrascan configuration management
setup_terrascan_environment() {
echo "Setting up Terrascan environment..."
# Create directory structure
mkdir -p ~/.terrascan/{config,policies,reports,cache}
# Create advanced configuration
cat > ~/.terrascan/config.toml << 'EOF'
[scan]
# Scan configuration
iac-type = ""
iac-version = ""
cloud-provider = ["aws", "azure", "gcp", "github", "kubernetes"]
severity = "high"
confidence = "high"
timeout = "300s"
[rules]
# Rule configuration
skip-rules = []
include-rules = []
[output]
# Output configuration
format = "human"
output-file = ""
[logging]
# Logging configuration
level = "info"
file = "~/.terrascan/terrascan.log"
[notifications]
# Notification configuration
webhook = ""
slack-webhook = ""
email-smtp = ""
[policy]
# Policy configuration
rego-subdir = ""
policy-path = ["~/.terrascan/policies"]
[server]
# Server configuration
port = 9010
cert-file = ""
private-key-file = ""
[cache]
# Cache configuration
enabled = true
directory = "~/.terrascan/cache"
ttl = "24h"
[integrations]
# Integration configuration
jira-url = ""
jira-username = ""
jira-token = ""
github-token = ""
gitlab-token = ""
sonarqube-url = ""
sonarqube-token = ""
EOF
# Set environment variables
cat >> ~/.bashrc << 'EOF'
# Terrascan environment variables
export TERRASCAN_CONFIG=~/.terrascan/config.toml
export TERRASCAN_LOG_LEVEL=info
export TERRASCAN_CACHE_DIR=~/.terrascan/cache
export TERRASCAN_POLICY_PATH=~/.terrascan/policies
EOF
source ~/.bashrc
echo "Terrascan environment setup complete"
}
# Advanced scanning function
advanced_terrascan_scan() {
local scan_dir="${1:-.}"
local output_dir="${2:-./terrascan-results}"
local config_file="${3:-~/.terrascan/config.toml}"
echo "Starting advanced Terrascan scan..."
echo "Scan directory: $scan_dir"
echo "Output directory: $output_dir"
# Create output directory
mkdir -p "$output_dir"
# Detect IaC types in directory
iac_types=()
if find "$scan_dir" -name "*.tf" -type f | head -1 | grep -q .; then
iac_types+=("terraform")
fi
if find "$scan_dir" -name "*.yaml" -o -name "*.yml" | xargs grep -l "apiVersion\|kind" 2>/dev/null | head -1 | grep -q .; then
iac_types+=("k8s")
fi
if find "$scan_dir" -name "Chart.yaml" -type f | head -1 | grep -q .; then
iac_types+=("helm")
fi
if find "$scan_dir" -name "Dockerfile*" -type f | head -1 | grep -q .; then
iac_types+=("docker")
fi
if find "$scan_dir" -name "*.json" | xargs grep -l "AWSTemplateFormatVersion\|Resources" 2>/dev/null | head -1 | grep -q .; then
iac_types+=("cfn")
fi
echo "Detected IaC types: ${iac_types[*]}"
# Scan each IaC type
for iac_type in "${iac_types[@]}"; do
echo "Scanning $iac_type files..."
# Multiple output formats
terrascan scan \
--iac-type "$iac_type" \
--directory "$scan_dir" \
--config-path "$config_file" \
--output json \
--output-file "$output_dir/${iac_type}-results.json" \
--severity high,medium,low
terrascan scan \
--iac-type "$iac_type" \
--directory "$scan_dir" \
--config-path "$config_file" \
--output sarif \
--output-file "$output_dir/${iac_type}-results.sarif"
terrascan scan \
--iac-type "$iac_type" \
--directory "$scan_dir" \
--config-path "$config_file" \
--output junit-xml \
--output-file "$output_dir/${iac_type}-results.xml"
done
# Generate combined report
python3 << EOF
import json
import glob
import os
from datetime import datetime
output_dir = "$output_dir"
results_files = glob.glob(f"{output_dir}/*-results.json")
combined_results = {
"scan_info": {
"timestamp": datetime.now().isoformat(),
"scan_directory": "$scan_dir",
"iac_types": ${iac_types[@]@Q},
"total_files_scanned": 0,
"total_violations": 0
},
"results_by_type": {},
"summary": {
"high_severity": 0,
"medium_severity": 0,
"low_severity": 0,
"categories": {}
}
}
for results_file in results_files:
iac_type = os.path.basename(results_file).replace("-results.json", "")
try:
with open(results_file, 'r') as f:
data = json.load(f)
violations = data.get("results", {}).get("violations", [])
combined_results["results_by_type"][iac_type] = {
"violations": violations,
"count": len(violations)
}
combined_results["scan_info"]["total_violations"] += len(violations)
# Count by severity
for violation in violations:
severity = violation.get("severity", "").lower()
if severity == "high":
combined_results["summary"]["high_severity"] += 1
elif severity == "medium":
combined_results["summary"]["medium_severity"] += 1
elif severity == "low":
combined_results["summary"]["low_severity"] += 1
# Count by category
category = violation.get("category", "Unknown")
if category in combined_results["summary"]["categories"]:
combined_results["summary"]["categories"][category] += 1
else:
combined_results["summary"]["categories"][category] = 1
except Exception as e:
print(f"Error processing {results_file}: {e}")
# Save combined results
with open(f"{output_dir}/combined-results.json", 'w') as f:
json.dump(combined_results, f, indent=2)
print(f"Combined results saved to {output_dir}/combined-results.json")
print(f"Total violations: {combined_results['scan_info']['total_violations']}")
print(f"High severity: {combined_results['summary']['high_severity']}")
print(f"Medium severity: {combined_results['summary']['medium_severity']}")
print(f"Low severity: {combined_results['summary']['low_severity']}")
EOF
echo "Advanced scan completed. Results saved to: $output_dir"
}
# Automated remediation suggestions
generate_remediation_report() {
local results_file="$1"
local output_file="${2:-remediation-report.md}"
echo "Generating remediation report..."
python3 << EOF
import json
import re
from datetime import datetime
# Load results
with open("$results_file", 'r') as f:
data = json.load(f)
violations = data.get("results", {}).get("violations", [])
# Group violations by category and severity
categories = {}
for violation in violations:
category = violation.get("category", "Unknown")
severity = violation.get("severity", "Unknown")
if category not in categories:
categories[category] = {"HIGH": [], "MEDIUM": [], "LOW": []}
categories[category][severity].append(violation)
# Generate markdown report
report = f"""# Terrascan Remediation Report
**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
**Total Violations:** {len(violations)}
## Executive Summary
"""
# Add summary statistics
high_count = sum(len(cats["HIGH"]) for cats in categories.values())
medium_count = sum(len(cats["MEDIUM"]) for cats in categories.values())
low_count = sum(len(cats["LOW"]) for cats in categories.values())
report += f"""
| Severity | Count | Percentage |
|----------|-------|------------|
| High | {high_count} | {high_count/len(violations)*100:.1f}% |
| Medium | {medium_count} | {medium_count/len(violations)*100:.1f}% |
| Low | {low_count} | {low_count/len(violations)*100:.1f}% |
## Remediation Recommendations
"""
# Add remediation suggestions by category
remediation_suggestions = {
"S3": {
"description": "Amazon S3 security configurations",
"common_fixes": [
"Enable S3 bucket encryption",
"Configure proper bucket policies",
"Enable access logging",
"Block public access"
]
},
"EC2": {
"description": "Amazon EC2 security configurations",
"common_fixes": [
"Use security groups instead of 0.0.0.0/0",
"Enable detailed monitoring",
"Use IMDSv2 for metadata service",
"Encrypt EBS volumes"
]
},
"IAM": {
"description": "Identity and Access Management",
"common_fixes": [
"Follow principle of least privilege",
"Avoid wildcard permissions",
"Use managed policies when possible",
"Enable MFA for sensitive operations"
]
},
"RDS": {
"description": "Amazon RDS security configurations",
"common_fixes": [
"Enable encryption at rest",
"Configure backup retention",
"Use VPC security groups",
"Enable automated backups"
]
}
}
for category, violations_by_severity in categories.items():
total_category_violations = sum(len(v) for v in violations_by_severity.values())
report += f"""
### {category} ({total_category_violations} violations)
"""
if category in remediation_suggestions:
report += f"{remediation_suggestions[category]['description']}\\n\\n"
report += "**Common Fixes:**\\n"
for fix in remediation_suggestions[category]['common_fixes']:
report += f"- {fix}\\n"
report += "\\n"
# Add specific violations
for severity in ["HIGH", "MEDIUM", "LOW"]:
if violations_by_severity[severity]:
report += f"#### {severity} Severity ({len(violations_by_severity[severity])} violations)\\n\\n"
for violation in violations_by_severity[severity][:5]: # Limit to first 5
report += f"""
**Rule:** {violation.get('rule_name', 'Unknown')}
**Description:** {violation.get('description', 'No description')}
**File:** {violation.get('file', 'Unknown')}:{violation.get('line', 'Unknown')}
**Resource:** {violation.get('resource_name', 'Unknown')}
"""
if len(violations_by_severity[severity]) > 5:
report += f"... and {len(violations_by_severity[severity]) - 5} more violations\\n\\n"
# Add next steps
report += """
## Next Steps
1. **Prioritize High Severity Issues:** Address all high severity violations first
2. **Review Medium Severity Issues:** Evaluate business impact and fix accordingly
3. **Implement Policy as Code:** Use custom Terrascan policies to prevent future violations
4. **Automate Scanning:** Integrate Terrascan into CI/CD pipelines
5. **Regular Reviews:** Schedule periodic security reviews and scans
## Resources
- [Terrascan Documentation](https://runterrascan.io/)
- [AWS Security Best Practices](https://aws.amazon.com/security/security-resources/)
- [Azure Security Documentation](https://docs.microsoft.com/en-us/azure/security/)
- [GCP Security Best Practices](https://cloud.google.com/security/best-practices)
"""
# Save report
with open("$output_file", 'w') as f:
f.write(report)
print(f"Remediation report generated: $output_file")
EOF
}
# Run setup
setup_terrascan_environment
# Example usage
# advanced_terrascan_scan ./infrastructure ./scan-results
# generate_remediation_report ./scan-results/combined-results.json
Optimisation des performances et dépannage
Analyse des performances
#!/bin/bash
# Terrascan performance optimization
optimize_terrascan_performance() {
echo "Optimizing Terrascan performance..."
# 1. System-level optimizations
echo "Applying system optimizations..."
# Increase file descriptor limits
ulimit -n 65536
echo "* soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "* hard nofile 65536" | sudo tee -a /etc/security/limits.conf
# Optimize memory settings
echo 'vm.max_map_count=262144' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# 2. Terrascan-specific optimizations
echo "Configuring Terrascan optimizations..."
# Create optimized configuration
cat > ~/.terrascan/performance-config.toml << 'EOF'
# High-performance Terrascan configuration
[scan]
timeout = "600s" # Increase timeout for large scans
[cache]
enabled = true # Enable caching
directory = "~/.terrascan/cache"
ttl = "24h" # Cache results for 24 hours
[logging]
level = "warn" # Reduce log verbosity
[policy]
# Use local policies to avoid network calls
policy-path = ["~/.terrascan/policies"]
EOF
# 3. Create performance monitoring script
cat > ~/.terrascan/monitor-performance.sh << 'EOF'
#!/bin/bash
# Monitor Terrascan performance
monitor_scan() {
local scan_dir="$1"
local output_file="terrascan-performance-$(date +%s).log"
echo "Monitoring Terrascan performance for: $scan_dir"
# Start monitoring
{
echo "Timestamp,CPU%,Memory(MB),Files_Processed,Violations_Found"
start_time=$(date +%s)
while pgrep -f "terrascan" > /dev/null; do
local pid=$(pgrep -f "terrascan")
local cpu=$(ps -p $pid -o %cpu --no-headers 2>/dev/null || echo "0")
local mem=$(ps -p $pid -o rss --no-headers 2>/dev/null | awk '{print $1/1024}' || echo "0")
local timestamp=$(date +%s)
local elapsed=$((timestamp - start_time))
echo "$timestamp,$cpu,$mem,N/A,N/A"
sleep 2
done
} > "$output_file" &
local monitor_pid=$!
# Run Terrascan with timing
echo "Starting Terrascan scan..."
time terrascan scan -d "$scan_dir" --config-path ~/.terrascan/performance-config.toml
# Stop monitoring
kill $monitor_pid 2>/dev/null
echo "Performance monitoring completed: $output_file"
}
# Usage: monitor_scan /path/to/iac/files
EOF
chmod +x ~/.terrascan/monitor-performance.sh
echo "Performance optimizations applied"
}
# Benchmark different configurations
benchmark_terrascan() {
local test_dir="$1"
echo "Benchmarking Terrascan configurations..."
# Test different cache settings
cache_settings=("true" "false")
for cache in "${cache_settings[@]}"; do
echo "Testing with cache: $cache"
# Create test config
cat > /tmp/test-config.toml << EOF
[cache]
enabled = $cache
directory = "~/.terrascan/cache"
ttl = "24h"
EOF
start_time=$(date +%s)
terrascan scan -d "$test_dir" --config-path /tmp/test-config.toml > /dev/null 2>&1
end_time=$(date +%s)
duration=$((end_time - start_time))
echo "Cache $cache: ${duration}s"
done
# Test different log levels
log_levels=("debug" "info" "warn" "error")
echo "Testing different log levels..."
for level in "${log_levels[@]}"; do
echo "Testing log level: $level"
start_time=$(date +%s)
TERRASCAN_LOG_LEVEL=$level terrascan scan -d "$test_dir" > /dev/null 2>&1
end_time=$(date +%s)
duration=$((end_time - start_time))
echo "Log level $level: ${duration}s"
done
}
# Memory optimization for large scans
optimize_memory_usage() {
echo "Optimizing memory usage for large scans..."
# Split large directories into smaller chunks
split_scan_directory() {
local input_dir="$1"
local chunk_size="${2:-100}"
local output_dir="${3:-./scan_chunks}"
mkdir -p "$output_dir"
# Find all IaC files
find "$input_dir" -name "*.tf" -o -name "*.yaml" -o -name "*.yml" -o -name "*.json" > /tmp/iac_files.txt
# Split into chunks
split -l "$chunk_size" /tmp/iac_files.txt "$output_dir/chunk_"
echo "Directory split into chunks in: $output_dir"
}
# Process chunks sequentially
process_chunks() {
local chunk_dir="$1"
local output_file="$2"
echo "Processing chunks for memory optimization..."
for chunk in "$chunk_dir"/chunk_*; do
echo "Processing chunk: $(basename "$chunk")"
# Create temporary directory for chunk
temp_dir="/tmp/terrascan_chunk_$$"
mkdir -p "$temp_dir"
# Copy files from chunk to temp directory
while IFS= read -r file; do
if [ -f "$file" ]; then
cp "$file" "$temp_dir/"
fi
done < "$chunk"
# Scan chunk
terrascan scan -d "$temp_dir" --output json >> "$output_file"
# Clean up
rm -rf "$temp_dir"
# Small delay to prevent memory buildup
sleep 1
done
echo "All chunks processed. Results in: $output_file"
}
# Example usage
# split_scan_directory "./large_infrastructure" 50 "./chunks"
# process_chunks "./chunks" "chunked_results.json"
}
# Run optimizations
optimize_terrascan_performance
Résolution de problèmes communs
#!/bin/bash
# Terrascan troubleshooting guide
troubleshoot_terrascan() {
echo "Terrascan Troubleshooting Guide"
echo "==============================="
# Check if Terrascan is installed
if ! command -v terrascan &> /dev/null; then
echo "❌ Terrascan not found"
echo "Solution: Install Terrascan using one of these methods:"
echo " # Binary installation"
echo " curl -L \"\$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E \"https://.+?_Linux_x86_64.tar.gz\")\" > terrascan.tar.gz"
echo " tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz"
echo " sudo install terrascan /usr/local/bin && rm terrascan"
echo ""
echo " # Package manager"
echo " brew install terrascan # macOS/Linux"
echo " choco install terrascan # Windows"
return 1
fi
echo "✅ Terrascan found: $(terrascan version 2>/dev/null || echo 'Version unknown')"
# Check configuration
if [ ! -f ~/.terrascan/config.toml ]; then
echo "⚠️ Configuration file not found"
echo "Solution: Create default configuration"
echo " mkdir -p ~/.terrascan"
echo " terrascan init"
else
echo "✅ Configuration file exists"
fi
# Check policies
if [ ! -d ~/.terrascan/policies ]; then
echo "⚠️ Policies directory not found"
echo "Solution: Initialize policies"
echo " mkdir -p ~/.terrascan/policies"
echo " # Download default policies from Terrascan repository"
else
echo "✅ Policies directory exists"
policy_count=$(find ~/.terrascan/policies -name "*.rego" | wc -l)
echo " Found $policy_count policy files"
fi
# Check system resources
available_memory=$(free -m | awk 'NR==2{printf "%.1f", $7/1024}')
if (( $(echo "$available_memory < 1.0" | bc -l) )); then
echo "⚠️ Low available memory: ${available_memory}GB"
echo "Solution: Free up memory or use chunked scanning"
else
echo "✅ Available memory: ${available_memory}GB"
fi
# Check file descriptor limits
fd_limit=$(ulimit -n)
if [ "$fd_limit" -lt 1024 ]; then
echo "⚠️ Low file descriptor limit: $fd_limit"
echo "Solution: Increase file descriptor limit"
echo " ulimit -n 65536"
else
echo "✅ File descriptor limit: $fd_limit"
fi
# Test basic functionality
echo "Testing basic functionality..."
# Create test Terraform file
cat > /tmp/test.tf << 'EOF'
resource "aws_s3_bucket" "test" {
bucket = "test-bucket"
}
EOF
# Test scan
if terrascan scan -f /tmp/test.tf > /dev/null 2>&1; then
echo "✅ Basic scan functionality working"
else
echo "❌ Basic scan functionality failed"
echo "Solution: Check Terrascan installation and configuration"
fi
# Clean up
rm -f /tmp/test.tf
echo "Troubleshooting completed"
}
# Common error solutions
fix_common_terrascan_errors() {
echo "Common Terrascan Errors and Solutions"
echo "===================================="
cat << 'EOF'
1. "terrascan: command not found"
Solution:
- Download and install Terrascan binary
- Add installation directory to PATH
- Verify installation with: terrascan version
2. "failed to load policies"
Solution:
- Initialize Terrascan: terrascan init
- Check policy directory: ~/.terrascan/policies
- Download policies from official repository
3. "timeout exceeded" during scan
Solution:
- Increase timeout in config: timeout = "600s"
- Split large directories into smaller chunks
- Use performance-optimized configuration
4. "out of memory" errors
Solution:
- Increase system memory
- Use chunked scanning for large directories
- Reduce concurrent scans
- Enable caching to reduce memory usage
5. "invalid IaC type" error
Solution:
- Specify correct IaC type: --iac-type terraform
- Check file extensions and content
- Use auto-detection by omitting --iac-type
6. "no violations found" (false negatives)
Solution:
- Check policy configuration
- Verify IaC type detection
- Use --verbose for detailed output
- Check severity and confidence filters
7. "policy evaluation failed"
Solution:
- Validate custom policies with OPA
- Check Rego syntax in custom policies
- Update to latest Terrascan version
- Review policy path configuration
8. "SSL certificate verification failed"
Solution:
- Update system CA certificates
- Use --insecure flag for testing (not recommended for production)
- Configure proxy settings if behind corporate firewall
9. "permission denied" errors
Solution:
- Check file and directory permissions
- Run with appropriate user privileges
- Verify write permissions for output directory
10. Slow scanning performance
Solution:
- Enable caching in configuration
- Use local policy files
- Reduce log verbosity
- Optimize system resources
- Use parallel scanning for multiple directories
EOF
}
# Performance diagnostics
diagnose_performance_issues() {
echo "Diagnosing Terrascan Performance Issues"
echo "======================================"
# Check system load
load_avg=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
echo "System load average: $load_avg"
# Check available CPU cores
cpu_cores=$(nproc)
echo "Available CPU cores: $cpu_cores"
# Check memory usage
memory_info=$(free -h | grep "Mem:")
echo "Memory info: $memory_info"
# Check disk I/O
if command -v iostat &> /dev/null; then
echo "Disk I/O statistics:"
iostat -x 1 1 | tail -n +4
fi
# Check file system performance
echo "Testing file system performance..."
test_dir="/tmp/terrascan_perf_test"
mkdir -p "$test_dir"
# Create test files
for i in {1..100}; do
echo "resource \"aws_s3_bucket\" \"test_$i\" { bucket = \"test-bucket-$i\" }" > "$test_dir/test_$i.tf"
done
# Time file operations
start_time=$(date +%s.%N)
find "$test_dir" -name "*.tf" | wc -l > /dev/null
end_time=$(date +%s.%N)
file_op_time=$(echo "$end_time - $start_time" | bc)
echo "File operation time: ${file_op_time}s"
# Clean up
rm -rf "$test_dir"
# Recommendations based on findings
echo ""
echo "Performance Recommendations:"
echo "- Optimal memory: >= 4GB for large scans"
echo "- Enable caching for repeated scans"
echo "- Use SSD storage for better I/O performance"
echo "- Consider chunked scanning for very large codebases"
}
# Main troubleshooting function
main() {
troubleshoot_terrascan
echo ""
fix_common_terrascan_errors
echo ""
diagnose_performance_issues
}
# Run troubleshooting
main
Ressources et documentation
Ressources officielles
- [Documentation de Terrascan] (LINK_12) - Documentation et guides officiels
- Terrascan GitHub Repository - Code source et numéros
- Terrascan publie - Télécharger les dernières versions
- Documentation de politique - Politiques intégrées et personnalisées
Ressources communautaires
- Communauté terrascane - Discussions communautaires
- DevSecOps Community - DevSecOps pratiques exemplaires
- [Infrastructure comme sécurité du code] (LINK_12) - Ressources de sécurité IaC
- [Cloud Security Alliance] (LINK_12) - Lignes directrices sur la sécurité dans le cloud
Exemples d'intégration
- [Exemples d'intégration IC/CD] (LINK_12) - Exemples officiels
- [Sécurité Terraform] (LINK_12) - Pratiques de sécurité Terraform
- Kubernetes Security - Pratiques exemplaires en matière de sécurité de Kubernetes
- [Sécurité AWS] (LINK_12) - Documents de sécurité AWS