npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
Harbor is an open-source container registry that provides security features including vulnerability scanning (integrated Trivy), image signing (Notary/Cosign), RBAC, content trust policies, replication, and audit logging. Securing Harbor involves configuring these features to enforce image provenance, prevent vulnerable image deployment, and maintain registry access control.
When to Use
- When deploying or configuring securing container registry with harbor capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Harbor 2.10+ installed (Helm or Docker Compose)
- TLS certificates for HTTPS
- Trivy scanner integration
- OIDC/LDAP for authentication
- Kubernetes cluster (for deployment target)
Workflow
Step 1: Install Harbor with Security Configuration
# harbor-values.yaml for Helm deployment
expose:
type: ingress
tls:
enabled: true
certSource: secret
secret:
secretName: harbor-tls
notarySecretName: harbor-tls
ingress:
hosts:
core: harbor.example.com
notary: notary.example.com
externalURL: https://harbor.example.com
persistence:
enabled: true
resourcePolicy: "keep"
harborAdminPassword: "<strong-password>"
trivy:
enabled: true
gitHubToken: "<github-token>"
severity: "CRITICAL,HIGH,MEDIUM"
autoScan: true
notary:
enabled: true
core:
secretKey: "<32-char-secret>"
database:
type: external
external:
host: postgres.example.com
port: "5432"
username: harbor
password: "<db-password>"
sslmode: requirehelm repo add harbor https://helm.getharbor.io
helm install harbor harbor/harbor -f harbor-values.yaml -n harbor --create-namespaceStep 2: Configure Vulnerability Scanning Policies
# Enable auto-scan on push (via Harbor API)
curl -k -X PUT "https://harbor.example.com/api/v2.0/projects/myproject" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"auto_scan": "true",
"severity": "critical",
"prevent_vul": "true",
"reuse_sys_cve_allowlist": "true"
}
}'Step 3: Configure Content Trust
# Enable content trust at project level
curl -k -X PUT "https://harbor.example.com/api/v2.0/projects/myproject" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"enable_content_trust": "true",
"enable_content_trust_cosign": "true"
}
}'
# Sign image with Cosign
cosign sign --key cosign.key harbor.example.com/myproject/myapp:v1.0.0
# Verify signature
cosign verify --key cosign.pub harbor.example.com/myproject/myapp:v1.0.0Step 4: Configure RBAC and Project Isolation
# Create project with private visibility
curl -k -X POST "https://harbor.example.com/api/v2.0/projects" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)" \
-H "Content-Type: application/json" \
-d '{
"project_name": "production",
"metadata": {
"public": "false",
"auto_scan": "true",
"prevent_vul": "true",
"severity": "high"
}
}'
# Harbor roles: ProjectAdmin, Maintainer, Developer, Guest, LimitedGuest
# Add member with specific role
curl -k -X POST "https://harbor.example.com/api/v2.0/projects/production/members" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)" \
-H "Content-Type: application/json" \
-d '{
"role_id": 3,
"member_user": {"username": "developer1"}
}'Step 5: Configure Immutable Tags and Retention
# Create tag immutability rule (prevent overwriting release tags)
curl -k -X POST "https://harbor.example.com/api/v2.0/projects/production/immutabletagrules" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)" \
-H "Content-Type: application/json" \
-d '{
"tag_filter": "v*",
"scope_selectors": {
"repository": [{"kind": "doublestar", "decoration": "repoMatches", "pattern": "**"}]
}
}'
# Configure retention policy (keep last 10 tags, delete untagged after 7 days)
curl -k -X POST "https://harbor.example.com/api/v2.0/retentions" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)" \
-H "Content-Type: application/json" \
-d '{
"algorithm": "or",
"rules": [
{
"action": "retain",
"template": "latestPushedK",
"params": {"latestPushedK": 10},
"tag_selectors": [{"kind": "doublestar", "decoration": "matches", "pattern": "**"}],
"scope_selectors": {"repository": [{"kind": "doublestar", "decoration": "repoMatches", "pattern": "**"}]}
}
],
"trigger": {"kind": "Schedule", "settings": {"cron": "0 0 * * *"}}
}'Step 6: OIDC Authentication Integration
# Harbor configuration for OIDC
auth_mode: oidc_auth
oidc_name: "Okta"
oidc_endpoint: "https://company.okta.com/oauth2/default"
oidc_client_id: "harbor-client-id"
oidc_client_secret: "harbor-client-secret"
oidc_groups_claim: "groups"
oidc_admin_group: "harbor-admins"
oidc_scope: "openid,profile,email,groups"
oidc_verify_cert: true
oidc_auto_onboard: trueValidation Commands
# Test vulnerability prevention (should block pull of vulnerable image)
docker pull harbor.example.com/production/vulnerable-app:latest
# Expected: Error - image blocked due to vulnerabilities
# Verify content trust enforcement
DOCKER_CONTENT_TRUST=0 docker push harbor.example.com/production/unsigned:latest
# Expected: Push rejected due to content trust policy
# Check scan results via API
curl -k "https://harbor.example.com/api/v2.0/projects/production/repositories/myapp/artifacts/v1.0.0/additions/vulnerabilities" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)"
# Audit log check
curl -k "https://harbor.example.com/api/v2.0/audit-logs?page=1&page_size=10" \
-H "Authorization: Basic $(echo -n admin:Harbor12345 | base64)"References
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.9 KB
API Reference: Securing Container Registry with Harbor
Harbor REST API v2.0
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v2.0/projects |
List all projects |
| PUT | /api/v2.0/projects/{name} |
Update project settings |
| GET | /api/v2.0/configurations |
Get system config |
| PUT | /api/v2.0/configurations |
Update system config |
| GET | /api/v2.0/projects/{name}/members |
List project members |
| POST | /api/v2.0/projects/{name}/members |
Add member |
| GET | /api/v2.0/projects/{name}/immutabletagrules |
List tag rules |
| GET | /api/v2.0/audit-logs |
Get audit logs |
| GET | /api/v2.0/projects/{name}/repositories/{repo}/artifacts/{ref}/additions/vulnerabilities |
Get scan results |
Harbor Roles
| Role ID | Name | Permissions |
|---|---|---|
| 1 | ProjectAdmin | Full project control |
| 2 | Maintainer | Push/pull/scan/sign |
| 3 | Developer | Push and pull images |
| 4 | Guest | Pull images only |
| 5 | LimitedGuest | Pull specific repos |
Security Metadata Fields
| Field | Values | Description |
|---|---|---|
auto_scan |
true/false | Scan images on push |
prevent_vul |
true/false | Block vulnerable images |
severity |
critical/high/medium | Block threshold |
enable_content_trust |
true/false | Notary signing |
enable_content_trust_cosign |
true/false | Cosign verification |
public |
true/false | Public project access |
Python Libraries
| Library | Version | Purpose |
|---|---|---|
requests |
>=2.28 | Harbor REST API calls |
json |
stdlib | Parse API responses |
References
- Harbor Documentation: https://goharbor.io/docs/
- Harbor API Spec: https://editor.swagger.io/?url=https://raw.githubusercontent.com/goharbor/harbor/main/api/v2.0/swagger.yaml
- Harbor GitHub: https://github.com/goharbor/harbor
standards.md1.1 KB
Standards Reference - Harbor Container Registry Security
NIST SP 800-190 - Container Security
- Use private registries with TLS
- Scan all images for vulnerabilities before deployment
- Sign images and verify signatures
- Implement RBAC on registry access
- Enable audit logging
CIS Docker Benchmark
- 2.5: Ensure insecure registries are not used
- 4.2: Ensure containers use trusted base images
- 4.4: Ensure images are scanned for vulnerabilities
- 4.5: Ensure Content trust for Docker is enabled
Harbor Security Features
| Feature | Purpose |
|---|---|
| Trivy Scanner | Vulnerability detection in images |
| Content Trust | Image signing with Notary/Cosign |
| RBAC | Role-based project access control |
| Vulnerability Prevention | Block deployment of vulnerable images |
| Immutable Tags | Prevent tag overwriting |
| Audit Logs | Track all registry operations |
| Replication | Secure cross-registry replication |
| Retention Policies | Automated cleanup of old images |
| Robot Accounts | Service-to-service authentication |
| OIDC/LDAP | Enterprise identity integration |
workflows.md1.0 KB
Workflows - Harbor Registry Security
Workflow 1: Secure Image Pipeline
[Build Image] --> [Push to Harbor] --> [Auto-Scan (Trivy)] --> [Sign (Cosign)]
|
+---------+---------+
| |
v v
Vulnerabilities? No vulnerabilities
Block deployment Allow pullWorkflow 2: Registry Hardening
Step 1: Enable HTTPS with valid TLS certificates
Step 2: Configure OIDC/LDAP authentication
Step 3: Create projects with auto-scan enabled
Step 4: Enable vulnerability prevention policy
Step 5: Configure content trust (Cosign)
Step 6: Set immutable tag rules for release tags
Step 7: Configure retention policies
Step 8: Enable audit logging
Step 9: Create robot accounts for CI/CD
Step 10: Test with vulnerability gate checkScripts 2
agent.py6.7 KB
#!/usr/bin/env python3
"""Agent for securing container registry with Harbor.
Audits Harbor registry security configuration including RBAC,
vulnerability scanning policies, content trust, immutable tags,
and OIDC authentication via Harbor REST API v2.0.
"""
import json
import sys
from pathlib import Path
from datetime import datetime
try:
import requests
except ImportError:
requests = None
class HarborSecurityAgent:
"""Audits and hardens Harbor container registry security."""
def __init__(self, harbor_url, username="admin", password="",
output_dir="./harbor_audit"):
self.base_url = harbor_url.rstrip("/") + "/api/v2.0"
self.auth = (username, password)
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.findings = []
def _get(self, path, params=None):
if not requests:
return {"error": "requests library required"}
resp = requests.get(f"{self.base_url}{path}", auth=self.auth,
params=params, verify=True, timeout=15)
try:
return resp.json()
except (json.JSONDecodeError, ValueError):
return {"status": resp.status_code}
def audit_projects(self):
"""Audit all projects for security configuration."""
projects = self._get("/projects", {"page_size": 100})
if isinstance(projects, dict) and "error" in projects:
return projects
results = []
for p in projects:
meta = p.get("metadata", {})
name = p.get("name", "")
issues = []
if meta.get("public") == "true":
issues.append("Project is public - images accessible without auth")
if meta.get("auto_scan") != "true":
issues.append("Auto-scan on push not enabled")
if meta.get("prevent_vul") != "true":
issues.append("Vulnerability prevention not enabled")
if meta.get("enable_content_trust") != "true":
issues.append("Content trust (Notary) not enabled")
if meta.get("enable_content_trust_cosign") != "true":
issues.append("Cosign content trust not enabled")
for issue in issues:
self.findings.append({
"severity": "high" if "public" in issue or "prevent_vul" in issue else "medium",
"project": name,
"issue": issue,
})
results.append({
"name": name,
"public": meta.get("public"),
"auto_scan": meta.get("auto_scan"),
"prevent_vul": meta.get("prevent_vul"),
"content_trust": meta.get("enable_content_trust"),
"cosign": meta.get("enable_content_trust_cosign"),
"issues": issues,
})
return results
def audit_system_config(self):
"""Check system-level security configuration."""
config = self._get("/configurations")
if isinstance(config, dict) and "error" in config:
return config
checks = []
auth_mode = config.get("auth_mode", {}).get("value", "db_auth")
if auth_mode == "db_auth":
self.findings.append({
"severity": "medium",
"issue": "Using local database auth instead of OIDC/LDAP",
})
checks.append({"check": "auth_mode", "value": auth_mode, "status": "WARN"})
else:
checks.append({"check": "auth_mode", "value": auth_mode, "status": "OK"})
self_reg = config.get("self_registration", {}).get("value", True)
if self_reg:
self.findings.append({
"severity": "high",
"issue": "Self-registration enabled - anyone can create accounts",
})
checks.append({"check": "self_registration", "value": self_reg,
"status": "FAIL" if self_reg else "OK"})
return checks
def list_project_members(self, project_name):
"""List members and their roles for a project."""
members = self._get(f"/projects/{project_name}/members")
if isinstance(members, dict) and "error" in members:
return members
role_map = {1: "ProjectAdmin", 2: "Maintainer", 3: "Developer",
4: "Guest", 5: "LimitedGuest"}
return [
{"username": m.get("entity_name", ""), "role": role_map.get(m.get("role_id"), "Unknown")}
for m in (members if isinstance(members, list) else [])
]
def check_immutable_tags(self, project_name):
"""Check immutable tag rules for a project."""
rules = self._get(f"/projects/{project_name}/immutabletagrules")
if not rules or (isinstance(rules, dict) and "error" in rules):
self.findings.append({
"severity": "medium",
"project": project_name,
"issue": "No immutable tag rules configured",
})
return []
return rules
def audit_logs(self, page_size=20):
"""Retrieve recent audit log entries."""
logs = self._get("/audit-logs", {"page_size": page_size})
if isinstance(logs, dict) and "error" in logs:
return logs
return [
{"operation": l.get("operation"), "resource": l.get("resource"),
"username": l.get("username"), "time": l.get("op_time")}
for l in (logs if isinstance(logs, list) else [])
]
def generate_report(self):
projects = self.audit_projects()
sys_config = self.audit_system_config()
report = {
"report_date": datetime.utcnow().isoformat(),
"harbor_url": self.base_url.replace("/api/v2.0", ""),
"projects_audited": len(projects) if isinstance(projects, list) else 0,
"system_config": sys_config,
"projects": projects,
"findings": self.findings,
"finding_count": len(self.findings),
}
out = self.output_dir / "harbor_audit_report.json"
with open(out, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
return report
def main():
if len(sys.argv) < 2:
print("Usage: agent.py <harbor_url> [--user admin] [--pass Harbor12345]")
sys.exit(1)
url = sys.argv[1]
user = "admin"
password = "Harbor12345"
if "--user" in sys.argv:
user = sys.argv[sys.argv.index("--user") + 1]
if "--pass" in sys.argv:
password = sys.argv[sys.argv.index("--pass") + 1]
agent = HarborSecurityAgent(url, user, password)
agent.generate_report()
if __name__ == "__main__":
main()
process.py6.5 KB
#!/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()