170 lines
14 KiB
Python
170 lines
14 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
CIS Runner Stub — Generates placeholder reports.
|
||
Replace this with the actual MCP server integration later.
|
||
|
||
Usage: python3 cis_runner.py --config /path/to/oci/config --output /path/to/dir --tenancy-name mytenancy [--regions r1,r2]
|
||
"""
|
||
import argparse, json, datetime, sys
|
||
from pathlib import Path
|
||
|
||
CHECKS = {
|
||
"1.1": {"id":"IAM-1","section":"Identity and Access Management","title":"Ensure service level admins are created to manage resources of particular service"},
|
||
"1.2": {"id":"IAM-2","section":"Identity and Access Management","title":"Ensure permissions on all resources are given only to the tenancy administrator group"},
|
||
"1.3": {"id":"IAM-3","section":"Identity and Access Management","title":"Ensure IAM administrators cannot update tenancy Administrators group"},
|
||
"1.4": {"id":"IAM-4","section":"Identity and Access Management","title":"Ensure IAM password policy requires minimum length of 14 or greater"},
|
||
"1.5": {"id":"IAM-5","section":"Identity and Access Management","title":"Ensure IAM password policy expires passwords within 365 days"},
|
||
"1.6": {"id":"IAM-6","section":"Identity and Access Management","title":"Ensure IAM password policy prevents password reuse"},
|
||
"1.7": {"id":"IAM-7","section":"Identity and Access Management","title":"Ensure MFA is enabled for all users with a console password"},
|
||
"1.8": {"id":"IAM-8","section":"Identity and Access Management","title":"Ensure user API keys rotate within 90 days"},
|
||
"1.9": {"id":"IAM-9","section":"Identity and Access Management","title":"Ensure user customer secret keys rotate within 90 days"},
|
||
"1.10": {"id":"IAM-10","section":"Identity and Access Management","title":"Ensure user auth tokens rotate within 90 days"},
|
||
"1.11": {"id":"IAM-11","section":"Identity and Access Management","title":"Ensure user IAM Database Passwords rotate within 90 days"},
|
||
"1.12": {"id":"IAM-12","section":"Identity and Access Management","title":"Ensure API keys are not created for tenancy administrator users"},
|
||
"1.13": {"id":"IAM-13","section":"Identity and Access Management","title":"Ensure all OCI IAM user accounts have a valid and current email address"},
|
||
"1.14": {"id":"IAM-14","section":"Identity and Access Management","title":"Ensure Instance Principal authentication is used"},
|
||
"1.15": {"id":"IAM-15","section":"Identity and Access Management","title":"Ensure storage service-level admins cannot delete resources they manage"},
|
||
"1.16": {"id":"IAM-16","section":"Identity and Access Management","title":"Ensure OCI IAM credentials unused for 45 days or more are disabled"},
|
||
"1.17": {"id":"IAM-17","section":"Identity and Access Management","title":"Ensure there is only one active API Key per user"},
|
||
"2.1": {"id":"NTW-1","section":"Networking","title":"Ensure no security lists allow ingress from 0.0.0.0/0 to port 22"},
|
||
"2.2": {"id":"NTW-2","section":"Networking","title":"Ensure no security lists allow ingress from 0.0.0.0/0 to port 3389"},
|
||
"2.3": {"id":"NTW-3","section":"Networking","title":"Ensure no network security groups allow ingress from 0.0.0.0/0 to port 22"},
|
||
"2.4": {"id":"NTW-4","section":"Networking","title":"Ensure no network security groups allow ingress from 0.0.0.0/0 to port 3389"},
|
||
"2.5": {"id":"NTW-5","section":"Networking","title":"Ensure the default security list of every VCN restricts all traffic except ICMP"},
|
||
"2.6": {"id":"NTW-6","section":"Networking","title":"Ensure Oracle Integration Cloud (OIC) access is restricted"},
|
||
"2.7": {"id":"NTW-7","section":"Networking","title":"Ensure Oracle Analytics Cloud (OAC) access is restricted or deployed within a VCN"},
|
||
"2.8": {"id":"NTW-8","section":"Networking","title":"Ensure Oracle Autonomous Shared Database access is restricted or deployed within a VCN"},
|
||
"3.1": {"id":"COM-1","section":"Compute","title":"Ensure Compute Instance Legacy Metadata service endpoint is disabled"},
|
||
"3.2": {"id":"COM-2","section":"Compute","title":"Ensure Secure Boot is enabled on Compute Instance"},
|
||
"3.3": {"id":"COM-3","section":"Compute","title":"Ensure In-transit Encryption is enabled on Compute Instance"},
|
||
"4.1": {"id":"LAM-1","section":"Logging and Monitoring","title":"Ensure default tags are used on resources"},
|
||
"4.2": {"id":"LAM-2","section":"Logging and Monitoring","title":"Create at least one notification topic and subscription"},
|
||
"4.3": {"id":"LAM-3","section":"Logging and Monitoring","title":"Ensure a notification is configured for Identity Provider changes"},
|
||
"4.4": {"id":"LAM-4","section":"Logging and Monitoring","title":"Ensure a notification is configured for IdP group mapping changes"},
|
||
"4.5": {"id":"LAM-5","section":"Logging and Monitoring","title":"Ensure a notification is configured for IAM group changes"},
|
||
"4.6": {"id":"LAM-6","section":"Logging and Monitoring","title":"Ensure a notification is configured for IAM policy changes"},
|
||
"4.7": {"id":"LAM-7","section":"Logging and Monitoring","title":"Ensure a notification is configured for user changes"},
|
||
"4.8": {"id":"LAM-8","section":"Logging and Monitoring","title":"Ensure a notification is configured for VCN changes"},
|
||
"4.9": {"id":"LAM-9","section":"Logging and Monitoring","title":"Ensure a notification is configured for changes to route tables"},
|
||
"4.10": {"id":"LAM-10","section":"Logging and Monitoring","title":"Ensure a notification is configured for security list changes"},
|
||
"4.11": {"id":"LAM-11","section":"Logging and Monitoring","title":"Ensure a notification is configured for network security group changes"},
|
||
"4.12": {"id":"LAM-12","section":"Logging and Monitoring","title":"Ensure a notification is configured for changes to network gateways"},
|
||
"4.13": {"id":"LAM-13","section":"Logging and Monitoring","title":"Ensure VCN flow logging is enabled for all subnets"},
|
||
"4.14": {"id":"LAM-14","section":"Logging and Monitoring","title":"Ensure Cloud Guard is enabled in the root compartment"},
|
||
"4.15": {"id":"LAM-15","section":"Logging and Monitoring","title":"Ensure a notification is configured for Cloud Guard problems detected"},
|
||
"4.16": {"id":"LAM-16","section":"Logging and Monitoring","title":"Ensure customer created CMK is rotated at least annually"},
|
||
"4.17": {"id":"LAM-17","section":"Logging and Monitoring","title":"Ensure write level Object Storage logging is enabled for all buckets"},
|
||
"4.18": {"id":"LAM-18","section":"Logging and Monitoring","title":"Ensure a notification is configured for Local OCI User Authentication"},
|
||
"5.1.1":{"id":"STO-1-1","section":"Storage - Object Storage","title":"Ensure no Object Storage buckets are publicly visible"},
|
||
"5.1.2":{"id":"STO-1-2","section":"Storage - Object Storage","title":"Ensure Object Storage Buckets are encrypted with a CMK"},
|
||
"5.1.3":{"id":"STO-1-3","section":"Storage - Object Storage","title":"Ensure Versioning is Enabled for Object Storage Buckets"},
|
||
"5.2.1":{"id":"STO-2-1","section":"Storage - Block Volumes","title":"Ensure Block Volumes are encrypted with Customer Managed Keys"},
|
||
"5.2.2":{"id":"STO-2-2","section":"Storage - Block Volumes","title":"Ensure Boot Volumes are encrypted with Customer Managed Key"},
|
||
"5.3.1":{"id":"STO-3-1","section":"Storage - File Storage Service","title":"Ensure File Storage Systems are encrypted with Customer Managed Keys"},
|
||
"6.1": {"id":"AM-1","section":"Asset Management","title":"Create at least one compartment in your tenancy"},
|
||
"6.2": {"id":"AM-2","section":"Asset Management","title":"Ensure no resources are created in the root compartment"},
|
||
}
|
||
|
||
def gen_html(findings, tenancy, output):
|
||
now = datetime.datetime.now()
|
||
sections = {}
|
||
for cid, ck in findings.items():
|
||
sec = ck["section"]
|
||
sections.setdefault(sec, {"total":0,"passed":0,"failed":0})
|
||
sections[sec]["total"] += 1
|
||
if ck["status"] == "PASS": sections[sec]["passed"] += 1
|
||
elif ck["status"] == "FAIL": sections[sec]["failed"] += 1
|
||
tot = sum(s["total"] for s in sections.values())
|
||
pas = sum(s["passed"] for s in sections.values())
|
||
fai = sum(s["failed"] for s in sections.values())
|
||
sec_rows = "".join(f'<tr><td><strong>{s}</strong></td><td>{v["total"]}</td>'
|
||
f'<td style="color:#dc2626;font-weight:700">{v["failed"]}</td>'
|
||
f'<td style="color:#16a34a;font-weight:700">{v["passed"]}</td></tr>' for s,v in sorted(sections.items()))
|
||
det_rows = ""
|
||
for cid in sorted(findings.keys(), key=lambda x: [int(p) if p.isdigit() else p for p in x.replace('.',' ').split()]):
|
||
ck = findings[cid]; st = ck["status"]
|
||
cls = "pass" if st=="PASS" else "fail" if st=="FAIL" else "review"
|
||
det_rows += f'<tr><td>{ck["id"]}</td><td>{cid}</td><td>{ck["title"]}</td><td><span class="b b-{cls}">{st}</span></td><td>{ck["section"]}</td></tr>'
|
||
html = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Cloud Security Assessment - {tenancy}</title><style>
|
||
*{{margin:0;padding:0;box-sizing:border-box}}body{{font-family:'Segoe UI',system-ui,sans-serif;background:#f8f9fa;color:#1a1a2e;line-height:1.6}}
|
||
.c{{max-width:1200px;margin:0 auto;padding:2rem}}h1{{font-size:2.5rem;color:#1a1a2e;margin-bottom:.5rem}}
|
||
h2{{color:#2d3748;margin:2rem 0 1rem;padding-bottom:.5rem;border-bottom:2px solid #e2e8f0}}
|
||
.m{{color:#718096;margin-bottom:2rem}}.d{{background:#fff;border:1px solid #e2e8f0;border-radius:8px;padding:1.5rem;margin:2rem 0}}
|
||
.d h3{{color:#c53030;margin-bottom:.5rem}}.d p{{font-size:.85rem;color:#4a5568}}
|
||
table{{width:100%;border-collapse:collapse;background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.1);margin:1rem 0}}
|
||
th{{background:#1a365d;color:#fff;padding:.75rem 1rem;text-align:left;font-size:.8rem;text-transform:uppercase;letter-spacing:.05em}}
|
||
td{{padding:.75rem 1rem;border-bottom:1px solid #e2e8f0}}tr:hover{{background:#f7fafc}}
|
||
.b{{padding:.25rem .75rem;border-radius:12px;font-size:.8rem;font-weight:600}}
|
||
.b-pass{{background:#c6f6d5;color:#22543d}}.b-fail{{background:#fed7d7;color:#9b2c2c}}.b-review{{background:#fefcbf;color:#744210}}
|
||
.sc{{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin:1.5rem 0}}
|
||
.sd{{background:#fff;border-radius:8px;padding:1.5rem;box-shadow:0 1px 3px rgba(0,0,0,.1);text-align:center}}
|
||
.sd .n{{font-size:2.5rem;font-weight:700}}.sd .l{{color:#718096;font-size:.9rem}}
|
||
.ft{{text-align:center;color:#a0aec0;margin-top:3rem;padding:1rem;border-top:1px solid #e2e8f0}}
|
||
</style></head><body><div class="c">
|
||
<h1>Cloud Security Assessment</h1>
|
||
<div class="m"><strong>Tenancy:</strong> {tenancy}<br>Tenancy – Cloud Infrastructure<br><br>
|
||
{now.strftime('%B, %Y')}, Version [1.0]<br>Extract date: {now.isoformat()[:19]}</div>
|
||
<div class="d"><h3>Disclaimer</h3>
|
||
<p>This document, in any form, software or printed matter, contains proprietary information that is the exclusive property of Oracle.
|
||
Your access to and use of this confidential material is subject to the terms and conditions of your Oracle software license and service agreement,
|
||
which has been executed and with which you agree to comply.</p>
|
||
<p style="margin-top:.5rem">This document is for informational purposes only and is intended solely to assist you in planning for the implementation and
|
||
upgrade of the product features described. It is not a commitment to deliver any material, code, or functionality.</p></div>
|
||
<h2>Findings Overview</h2><p style="color:#718096">Resumo por domínio (Section)</p>
|
||
<div class="sc"><div class="sd"><div class="n" style="color:#2d3748">{tot}</div><div class="l">Total Controls</div></div>
|
||
<div class="sd"><div class="n" style="color:#16a34a">{pas}</div><div class="l">Passed</div></div>
|
||
<div class="sd"><div class="n" style="color:#dc2626">{fai}</div><div class="l">Failed</div></div></div>
|
||
<table><thead><tr><th>Domains</th><th>Total Controls</th><th>Failed</th><th>Passed</th></tr></thead><tbody>{sec_rows}</tbody></table>
|
||
<p style="color:#718096;margin-top:.5rem">Total: {tot} · Passed: {pas} · Failed: {fai}</p>
|
||
<h2>Detailed Findings</h2>
|
||
<table><thead><tr><th>ID</th><th>Check #</th><th>Title</th><th>Status</th><th>Section</th></tr></thead><tbody>{det_rows}</tbody></table>
|
||
<div class="ft">Generated by OCI CIS AI Agent · CIS OCI Foundations Benchmark 3.0 · {now.strftime('%Y-%m-%d %H:%M:%S')}</div>
|
||
</div></body></html>"""
|
||
Path(output).write_text(html, encoding="utf-8")
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--config", required=True)
|
||
ap.add_argument("--output", required=True)
|
||
ap.add_argument("--tenancy-name", default="Tenancy")
|
||
ap.add_argument("--regions", default=None)
|
||
args = ap.parse_args()
|
||
out = Path(args.output); out.mkdir(parents=True, exist_ok=True)
|
||
|
||
# TODO: Replace with MCP server call
|
||
# For now generate a stub report showing all checks as REVIEW
|
||
print(f"[CIS Runner] Config: {args.config}")
|
||
print(f"[CIS Runner] Output: {args.output}")
|
||
print(f"[CIS Runner] Tenancy: {args.tenancy_name}")
|
||
|
||
# Check if OCI config file exists
|
||
if not Path(args.config).exists():
|
||
print(f"[ERROR] Config not found: {args.config}", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
findings = {}
|
||
for cid, ck in CHECKS.items():
|
||
findings[cid] = {**ck, "status": "REVIEW", "findings": [], "total": []}
|
||
|
||
report = {
|
||
"tenancy": args.tenancy_name,
|
||
"generated_at": datetime.datetime.now().isoformat(),
|
||
"benchmark": "CIS OCI Foundations Benchmark 3.0",
|
||
"regions": args.regions.split(",") if args.regions else ["all"],
|
||
"summary": {
|
||
"total": len(findings),
|
||
"passed": sum(1 for f in findings.values() if f["status"]=="PASS"),
|
||
"failed": sum(1 for f in findings.values() if f["status"]=="FAIL"),
|
||
"review": sum(1 for f in findings.values() if f["status"]=="REVIEW"),
|
||
},
|
||
"findings": findings,
|
||
}
|
||
|
||
(out / "report.json").write_text(json.dumps(report, indent=2, default=str))
|
||
gen_html(findings, args.tenancy_name, str(out / "report.html"))
|
||
print("[CIS Runner] Reports generated successfully")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|