mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 14:14:56 +03:00
192 lines
5.1 KiB
Markdown
192 lines
5.1 KiB
Markdown
# API Reference: Container Image Hardening Audit
|
|
|
|
## Libraries Used
|
|
|
|
| Library | Purpose |
|
|
|---------|---------|
|
|
| `subprocess` | Execute Trivy, Docker, and hadolint CLI commands |
|
|
| `json` | Parse vulnerability scan and inspection results |
|
|
| `re` | Analyze Dockerfile instructions |
|
|
| `pathlib` | Handle Dockerfile and image paths |
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
# Trivy vulnerability scanner
|
|
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
|
|
|
# Hadolint Dockerfile linter
|
|
wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
|
|
chmod +x /usr/local/bin/hadolint
|
|
|
|
# Docker CLI (already installed in most environments)
|
|
```
|
|
|
|
## Trivy Image Scanning
|
|
|
|
### Scan Image for Vulnerabilities
|
|
```python
|
|
import subprocess
|
|
import json
|
|
|
|
def scan_image(image_name, severity="CRITICAL,HIGH"):
|
|
cmd = [
|
|
"trivy", "image",
|
|
"--format", "json",
|
|
"--severity", severity,
|
|
"--exit-code", "0",
|
|
image_name,
|
|
]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
|
return json.loads(result.stdout) if result.stdout else {}
|
|
```
|
|
|
|
### Scan for Secrets in Image
|
|
```python
|
|
def scan_secrets(image_name):
|
|
cmd = [
|
|
"trivy", "image",
|
|
"--format", "json",
|
|
"--scanners", "secret",
|
|
image_name,
|
|
]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
|
return json.loads(result.stdout) if result.stdout else {}
|
|
```
|
|
|
|
### Scan for Misconfigurations
|
|
```python
|
|
def scan_misconfig(image_name):
|
|
cmd = [
|
|
"trivy", "image",
|
|
"--format", "json",
|
|
"--scanners", "misconfig",
|
|
image_name,
|
|
]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
|
return json.loads(result.stdout) if result.stdout else {}
|
|
```
|
|
|
|
## Docker Image Inspection
|
|
|
|
### Inspect Image Metadata
|
|
```python
|
|
def inspect_image(image_name):
|
|
cmd = ["docker", "inspect", image_name]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
data = json.loads(result.stdout)[0]
|
|
config = data.get("Config", {})
|
|
return {
|
|
"image": image_name,
|
|
"user": config.get("User", "root"),
|
|
"exposed_ports": list(config.get("ExposedPorts", {}).keys()),
|
|
"env_vars": config.get("Env", []),
|
|
"entrypoint": config.get("Entrypoint"),
|
|
"cmd": config.get("Cmd"),
|
|
"labels": config.get("Labels", {}),
|
|
"layers": len(data.get("RootFS", {}).get("Layers", [])),
|
|
"size_mb": round(data.get("Size", 0) / 1048576, 1),
|
|
}
|
|
```
|
|
|
|
### Check for Root User
|
|
```python
|
|
def check_non_root(image_name):
|
|
inspection = inspect_image(image_name)
|
|
user = inspection["user"]
|
|
return {
|
|
"image": image_name,
|
|
"runs_as_root": user in ("", "root", "0"),
|
|
"user": user or "root (default)",
|
|
"severity": "high" if user in ("", "root", "0") else "pass",
|
|
}
|
|
```
|
|
|
|
## Hadolint Dockerfile Linting
|
|
|
|
```python
|
|
def lint_dockerfile(dockerfile_path):
|
|
cmd = [
|
|
"hadolint",
|
|
"--format", "json",
|
|
str(dockerfile_path),
|
|
]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
findings = json.loads(result.stdout) if result.stdout else []
|
|
return [
|
|
{
|
|
"line": f["line"],
|
|
"code": f["code"],
|
|
"level": f["level"],
|
|
"message": f["message"],
|
|
}
|
|
for f in findings
|
|
]
|
|
```
|
|
|
|
## Hardening Checks
|
|
|
|
### Common Dockerfile Issues
|
|
```python
|
|
def audit_dockerfile(dockerfile_path):
|
|
findings = []
|
|
with open(dockerfile_path) as f:
|
|
lines = f.readlines()
|
|
|
|
has_user = False
|
|
has_healthcheck = False
|
|
|
|
for i, line in enumerate(lines, 1):
|
|
stripped = line.strip()
|
|
if stripped.startswith("USER") and stripped.split()[-1] not in ("root", "0"):
|
|
has_user = True
|
|
if stripped.startswith("HEALTHCHECK"):
|
|
has_healthcheck = True
|
|
if stripped.startswith("FROM") and ":latest" in stripped:
|
|
findings.append({
|
|
"line": i, "severity": "medium",
|
|
"issue": "Using :latest tag — pin specific version",
|
|
})
|
|
if "ADD" in stripped and ("http://" in stripped or "https://" in stripped):
|
|
findings.append({
|
|
"line": i, "severity": "high",
|
|
"issue": "ADD with remote URL — use COPY + curl for verification",
|
|
})
|
|
|
|
if not has_user:
|
|
findings.append({"line": 0, "severity": "high", "issue": "No USER instruction — runs as root"})
|
|
if not has_healthcheck:
|
|
findings.append({"line": 0, "severity": "low", "issue": "No HEALTHCHECK instruction"})
|
|
|
|
return findings
|
|
```
|
|
|
|
## Output Format
|
|
|
|
```json
|
|
{
|
|
"image": "myapp:v1.2.3",
|
|
"vulnerabilities": {
|
|
"critical": 2,
|
|
"high": 8,
|
|
"medium": 15,
|
|
"low": 23
|
|
},
|
|
"runs_as_root": false,
|
|
"size_mb": 142.5,
|
|
"layers": 12,
|
|
"dockerfile_issues": 3,
|
|
"secrets_found": 0,
|
|
"findings": [
|
|
{
|
|
"type": "vulnerability",
|
|
"package": "openssl",
|
|
"installed": "3.0.2",
|
|
"fixed": "3.0.13",
|
|
"severity": "CRITICAL",
|
|
"cve": "CVE-2024-0727"
|
|
}
|
|
]
|
|
}
|
|
```
|