Files
Anthropic-Cybersecurity-Skills/skills/securing-container-registry-with-harbor/scripts/process.py
T

181 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""
Harbor Container Registry Security Auditor
Audits Harbor registry configuration for security best practices
including scanning policies, content trust, RBAC, and TLS.
"""
import json
import sys
import urllib.request
import urllib.error
import ssl
import base64
from dataclasses import dataclass, field
@dataclass
class HarborFinding:
category: str
title: str
severity: str
details: str
remediation: str
@dataclass
class HarborAuditReport:
findings: list = field(default_factory=list)
harbor_url: str = ""
projects_audited: int = 0
def harbor_api_call(base_url: str, endpoint: str, username: str, password: str) -> dict:
"""Call Harbor API and return JSON response."""
url = f"{base_url}/api/v2.0{endpoint}"
credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
req = urllib.request.Request(url)
req.add_header("Authorization", f"Basic {credentials}")
req.add_header("Content-Type", "application/json")
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
try:
with urllib.request.urlopen(req, context=ctx, timeout=10) as response:
return json.loads(response.read().decode())
except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError) as e:
print(f"[!] API call failed for {endpoint}: {e}")
return {}
def audit_projects(base_url: str, username: str, password: str, report: HarborAuditReport):
"""Audit all projects for security configurations."""
projects = harbor_api_call(base_url, "/projects?page=1&page_size=100", username, password)
if not projects:
return
for project in projects:
name = project.get("name", "unknown")
metadata = project.get("metadata", {})
report.projects_audited += 1
# Check auto-scan
if metadata.get("auto_scan") != "true":
report.findings.append(HarborFinding(
category="Scanning",
title=f"Auto-scan disabled for project: {name}",
severity="HIGH",
details="Images pushed to this project are not automatically scanned",
remediation=f"Enable auto_scan for project '{name}'"
))
# Check vulnerability prevention
if metadata.get("prevent_vul") != "true":
report.findings.append(HarborFinding(
category="Scanning",
title=f"Vulnerability prevention disabled for project: {name}",
severity="HIGH",
details="Vulnerable images can be pulled from this project",
remediation=f"Enable prevent_vul for project '{name}'"
))
# Check content trust
if metadata.get("enable_content_trust") != "true" and metadata.get("enable_content_trust_cosign") != "true":
report.findings.append(HarborFinding(
category="Content Trust",
title=f"Content trust not enforced for project: {name}",
severity="MEDIUM",
details="Unsigned images can be used from this project",
remediation=f"Enable content trust (Cosign) for project '{name}'"
))
# Check public visibility
if metadata.get("public") == "true":
report.findings.append(HarborFinding(
category="Access Control",
title=f"Project is publicly accessible: {name}",
severity="MEDIUM",
details="Anyone can pull images from this project",
remediation=f"Set project '{name}' to private unless intentionally public"
))
def audit_system_config(base_url: str, username: str, password: str, report: HarborAuditReport):
"""Audit system-level Harbor configuration."""
config = harbor_api_call(base_url, "/configurations", username, password)
if not config:
return
# Check auth mode
auth_mode = config.get("auth_mode", {}).get("value", "db_auth")
if auth_mode == "db_auth":
report.findings.append(HarborFinding(
category="Authentication",
title="Using local database authentication",
severity="MEDIUM",
details="Harbor uses local DB auth instead of enterprise IdP",
remediation="Configure OIDC or LDAP authentication"
))
# Check self-registration
self_reg = config.get("self_registration", {}).get("value", False)
if self_reg:
report.findings.append(HarborFinding(
category="Authentication",
title="Self-registration is enabled",
severity="HIGH",
details="Anyone can create an account on this Harbor instance",
remediation="Disable self-registration in Harbor configuration"
))
def print_report(report: HarborAuditReport):
print("\n" + "=" * 70)
print("HARBOR REGISTRY SECURITY AUDIT")
print("=" * 70)
print(f"Harbor URL: {report.harbor_url}")
print(f"Projects Audited: {report.projects_audited}")
print(f"Findings: {len(report.findings)}")
print("=" * 70)
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
findings = [f for f in report.findings if f.severity == sev]
if findings:
print(f"\n{sev} ({len(findings)}):")
for f in findings:
print(f" [{f.category}] {f.title}")
print(f" Fix: {f.remediation}")
def main():
import argparse
parser = argparse.ArgumentParser(description="Harbor Registry Security Auditor")
parser.add_argument("--url", required=True, help="Harbor URL (e.g., https://harbor.example.com)")
parser.add_argument("--username", default="admin", help="Harbor admin username")
parser.add_argument("--password", required=True, help="Harbor admin password")
args = parser.parse_args()
report = HarborAuditReport(harbor_url=args.url)
print(f"[*] Auditing Harbor at {args.url}")
audit_projects(args.url, args.username, args.password, report)
audit_system_config(args.url, args.username, args.password, report)
print_report(report)
with open("harbor_audit_report.json", "w") as f:
json.dump({
"harbor_url": report.harbor_url,
"findings": [{"category": f.category, "title": f.title,
"severity": f.severity, "remediation": f.remediation}
for f in report.findings]
}, f, indent=2)
print("\n[*] Report saved to harbor_audit_report.json")
if __name__ == "__main__":
main()