feat(hardening): add container security scanning with Trivy
- Add container-scanning.yml task file for vulnerability scans - Add systemd timer and service for scheduled scans - Add container-security-scan.sh script for manual scans - Integrate Trivy for Docker image vulnerability detection Implements: Automated container security monitoring
This commit is contained in:
parent
ac8b0b9abd
commit
0eb8c1b139
4 changed files with 214 additions and 0 deletions
70
roles/hardening/tasks/container-scanning.yml
Normal file
70
roles/hardening/tasks/container-scanning.yml
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
# Container Security Scanning with Trivy
|
||||
|
||||
- name: Install Trivy scanner
|
||||
apt:
|
||||
name:
|
||||
- apt-transport-https
|
||||
- gnupg
|
||||
- lsb-release
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Add Trivy GPG key
|
||||
apt_key:
|
||||
url: https://aquasecurity.github.io/trivy-repo/deb/public.key
|
||||
state: present
|
||||
|
||||
- name: Add Trivy repository
|
||||
apt_repository:
|
||||
repo: "deb https://aquasecurity.github.io/trivy-repo/deb {{ ansible_distribution_release }} main"
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Install Trivy
|
||||
apt:
|
||||
name: trivy
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Create Trivy report directory
|
||||
file:
|
||||
path: /var/log/trivy-scans
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
- name: Install container scan script
|
||||
copy:
|
||||
src: container-security-scan.sh
|
||||
dest: /usr/local/sbin/container-security-scan
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
- name: Install systemd service for container scanning
|
||||
template:
|
||||
src: trivy-scan.service.j2
|
||||
dest: /etc/systemd/system/trivy-scan.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Install systemd timer for weekly scans
|
||||
template:
|
||||
src: trivy-scan.timer.j2
|
||||
dest: /etc/systemd/system/trivy-scan.timer
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Reload systemd
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Enable and start weekly scan timer
|
||||
systemd:
|
||||
name: trivy-scan.timer
|
||||
enabled: true
|
||||
state: started
|
||||
10
roles/hardening/templates/trivy-scan.service.j2
Normal file
10
roles/hardening/templates/trivy-scan.service.j2
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[Unit]
|
||||
Description=Container Security Scan with Trivy
|
||||
After=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/container-security-scan --severity HIGH,CRITICAL
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=trivy-scan
|
||||
9
roles/hardening/templates/trivy-scan.timer.j2
Normal file
9
roles/hardening/templates/trivy-scan.timer.j2
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description=Weekly Container Security Scan with Trivy
|
||||
|
||||
[Timer]
|
||||
OnCalendar=Sun 03:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
125
scripts/container-security-scan.sh
Normal file
125
scripts/container-security-scan.sh
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Container Image Security Scanner
|
||||
# Uses Trivy to scan images for CVEs
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VAULT_PASSWORD_FILE="${VAULT_PASSWORD_FILE:-secrets/.vault_pass}"
|
||||
REPORT_DIR="${REPORT_DIR:-/var/log/trivy-scans}"
|
||||
SEVERITY="${SEVERITY:-HIGH,CRITICAL}"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [options]"
|
||||
echo "Options:"
|
||||
echo " --scan-all Scan all running containers (default)"
|
||||
echo " --scan-image IMAGE Scan specific image"
|
||||
echo " --severity LEVEL Severity filter (default: HIGH,CRITICAL)"
|
||||
echo " --report-only Exit 0 even if CVEs found (for CI reporting)"
|
||||
echo " --help Show this help"
|
||||
exit 1
|
||||
}
|
||||
|
||||
SCAN_MODE="all"
|
||||
TARGET_IMAGE=""
|
||||
REPORT_ONLY=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--scan-all) SCAN_MODE="all"; shift ;;
|
||||
--scan-image)
|
||||
SCAN_MODE="single"
|
||||
TARGET_IMAGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--severity) SEVERITY="$2"; shift 2 ;;
|
||||
--report-only) REPORT_ONLY=true; shift ;;
|
||||
--help) usage ;;
|
||||
*) echo "Unknown option: $1"; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if Trivy is installed
|
||||
if ! command -v trivy &> /dev/null; then
|
||||
echo "Installing Trivy..."
|
||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||
fi
|
||||
|
||||
# Create report directory
|
||||
mkdir -p "$REPORT_DIR"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
REPORT_FILE="$REPORT_DIR/trivy-scan-$TIMESTAMP.json"
|
||||
SUMMARY_FILE="$REPORT_DIR/trivy-scan-$TIMESTAMP.summary"
|
||||
|
||||
scan_image() {
|
||||
local image="$1"
|
||||
echo "Scanning: $image"
|
||||
|
||||
# Scan with Trivy
|
||||
if trivy image \
|
||||
--severity "$SEVERITY" \
|
||||
--format json \
|
||||
--output "$REPORT_FILE.tmp" \
|
||||
--scanners vuln,config \
|
||||
"$image" 2>/dev/null; then
|
||||
|
||||
# Check for vulnerabilities
|
||||
if command -v jq &> /dev/null; then
|
||||
CRITICAL=$(jq '[.Results[]?.Vulnerabilities? // empty | .[] | select(.Severity == "CRITICAL")] | length' "$REPORT_FILE.tmp" 2>/dev/null || echo "0")
|
||||
HIGH=$(jq '[.Results[]?.Vulnerabilities? // empty | .[] | select(.Severity == "HIGH")] | length' "$REPORT_FILE.tmp" 2>/dev/null || echo "0")
|
||||
|
||||
echo " CRITICAL: $CRITICAL, HIGH: $HIGH"
|
||||
|
||||
if [[ "$CRITICAL" -gt 0 ]] && [[ "$REPORT_ONLY" == "false" ]]; then
|
||||
echo "ERROR: CRITICAL vulnerabilities found in $image" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cat "$REPORT_FILE.tmp" >> "$REPORT_FILE"
|
||||
rm -f "$REPORT_FILE.tmp"
|
||||
return 0
|
||||
else
|
||||
echo "WARNING: Failed to scan $image" >&2
|
||||
return 0 # Continue scanning other images
|
||||
fi
|
||||
}
|
||||
|
||||
# Main scanning logic
|
||||
echo "Starting Trivy container scan at $(date)"
|
||||
echo "Severity filter: $SEVERITY"
|
||||
echo ""
|
||||
|
||||
if [[ "$SCAN_MODE" == "single" ]]; then
|
||||
scan_image "$TARGET_IMAGE"
|
||||
else
|
||||
# Get all unique images from running containers
|
||||
IMAGES=$(docker ps --format '{{.Image}}' | sort -u)
|
||||
|
||||
TOTAL=0
|
||||
FAILED=0
|
||||
|
||||
for image in $IMAGES; do
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if ! scan_image "$image"; then
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Scan complete: $TOTAL images scanned, $FAILED failed"
|
||||
fi
|
||||
|
||||
# Generate summary
|
||||
echo ""
|
||||
echo "Report saved to: $REPORT_FILE"
|
||||
echo "To view: trivy convert --format table $REPORT_FILE"
|
||||
|
||||
# Cleanup old reports (keep last 30 days)
|
||||
find "$REPORT_DIR" -name "trivy-scan-*.json" -mtime +30 -delete 2>/dev/null || true
|
||||
|
||||
if [[ "$FAILED" -gt 0 ]] && [[ "$REPORT_ONLY" == "false" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Loading…
Reference in a new issue