Terrascan Cheat Sheet
"Clase de la hoja" id="copy-btn" class="copy-btn" onclick="copyAllCommands()" Copiar todos los comandos id="pdf-btn" class="pdf-btn" onclick="generatePDF()" Generar PDF seleccionado/button ■/div titulada
Sinopsis
Terrascan es un analizador de códigos estáticos para la infraestructura como código (IaC) que ayuda a detectar violaciones de cumplimiento y seguridad en el suministro de infraestructura nativa en la nube. Soporta a múltiples proveedores de IaC incluyendo Terraform, Kubernetes, Helm, Kustomize, Dockerfiles y plantillas de formación de nubes. Terrascan ofrece más de 500 policías y apoya OPA, Rego Language y JSON/YAML para escribir políticas personalizadas.
■ Características clave: Multi-Ia C support, 500+ built-in policies, custom policy creation, CI/CD integration, compliance frameworks (CIS, NIST, AWS Foundational Security Standard), and comprehensive reporting.
Instalación y configuración
Instalación binaria
# 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
Paquete Manager Instalación
# 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
Docker Instalación
# 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
Fuente: Instalación
# 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
Configuración y configuración
# 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/
Uso básico y comandos
Escáner 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
Opciones avanzadas de exploración
# 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
Escáner de tipo 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 {} \;
Advanced Policy Management
Creación de políticas personalizadas
# 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 gestión de políticas
#!/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()
CI/CD Integration
GitHub Actions Integration
# .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
GitLab CI Integration
# .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
Jenkins Pipeline Integration
// 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}"
)
}
}
}
Configuración y automatización avanzadas
Gestión integral de configuración
#!/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
Optimización del rendimiento y solución de problemas
Performance Tuning
#!/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
Problemas comunes
#!/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
Recursos y documentación
Recursos oficiales
- Terrascan Documentation - Documentación y guías oficiales
- Terrascan GitHub Repository - Código fuente y cuestiones
- Terrascan Releases - Descargar las últimas versiones
- Policy Documentation - Políticas incorporadas y personalizadas
Recursos comunitarios
- Comunidad Terrascan - Debates comunitarios
- DevSecOps Community - DevSecOps mejores prácticas
- Infraestructura como seguridad del código - Recursos de seguridad de IaC
- Cloud Security Alliance - Directrices de seguridad en la nube
Ejemplos de integración
- CI/CD Integration Ejemplos - Ejemplos oficiales
- Terraform Security - Terraform security practices
- Kubernetes Security - Kubernetes security best practices
- AWS Security - AWS security documentation