mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 05:34:55 +03:00
c21af3347e
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
153 lines
5.0 KiB
Python
153 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Tailscale zero trust VPN deployment audit agent."""
|
|
|
|
import json
|
|
import sys
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
try:
|
|
import requests
|
|
except ImportError:
|
|
print("Install: pip install requests")
|
|
sys.exit(1)
|
|
|
|
|
|
class TailscaleClient:
|
|
"""Client for Tailscale API v2."""
|
|
|
|
def __init__(self, api_key, tailnet="-"):
|
|
self.base_url = "https://api.tailscale.com/api/v2"
|
|
self.tailnet = tailnet
|
|
self.headers = {"Authorization": f"Bearer {api_key}"}
|
|
|
|
def _get(self, path):
|
|
resp = requests.get(f"{self.base_url}{path}",
|
|
headers=self.headers, timeout=15)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def list_devices(self):
|
|
return self._get(f"/tailnet/{self.tailnet}/devices").get("devices", [])
|
|
|
|
def get_acl(self):
|
|
return self._get(f"/tailnet/{self.tailnet}/acl")
|
|
|
|
def list_dns(self):
|
|
return self._get(f"/tailnet/{self.tailnet}/dns/nameservers")
|
|
|
|
def get_key_expiry_disabled(self):
|
|
return self._get(f"/tailnet/{self.tailnet}/keys")
|
|
|
|
def list_webhooks(self):
|
|
return self._get(f"/tailnet/{self.tailnet}/webhooks").get("webhooks", [])
|
|
|
|
|
|
def audit_devices(devices):
|
|
"""Audit device compliance: OS versions, key expiry, last seen."""
|
|
findings = []
|
|
now = datetime.utcnow()
|
|
for dev in devices:
|
|
hostname = dev.get("hostname", "unknown")
|
|
if dev.get("keyExpiryDisabled", False):
|
|
findings.append({
|
|
"device": hostname,
|
|
"issue": "Key expiry disabled — device never requires re-authentication",
|
|
"severity": "HIGH",
|
|
})
|
|
if not dev.get("updateAvailable", False) is False and dev.get("updateAvailable"):
|
|
findings.append({
|
|
"device": hostname,
|
|
"issue": "Tailscale update available but not installed",
|
|
"severity": "MEDIUM",
|
|
})
|
|
os_name = dev.get("os", "")
|
|
if dev.get("blocksIncomingConnections", False):
|
|
findings.append({
|
|
"device": hostname,
|
|
"issue": "Device blocks incoming connections (shields up mode)",
|
|
"severity": "INFO",
|
|
})
|
|
return findings
|
|
|
|
|
|
def audit_acl(acl_data):
|
|
"""Check ACL policy for overly permissive rules."""
|
|
findings = []
|
|
acls = acl_data.get("acls", []) if isinstance(acl_data, dict) else []
|
|
for i, rule in enumerate(acls):
|
|
src = rule.get("src", [])
|
|
dst = rule.get("dst", [])
|
|
if "*" in src and any("*:*" in d for d in dst):
|
|
findings.append({
|
|
"rule_index": i,
|
|
"issue": "Allow-all rule: src=* dst=*:* — no zero trust segmentation",
|
|
"severity": "CRITICAL",
|
|
})
|
|
ssh_rules = acl_data.get("ssh", []) if isinstance(acl_data, dict) else []
|
|
for rule in ssh_rules:
|
|
if rule.get("action") == "accept" and "*" in rule.get("src", []):
|
|
findings.append({
|
|
"rule": "SSH",
|
|
"issue": "SSH access allowed from all users",
|
|
"severity": "HIGH",
|
|
})
|
|
return findings
|
|
|
|
|
|
def run_audit(api_key, tailnet):
|
|
"""Execute Tailscale zero trust audit."""
|
|
print(f"\n{'='*60}")
|
|
print(f" TAILSCALE ZERO TRUST VPN AUDIT")
|
|
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
|
|
print(f"{'='*60}\n")
|
|
|
|
client = TailscaleClient(api_key, tailnet)
|
|
report = {}
|
|
|
|
devices = client.list_devices()
|
|
report["total_devices"] = len(devices)
|
|
print(f"--- DEVICES ({len(devices)}) ---")
|
|
for d in devices[:15]:
|
|
print(f" {d.get('hostname','')}: {d.get('os','')}/{d.get('clientVersion','')} "
|
|
f"({'online' if d.get('online') else 'offline'})")
|
|
|
|
dev_findings = audit_devices(devices)
|
|
report["device_findings"] = dev_findings
|
|
print(f"\n--- DEVICE FINDINGS ({len(dev_findings)}) ---")
|
|
for f in dev_findings[:10]:
|
|
print(f" [{f['severity']}] {f['device']}: {f['issue']}")
|
|
|
|
acl_data = client.get_acl()
|
|
acl_findings = audit_acl(acl_data)
|
|
report["acl_findings"] = acl_findings
|
|
print(f"\n--- ACL POLICY FINDINGS ({len(acl_findings)}) ---")
|
|
for f in acl_findings[:10]:
|
|
print(f" [{f['severity']}] {f['issue']}")
|
|
|
|
dns = client.list_dns()
|
|
report["dns_config"] = dns
|
|
print(f"\n--- DNS CONFIG ---")
|
|
for ns in dns.get("dns", []):
|
|
print(f" Nameserver: {ns}")
|
|
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Tailscale Zero Trust Audit")
|
|
parser.add_argument("--api-key", required=True, help="Tailscale API key")
|
|
parser.add_argument("--tailnet", default="-", help="Tailnet name (default: current)")
|
|
parser.add_argument("--output", help="Save report to JSON file")
|
|
args = parser.parse_args()
|
|
|
|
report = run_audit(args.api_key, args.tailnet)
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
print(f"\n[+] Report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|