mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-09 03:43:20 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tailscale Zero Trust VPN Management and Monitoring.
|
||||
|
||||
Manages Tailscale deployment, ACL generation, network health monitoring,
|
||||
and compliance reporting for zero trust mesh VPN infrastructure.
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import datetime
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class TailscaleNode:
|
||||
hostname: str
|
||||
ip4: str
|
||||
ip6: str = ""
|
||||
os: str = ""
|
||||
online: bool = False
|
||||
tags: list = field(default_factory=list)
|
||||
key_expiry: str = ""
|
||||
last_seen: str = ""
|
||||
exit_node: bool = False
|
||||
subnet_routes: list = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ACLRule:
|
||||
action: str # "accept" or "deny"
|
||||
src: list
|
||||
dst: list
|
||||
description: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class ACLGroup:
|
||||
name: str
|
||||
members: list
|
||||
|
||||
|
||||
class TailscaleACLGenerator:
|
||||
"""Generate and validate Tailscale ACL configurations."""
|
||||
|
||||
def __init__(self):
|
||||
self.groups: dict[str, list] = {}
|
||||
self.tag_owners: dict[str, list] = {}
|
||||
self.acls: list[dict] = []
|
||||
self.ssh_rules: list[dict] = []
|
||||
self.auto_approvers: dict = {"routes": {}, "exitNode": []}
|
||||
self.node_attrs: list[dict] = []
|
||||
|
||||
def add_group(self, name: str, members: list):
|
||||
if not name.startswith("group:"):
|
||||
name = f"group:{name}"
|
||||
self.groups[name] = members
|
||||
|
||||
def add_tag(self, tag: str, owners: list):
|
||||
if not tag.startswith("tag:"):
|
||||
tag = f"tag:{tag}"
|
||||
self.tag_owners[tag] = owners
|
||||
|
||||
def add_acl_rule(self, src: list, dst: list, action: str = "accept"):
|
||||
self.acls.append({"action": action, "src": src, "dst": dst})
|
||||
|
||||
def add_ssh_rule(self, src: list, dst: list, users: list, action: str = "check"):
|
||||
self.ssh_rules.append({
|
||||
"action": action,
|
||||
"src": src,
|
||||
"dst": dst,
|
||||
"users": users,
|
||||
})
|
||||
|
||||
def add_auto_approver_route(self, cidr: str, approvers: list):
|
||||
self.auto_approvers["routes"][cidr] = approvers
|
||||
|
||||
def add_auto_approver_exit_node(self, approvers: list):
|
||||
self.auto_approvers["exitNode"] = approvers
|
||||
|
||||
def generate_policy(self) -> dict:
|
||||
policy = {}
|
||||
if self.groups:
|
||||
policy["groups"] = self.groups
|
||||
if self.tag_owners:
|
||||
policy["tagOwners"] = self.tag_owners
|
||||
if self.acls:
|
||||
policy["acls"] = self.acls
|
||||
if self.ssh_rules:
|
||||
policy["ssh"] = self.ssh_rules
|
||||
if self.auto_approvers["routes"] or self.auto_approvers["exitNode"]:
|
||||
policy["autoApprovers"] = self.auto_approvers
|
||||
if self.node_attrs:
|
||||
policy["nodeAttrs"] = self.node_attrs
|
||||
return policy
|
||||
|
||||
def export_policy(self, output_path: str):
|
||||
policy = self.generate_policy()
|
||||
path = Path(output_path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(path, "w") as f:
|
||||
json.dump(policy, f, indent=2)
|
||||
return policy
|
||||
|
||||
def validate_policy(self) -> list:
|
||||
"""Validate ACL policy for common issues."""
|
||||
issues = []
|
||||
policy = self.generate_policy()
|
||||
|
||||
# Check for empty ACLs
|
||||
if not policy.get("acls"):
|
||||
issues.append("WARNING: No ACL rules defined - all traffic will be denied")
|
||||
|
||||
# Check for overly permissive rules
|
||||
for i, rule in enumerate(self.acls):
|
||||
if "*" in rule.get("src", []) and "*" in rule.get("dst", []):
|
||||
issues.append(f"CRITICAL: Rule {i} allows all-to-all access")
|
||||
for dst in rule.get("dst", []):
|
||||
if dst.endswith(":*"):
|
||||
issues.append(f"WARNING: Rule {i} allows all ports to {dst}")
|
||||
|
||||
# Check groups reference valid members
|
||||
for group_name, members in self.groups.items():
|
||||
if not members:
|
||||
issues.append(f"WARNING: Group {group_name} has no members")
|
||||
|
||||
# Check tag owners exist
|
||||
for tag, owners in self.tag_owners.items():
|
||||
for owner in owners:
|
||||
if owner.startswith("group:") and owner not in self.groups:
|
||||
issues.append(f"ERROR: Tag {tag} references undefined group {owner}")
|
||||
|
||||
# Check SSH rules
|
||||
for i, rule in enumerate(self.ssh_rules):
|
||||
if "root" in rule.get("users", []) and rule.get("action") != "check":
|
||||
issues.append(f"WARNING: SSH rule {i} allows root access without re-auth check")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
class TailscaleMonitor:
|
||||
"""Monitor Tailscale network health and compliance."""
|
||||
|
||||
def __init__(self):
|
||||
self.nodes: list[TailscaleNode] = []
|
||||
|
||||
def get_status(self) -> dict:
|
||||
"""Get current Tailscale status via CLI."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["tailscale", "status", "--json"],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return json.loads(result.stdout)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError, json.JSONDecodeError):
|
||||
pass
|
||||
return {}
|
||||
|
||||
def parse_nodes(self, status: dict) -> list[TailscaleNode]:
|
||||
"""Parse Tailscale status into node objects."""
|
||||
self.nodes = []
|
||||
peers = status.get("Peer", {})
|
||||
for peer_id, peer_data in peers.items():
|
||||
node = TailscaleNode(
|
||||
hostname=peer_data.get("HostName", "unknown"),
|
||||
ip4=peer_data.get("TailscaleIPs", [""])[0] if peer_data.get("TailscaleIPs") else "",
|
||||
ip6=peer_data.get("TailscaleIPs", ["", ""])[1] if len(peer_data.get("TailscaleIPs", [])) > 1 else "",
|
||||
os=peer_data.get("OS", ""),
|
||||
online=peer_data.get("Online", False),
|
||||
tags=peer_data.get("Tags", []),
|
||||
key_expiry=peer_data.get("KeyExpiry", ""),
|
||||
last_seen=peer_data.get("LastSeen", ""),
|
||||
exit_node=peer_data.get("ExitNode", False),
|
||||
)
|
||||
self.nodes.append(node)
|
||||
return self.nodes
|
||||
|
||||
def check_health(self) -> dict:
|
||||
"""Run health checks on the tailnet."""
|
||||
report = {
|
||||
"timestamp": datetime.datetime.now().isoformat(),
|
||||
"total_nodes": len(self.nodes),
|
||||
"online_nodes": sum(1 for n in self.nodes if n.online),
|
||||
"offline_nodes": sum(1 for n in self.nodes if not n.online),
|
||||
"expiring_keys": [],
|
||||
"untagged_nodes": [],
|
||||
"exit_nodes": [],
|
||||
"issues": [],
|
||||
}
|
||||
|
||||
for node in self.nodes:
|
||||
# Check for expiring keys
|
||||
if node.key_expiry:
|
||||
try:
|
||||
expiry = datetime.datetime.fromisoformat(node.key_expiry.replace("Z", "+00:00"))
|
||||
days_until = (expiry - datetime.datetime.now(datetime.timezone.utc)).days
|
||||
if days_until < 30:
|
||||
report["expiring_keys"].append({
|
||||
"hostname": node.hostname,
|
||||
"expires_in_days": days_until,
|
||||
})
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# Check for untagged nodes
|
||||
if not node.tags:
|
||||
report["untagged_nodes"].append(node.hostname)
|
||||
|
||||
# Track exit nodes
|
||||
if node.exit_node:
|
||||
report["exit_nodes"].append(node.hostname)
|
||||
|
||||
# Generate issues
|
||||
if report["offline_nodes"] > 0:
|
||||
report["issues"].append(
|
||||
f"{report['offline_nodes']} nodes offline"
|
||||
)
|
||||
if report["expiring_keys"]:
|
||||
report["issues"].append(
|
||||
f"{len(report['expiring_keys'])} nodes with keys expiring within 30 days"
|
||||
)
|
||||
if report["untagged_nodes"]:
|
||||
report["issues"].append(
|
||||
f"{len(report['untagged_nodes'])} nodes without tags (ungoverned by ACLs)"
|
||||
)
|
||||
|
||||
return report
|
||||
|
||||
def generate_compliance_report(self) -> dict:
|
||||
"""Generate zero trust compliance report for the tailnet."""
|
||||
report = {
|
||||
"report_date": datetime.datetime.now().isoformat(),
|
||||
"zero_trust_checks": {
|
||||
"encryption": {
|
||||
"status": "PASS",
|
||||
"detail": "All connections use WireGuard end-to-end encryption",
|
||||
},
|
||||
"identity_based_access": {
|
||||
"status": "PASS" if all(n.tags for n in self.nodes) else "FAIL",
|
||||
"detail": "All nodes should have identity tags for ACL enforcement",
|
||||
},
|
||||
"least_privilege": {
|
||||
"status": "REVIEW",
|
||||
"detail": "ACL policy review required - validate minimum necessary access",
|
||||
},
|
||||
"continuous_verification": {
|
||||
"status": "PASS" if not any(
|
||||
n.key_expiry == "" for n in self.nodes
|
||||
) else "WARNING",
|
||||
"detail": "Key expiry should be enabled for all non-server nodes",
|
||||
},
|
||||
"device_trust": {
|
||||
"status": "REVIEW",
|
||||
"detail": "Verify device authorization and Network Lock status",
|
||||
},
|
||||
},
|
||||
}
|
||||
return report
|
||||
|
||||
|
||||
def generate_example_policy():
|
||||
"""Generate an example zero trust ACL policy for Tailscale."""
|
||||
gen = TailscaleACLGenerator()
|
||||
|
||||
# Define groups
|
||||
gen.add_group("engineering", ["eng1@company.com", "eng2@company.com"])
|
||||
gen.add_group("sre", ["sre1@company.com", "sre2@company.com"])
|
||||
gen.add_group("security", ["sec1@company.com"])
|
||||
gen.add_group("management", ["mgr1@company.com"])
|
||||
|
||||
# Define tags
|
||||
gen.add_tag("production", ["group:sre"])
|
||||
gen.add_tag("staging", ["group:engineering", "group:sre"])
|
||||
gen.add_tag("development", ["group:engineering"])
|
||||
gen.add_tag("database", ["group:sre"])
|
||||
gen.add_tag("monitoring", ["group:sre", "group:security"])
|
||||
gen.add_tag("ci-runner", ["group:sre"])
|
||||
|
||||
# ACL rules - zero trust, least privilege
|
||||
gen.add_acl_rule(
|
||||
src=["group:engineering"],
|
||||
dst=["tag:development:*", "tag:staging:443,8080"]
|
||||
)
|
||||
gen.add_acl_rule(
|
||||
src=["group:sre"],
|
||||
dst=["tag:production:22,443,8080", "tag:staging:*", "tag:database:5432,3306"]
|
||||
)
|
||||
gen.add_acl_rule(
|
||||
src=["group:security"],
|
||||
dst=["tag:monitoring:443,9090", "tag:production:443"]
|
||||
)
|
||||
gen.add_acl_rule(
|
||||
src=["tag:ci-runner"],
|
||||
dst=["tag:staging:443,8080", "tag:production:443"]
|
||||
)
|
||||
|
||||
# SSH rules with re-authentication
|
||||
gen.add_ssh_rule(
|
||||
src=["group:sre"],
|
||||
dst=["tag:production"],
|
||||
users=["admin"],
|
||||
action="check" # Requires re-auth, records session
|
||||
)
|
||||
gen.add_ssh_rule(
|
||||
src=["group:engineering"],
|
||||
dst=["tag:development"],
|
||||
users=["autogroup:nonroot"],
|
||||
action="accept"
|
||||
)
|
||||
|
||||
# Auto-approvers for subnet routes
|
||||
gen.add_auto_approver_route("10.0.0.0/16", ["group:sre"])
|
||||
gen.add_auto_approver_exit_node(["group:sre"])
|
||||
|
||||
# Validate
|
||||
issues = gen.validate_policy()
|
||||
if issues:
|
||||
print("Policy Validation Issues:")
|
||||
for issue in issues:
|
||||
print(f" - {issue}")
|
||||
else:
|
||||
print("Policy validation: PASSED")
|
||||
|
||||
# Export
|
||||
policy = gen.export_policy("tailscale_acl_policy.json")
|
||||
print(f"\nGenerated ACL policy with:")
|
||||
print(f" Groups: {len(gen.groups)}")
|
||||
print(f" Tags: {len(gen.tag_owners)}")
|
||||
print(f" ACL Rules: {len(gen.acls)}")
|
||||
print(f" SSH Rules: {len(gen.ssh_rules)}")
|
||||
print(f"\nPolicy saved to: tailscale_acl_policy.json")
|
||||
print(f"\nPolicy preview:")
|
||||
print(json.dumps(policy, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_example_policy()
|
||||
Reference in New Issue
Block a user