mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 05:34:55 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
217 lines
8.4 KiB
Python
217 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
"""pfSense Firewall Configuration Agent - Manages firewall rules via pfSense API."""
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
import requests
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PfSenseAPI:
|
|
"""Client for pfSense REST API (pfsense-api package)."""
|
|
|
|
def __init__(self, base_url, api_key, api_secret):
|
|
self.base_url = base_url.rstrip("/")
|
|
self.session = requests.Session()
|
|
self.session.headers.update({
|
|
"Authorization": f"{api_key} {api_secret}",
|
|
"Content-Type": "application/json",
|
|
})
|
|
self.session.verify = not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true" # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
|
|
def get(self, endpoint):
|
|
resp = self.session.get(f"{self.base_url}/api/v1/{endpoint}", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def post(self, endpoint, data):
|
|
resp = self.session.post(f"{self.base_url}/api/v1/{endpoint}", json=data, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def put(self, endpoint, data):
|
|
resp = self.session.put(f"{self.base_url}/api/v1/{endpoint}", json=data, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def delete(self, endpoint, data=None):
|
|
resp = self.session.delete(f"{self.base_url}/api/v1/{endpoint}", json=data, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def get_firewall_rules(api):
|
|
"""Retrieve all firewall rules from pfSense."""
|
|
result = api.get("firewall/rule")
|
|
rules = result.get("data", [])
|
|
logger.info("Retrieved %d firewall rules", len(rules))
|
|
return rules
|
|
|
|
|
|
def create_firewall_rule(api, interface, action, protocol, source, destination, dst_port, description):
|
|
"""Create a new firewall rule on pfSense."""
|
|
rule = {
|
|
"type": action,
|
|
"interface": interface,
|
|
"ipprotocol": "inet",
|
|
"protocol": protocol,
|
|
"src": source,
|
|
"dst": destination,
|
|
"dstport": dst_port,
|
|
"descr": description,
|
|
"top": False,
|
|
}
|
|
result = api.post("firewall/rule", rule)
|
|
logger.info("Created rule: %s %s %s -> %s:%s (%s)",
|
|
action, protocol, source, destination, dst_port, description)
|
|
return result
|
|
|
|
|
|
def create_lan_to_wan_rules(api):
|
|
"""Create standard LAN-to-WAN egress rules."""
|
|
rules = [
|
|
("pass", "tcp", "LAN net", "any", "80", "Allow HTTP outbound"),
|
|
("pass", "tcp", "LAN net", "any", "443", "Allow HTTPS outbound"),
|
|
("pass", "udp", "LAN net", "any", "53", "Allow DNS outbound"),
|
|
("pass", "tcp", "LAN net", "any", "53", "Allow DNS TCP outbound"),
|
|
("block", "any", "LAN net", "any", "", "Block all other LAN egress"),
|
|
]
|
|
for action, proto, src, dst, port, desc in rules:
|
|
create_firewall_rule(api, "lan", action, proto, src, dst, port, desc)
|
|
logger.info("Created %d LAN-to-WAN rules", len(rules))
|
|
|
|
|
|
def create_dmz_rules(api, dmz_interface="opt1"):
|
|
"""Create DMZ isolation rules allowing only inbound web traffic."""
|
|
rules = [
|
|
("pass", "tcp", "any", "DMZ net", "80", "Allow HTTP to DMZ web servers"),
|
|
("pass", "tcp", "any", "DMZ net", "443", "Allow HTTPS to DMZ web servers"),
|
|
("block", "any", "DMZ net", "LAN net", "", "Block DMZ to LAN"),
|
|
("pass", "tcp", "DMZ net", "any", "80", "Allow DMZ HTTP outbound for updates"),
|
|
("pass", "tcp", "DMZ net", "any", "443", "Allow DMZ HTTPS outbound"),
|
|
("pass", "udp", "DMZ net", "any", "53", "Allow DMZ DNS"),
|
|
]
|
|
for action, proto, src, dst, port, desc in rules:
|
|
create_firewall_rule(api, dmz_interface, action, proto, src, dst, port, desc)
|
|
logger.info("Created %d DMZ rules", len(rules))
|
|
|
|
|
|
def create_guest_isolation_rules(api, guest_interface="opt2"):
|
|
"""Create guest network isolation rules - internet only, no internal access."""
|
|
rules = [
|
|
("block", "any", "GUEST net", "LAN net", "", "Block Guest to LAN"),
|
|
("block", "any", "GUEST net", "DMZ net", "", "Block Guest to DMZ"),
|
|
("block", "any", "GUEST net", "192.168.0.0/16", "", "Block Guest to RFC1918"),
|
|
("block", "any", "GUEST net", "10.0.0.0/8", "", "Block Guest to RFC1918"),
|
|
("block", "any", "GUEST net", "172.16.0.0/12", "", "Block Guest to RFC1918"),
|
|
("pass", "any", "GUEST net", "any", "", "Allow Guest to Internet"),
|
|
]
|
|
for action, proto, src, dst, port, desc in rules:
|
|
create_firewall_rule(api, guest_interface, action, proto, src, dst, port, desc)
|
|
logger.info("Created %d Guest isolation rules", len(rules))
|
|
|
|
|
|
def configure_nat_port_forward(api, interface, external_port, target_ip, target_port, protocol="tcp"):
|
|
"""Create a NAT port forward rule."""
|
|
nat_rule = {
|
|
"interface": interface,
|
|
"protocol": protocol,
|
|
"src": "any",
|
|
"dst": "wanip",
|
|
"dstport": external_port,
|
|
"target": target_ip,
|
|
"local-port": target_port,
|
|
"descr": f"Port forward {external_port} -> {target_ip}:{target_port}",
|
|
}
|
|
result = api.post("firewall/nat/port_forward", nat_rule)
|
|
logger.info("Created NAT: %s:%s -> %s:%s", interface, external_port, target_ip, target_port)
|
|
return result
|
|
|
|
|
|
def audit_firewall_rules(rules):
|
|
"""Audit firewall rules for common security issues."""
|
|
findings = []
|
|
for i, rule in enumerate(rules):
|
|
if rule.get("src") == "any" and rule.get("dst") == "any" and rule.get("type") == "pass":
|
|
findings.append({
|
|
"rule_index": i,
|
|
"finding": "Overly permissive rule: any-to-any pass",
|
|
"severity": "High",
|
|
"rule": rule.get("descr", "No description"),
|
|
})
|
|
if not rule.get("descr"):
|
|
findings.append({
|
|
"rule_index": i,
|
|
"finding": "Rule without description",
|
|
"severity": "Low",
|
|
"rule": f"Rule #{i}",
|
|
})
|
|
if rule.get("disabled"):
|
|
findings.append({
|
|
"rule_index": i,
|
|
"finding": "Disabled rule should be reviewed or removed",
|
|
"severity": "Info",
|
|
"rule": rule.get("descr", "No description"),
|
|
})
|
|
logger.info("Audit: %d findings across %d rules", len(findings), len(rules))
|
|
return findings
|
|
|
|
|
|
def get_firewall_logs(api, limit=100):
|
|
"""Retrieve recent firewall log entries."""
|
|
result = api.get(f"diagnostics/system_log/firewall?limit={limit}")
|
|
return result.get("data", [])
|
|
|
|
|
|
def generate_report(rules, audit_findings, nat_rules=None):
|
|
"""Generate pfSense firewall configuration report."""
|
|
report = {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"total_rules": len(rules),
|
|
"audit_findings": audit_findings,
|
|
"rules_summary": [
|
|
{"interface": r.get("interface", ""), "type": r.get("type", ""),
|
|
"description": r.get("descr", "")}
|
|
for r in rules
|
|
],
|
|
}
|
|
print(f"PFSENSE FIREWALL REPORT: {len(rules)} rules, {len(audit_findings)} findings")
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="pfSense Firewall Configuration Agent")
|
|
parser.add_argument("--url", required=True, help="pfSense base URL (https://192.168.1.1)")
|
|
parser.add_argument("--api-key", required=True, help="pfSense API key")
|
|
parser.add_argument("--api-secret", required=True, help="pfSense API secret")
|
|
parser.add_argument("--audit-only", action="store_true", help="Audit rules without changes")
|
|
parser.add_argument("--setup-dmz", action="store_true", help="Create DMZ rules")
|
|
parser.add_argument("--setup-guest", action="store_true", help="Create guest isolation rules")
|
|
parser.add_argument("--output", default="pfsense_report.json")
|
|
args = parser.parse_args()
|
|
|
|
api = PfSenseAPI(args.url, args.api_key, args.api_secret)
|
|
rules = get_firewall_rules(api)
|
|
findings = audit_firewall_rules(rules)
|
|
|
|
if not args.audit_only:
|
|
if args.setup_dmz:
|
|
create_dmz_rules(api)
|
|
if args.setup_guest:
|
|
create_guest_isolation_rules(api)
|
|
|
|
report = generate_report(rules, findings)
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
logger.info("Report saved to %s", args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|