Files
T
mukul975 27c6414ca5 Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills:
- scripts/agent.py: 80-150 line Python agents using real libraries (impacket,
  boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.)
- references/api-reference.md: real API documentation with method signatures
- LICENSE: MIT license for all skill folders
2026-03-10 21:02:12 +01:00

141 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""AWS IAM permission boundary management agent using boto3."""
import argparse
import json
import logging
import os
import sys
from datetime import datetime
from typing import Dict, List, Optional
try:
import boto3
from botocore.exceptions import ClientError
except ImportError:
sys.exit("boto3 required: pip install boto3")
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
def get_iam_client(profile: str = "", region: str = "us-east-1"):
"""Create IAM client with optional profile."""
session = boto3.Session(profile_name=profile) if profile else boto3.Session()
return session.client("iam", region_name=region)
def create_permission_boundary(client, policy_name: str, allowed_services: List[str],
allowed_regions: List[str] = None) -> dict:
"""Create a permission boundary policy restricting services and regions."""
statements = [{
"Sid": "AllowedServices",
"Effect": "Allow",
"Action": [f"{svc}:*" for svc in allowed_services],
"Resource": "*",
}]
if allowed_regions:
statements[0]["Condition"] = {
"StringEquals": {"aws:RequestedRegion": allowed_regions}
}
statements.append({
"Sid": "DenyBoundaryChanges",
"Effect": "Deny",
"Action": ["iam:DeleteRolePermissionsBoundary", "iam:PutRolePermissionsBoundary"],
"Resource": "*",
})
policy_doc = {"Version": "2012-10-17", "Statement": statements}
try:
resp = client.create_policy(
PolicyName=policy_name,
PolicyDocument=json.dumps(policy_doc),
Description=f"Permission boundary: {', '.join(allowed_services)}",
)
arn = resp["Policy"]["Arn"]
logger.info("Created boundary policy: %s", arn)
return {"policy_arn": arn, "policy_document": policy_doc}
except ClientError as exc:
return {"error": str(exc)}
def attach_boundary_to_role(client, role_name: str, boundary_arn: str) -> dict:
"""Attach permission boundary to an IAM role."""
try:
client.put_role_permissions_boundary(
RoleName=role_name, PermissionsBoundary=boundary_arn)
logger.info("Attached boundary %s to role %s", boundary_arn, role_name)
return {"role": role_name, "boundary_arn": boundary_arn, "attached": True}
except ClientError as exc:
return {"role": role_name, "error": str(exc)}
def audit_roles_without_boundary(client) -> List[dict]:
"""Find IAM roles that lack a permission boundary."""
paginator = client.get_paginator("list_roles")
unbounded = []
for page in paginator.paginate():
for role in page["Roles"]:
if "PermissionsBoundary" not in role:
unbounded.append({
"role_name": role["RoleName"],
"arn": role["Arn"],
"created": role["CreateDate"].isoformat(),
})
logger.info("Found %d roles without permission boundary", len(unbounded))
return unbounded
def audit_boundary_effectiveness(client, role_name: str) -> dict:
"""Audit effective permissions for a role with boundary."""
try:
role = client.get_role(RoleName=role_name)["Role"]
boundary = role.get("PermissionsBoundary", {})
policies_resp = client.list_attached_role_policies(RoleName=role_name)
inline_resp = client.list_role_policies(RoleName=role_name)
return {
"role": role_name,
"boundary_arn": boundary.get("PermissionsBoundaryArn", "NONE"),
"attached_policies": [p["PolicyName"] for p in policies_resp["AttachedPolicies"]],
"inline_policies": inline_resp["PolicyNames"],
}
except ClientError as exc:
return {"role": role_name, "error": str(exc)}
def generate_report(client) -> dict:
"""Generate permission boundary compliance report."""
unbounded = audit_roles_without_boundary(client)
report = {
"analysis_date": datetime.utcnow().isoformat(),
"roles_without_boundary": unbounded,
"roles_without_boundary_count": len(unbounded),
"recommendations": [],
}
if unbounded:
report["recommendations"].append(
f"Attach permission boundaries to {len(unbounded)} roles lacking boundaries")
return report
def main():
parser = argparse.ArgumentParser(description="AWS IAM Permission Boundary Agent")
parser.add_argument("--profile", default="", help="AWS CLI profile name")
parser.add_argument("--region", default="us-east-1")
parser.add_argument("--audit", action="store_true", help="Audit roles without boundaries")
parser.add_argument("--output-dir", default=".")
parser.add_argument("--output", default="iam_boundary_report.json")
args = parser.parse_args()
os.makedirs(args.output_dir, exist_ok=True)
client = get_iam_client(args.profile, args.region)
report = generate_report(client)
out_path = os.path.join(args.output_dir, args.output)
with open(out_path, "w") as f:
json.dump(report, f, indent=2, default=str)
logger.info("Report saved to %s", out_path)
print(json.dumps(report, indent=2, default=str))
if __name__ == "__main__":
main()