def test_get_public_nodes(self): args, accounts, config = parse_arguments( ['--accounts', 'demo', '--config', 'config.json.demo'], None) public_nodes = get_public_nodes(accounts[0], config) # public_nodes contains a list of nodes from the network diagram as the first item, # and then things like API Gateway and CloudFront as the second item assert_equal(len(public_nodes), 2) # Check sizes of other items assert_equal(len(public_nodes[1]), 0) assert_equal(len(public_nodes[0]), 3) public_nodes[0] ecs_nodes = [] elb_nodes = [] elbv2_nodes = [] print(public_nodes[0]) for node in public_nodes[0]: if node['type'] == 'ecs': ecs_nodes.append(node) if node['type'] == 'elb': elb_nodes.append(node) if node['type'] == 'elbv2': elbv2_nodes.append(node) assert_equal(len(ecs_nodes), 1) assert_equal(ecs_nodes[0]['ports'], '443') assert_equal(len(elb_nodes), 1) assert_equal(len(elbv2_nodes), 1)
def test_get_public_nodes(self): args, accounts, config = parse_arguments( ["--accounts", "demo", "--config", "config.json.demo"], None) public_nodes, warnings = get_public_nodes(accounts[0], config) assert_equal(len(public_nodes), 3) # Check sizes of other items assert_equal(len(public_nodes[0]), 6) public_nodes[0] ecs_nodes = [] elb_nodes = [] elbv2_nodes = [] print(public_nodes) for node in public_nodes: if node["type"] == "ecs": ecs_nodes.append(node) if node["type"] == "elb": elb_nodes.append(node) if node["type"] == "elbv2": elbv2_nodes.append(node) assert_equal(len(ecs_nodes), 1) assert_equal(ecs_nodes[0]["ports"], "443") assert_equal(len(elb_nodes), 1) assert_equal(len(elbv2_nodes), 1)
def test_get_public_nodes(self): args, accounts, config = parse_arguments( ["--accounts", "demo", "--config", "config.json.demo"], None) public_nodes = get_public_nodes(accounts[0], config) # public_nodes contains a list of nodes from the network diagram as the first item, # and then things like API Gateway and CloudFront as the second item assert_equal(len(public_nodes), 2) # Check sizes of other items assert_equal(len(public_nodes[1]), 0) assert_equal(len(public_nodes[0]), 3) public_nodes[0] ecs_nodes = [] elb_nodes = [] elbv2_nodes = [] print(public_nodes[0]) for node in public_nodes[0]: if node["type"] == "ecs": ecs_nodes.append(node) if node["type"] == "elb": elb_nodes.append(node) if node["type"] == "elbv2": elbv2_nodes.append(node) assert_equal(len(ecs_nodes), 1) assert_equal(ecs_nodes[0]["ports"], "443") assert_equal(len(elb_nodes), 1) assert_equal(len(elbv2_nodes), 1)
def public(accounts, config): for account in accounts: public_nodes, warnings = get_public_nodes(account, config) for public_node in public_nodes: print(json.dumps(public_node, indent=4, sort_keys=True)) for warning in warnings: print('WARNING: {}'.format(warning), file=sys.stderr)
def public(accounts, config): all_accounts = [] for account in accounts: public_nodes, warnings = get_public_nodes(account, config) for public_node in public_nodes: all_accounts.append(public_node) for warning in warnings: print("WARNING: {}".format(warning), file=sys.stderr) print(json.dumps(all_accounts, indent=4, sort_keys=True))
def report(accounts, config, args): """Create report""" # Create directory for output file if it doesn't already exists try: os.mkdir(os.path.dirname(REPORT_OUTPUT_FILE)) except OSError: # Already exists pass # Read template with open(os.path.join("templates", "report.html"), "r") as report_template: template = Template(report_template.read()) # Data to be passed to the template t = {} t["version"] = __version__ # Get account names and id's t["accounts"] = [] for account in accounts: t["accounts"].append( { "name": account["name"], "id": account["id"], "collection_date": get_collection_date(account)[:10], } ) # Get resource count info # Collect counts account_stats = {} print("* Getting resource counts") for account in accounts: account_stats[account["name"]] = get_account_stats( account, args.stats_all_resources ) print(" - {}".format(account["name"])) # Get names of resources # TODO: Change the structure passed through here to be a dict of dict's like I do for the regions t["resource_names"] = [""] # Just look at the resource names of the first account as they are all the same first_account = list(account_stats.keys())[0] for name in account_stats[first_account]["keys"]: t["resource_names"].append(name) # Create jinja data for the resource stats per account t["resource_stats"] = [] for account in accounts: for resource_name in t["resource_names"]: if resource_name == "": resource_row = [account["name"]] else: count = sum(account_stats[account["name"]][resource_name].values()) resource_row.append(count) t["resource_stats"].append(resource_row) t["resource_names"].pop(0) # Get region names t["region_names"] = [] account = accounts[0] account = Account(None, account) for region in get_regions(account): region = Region(account, region) t["region_names"].append(region.name) # Get stats for the regions region_stats = {} region_stats_tooltip = {} for account in accounts: account = Account(None, account) region_stats[account.name] = {} region_stats_tooltip[account.name] = {} for region in get_regions(account): region = Region(account, region) count = 0 for resource_name in t["resource_names"]: n = account_stats[account.name][resource_name].get(region.name, 0) count += n if n > 0: if region.name not in region_stats_tooltip[account.name]: region_stats_tooltip[account.name][region.name] = "" region_stats_tooltip[account.name][ region.name ] += "{}:{}<br>".format(resource_name, n) if count > 0: has_resources = "Y" else: has_resources = "N" region_stats[account.name][region.name] = has_resources t["region_stats"] = region_stats t["region_stats_tooltip"] = region_stats_tooltip # Pass the account names t["account_names"] = [] for a in accounts: t["account_names"].append(a["name"]) t["resource_data_set"] = [] # Pass data for the resource chart color_index = 0 for resource_name in t["resource_names"]: resource_counts = [] for account_name in t["account_names"]: resource_counts.append( sum(account_stats[account_name][resource_name].values()) ) resource_data = { "label": resource_name, "data": resource_counts, "backgroundColor": COLOR_PALETTE[color_index], "borderWidth": 1, } t["resource_data_set"].append(resource_data) color_index = (color_index + 1) % len(COLOR_PALETTE) # Get IAM access dat print("* Getting IAM data") t["iam_active_data_set"] = [ { "label": "Active users", "stack": "users", "data": [], "backgroundColor": "rgb(162, 203, 249)", "borderWidth": 1, }, { "label": "Inactive users", "stack": "users", "data": [], "backgroundColor": INACTIVE_COLOR, "borderWidth": 1, }, { "label": "Active roles", "stack": "roles", "data": [], "backgroundColor": ACTIVE_COLOR, "borderWidth": 1, }, { "label": "Inactive roles", "stack": "roles", "data": [], "backgroundColor": INACTIVE_COLOR, "borderWidth": 1, }, ] for account in accounts: account = Account(None, account) print(" - {}".format(account.name)) account_stats = get_access_advisor_active_counts(account, args.max_age) # Add to dataset t["iam_active_data_set"][0]["data"].append(account_stats["users"]["active"]) t["iam_active_data_set"][1]["data"].append(account_stats["users"]["inactive"]) t["iam_active_data_set"][2]["data"].append(account_stats["roles"]["active"]) t["iam_active_data_set"][3]["data"].append(account_stats["roles"]["inactive"]) print("* Getting public resource data") # TODO Need to cache this data as this can take a long time t["public_network_resource_type_names"] = [ "ec2", "elb", "elbv2", "rds", "redshift", "ecs", "autoscaling", "cloudfront", "apigateway", ] t["public_network_resource_types"] = {} t["public_ports"] = [] t["account_public_ports"] = {} for account in accounts: print(" - {}".format(account["name"])) t["public_network_resource_types"][account["name"]] = {} t["account_public_ports"][account["name"]] = {} for type_name in t["public_network_resource_type_names"]: t["public_network_resource_types"][account["name"]][type_name] = 0 public_nodes, _ = get_public_nodes(account, config, use_cache=True) for public_node in public_nodes: if public_node["type"] in t["public_network_resource_type_names"]: t["public_network_resource_types"][account["name"]][ public_node["type"] ] += 1 else: raise Exception( "Unknown type {} of public node".format(public_node["type"]) ) if public_node["ports"] not in t["public_ports"]: t["public_ports"].append(public_node["ports"]) t["account_public_ports"][account["name"]][public_node["ports"]] = ( t["account_public_ports"][account["name"]].get(public_node["ports"], 0) + 1 ) # Pass data for the public port chart t["public_ports_data_set"] = [] color_index = 0 for ports in t["public_ports"]: port_counts = [] for account_name in t["account_names"]: port_counts.append(t["account_public_ports"][account_name].get(ports, 0)) # Fix the port range name for '' when ICMP is being allowed if ports == "": ports = "ICMP only" port_data = { "label": ports, "data": port_counts, "backgroundColor": COLOR_PALETTE[color_index], "borderWidth": 1, } t["public_ports_data_set"].append(port_data) color_index = (color_index + 1) % len(COLOR_PALETTE) print("* Auditing accounts") findings = audit(accounts) audit_config = load_audit_config() # Filter findings tmp_findings = [] for finding in findings: conf = audit_config[finding.issue_id] if finding_is_filtered(finding, conf, minimum_severity=args.minimum_severity): continue tmp_findings.append(finding) findings = tmp_findings t["findings_severity_by_account_chart"] = [] # Figure out the counts of findings for each account # Create chart for finding type counts findings_severity_by_account = {} for account in accounts: findings_severity_by_account[account["name"]] = {} for severity in SEVERITIES: findings_severity_by_account[account["name"]][severity["name"]] = {} # Filtering the list of findings down to the ones specific to the current account. for finding in [f for f in findings if f.account_name == account["name"]]: conf = audit_config[finding.issue_id] count = findings_severity_by_account[finding.account_name][ conf["severity"] ].get(finding.issue_id, 0) findings_severity_by_account[finding.account_name][conf["severity"]][ finding.issue_id ] = (count + 1) t["findings_severity_by_account_chart"] = [] for severity in SEVERITIES: severity_counts_by_account = [] for _ in accounts: severity_counts_by_account.append( len( findings_severity_by_account[finding.account_name][severity["name"]] ) ) t["findings_severity_by_account_chart"].append( { "label": severity["name"], "data": severity_counts_by_account, "backgroundColor": severity["color"], "borderWidth": 1, } ) # Create list by severity t["severities"] = {} for severity in SEVERITIES: t["severities"][severity["name"]] = {} for finding in findings: conf = audit_config[finding.issue_id] t["severities"][conf["severity"]][finding.issue_id] = { "title": conf["title"], "id": finding.issue_id, } # Create chart for finding counts finding_type_set = {} for f in findings: finding_type_set[f.issue_id] = 1 t["finding_counts_by_account_chart"] = [] for finding_type in finding_type_set: finding_counts = [] for account in accounts: count = 0 for severity in findings_severity_by_account[account["name"]]: count += findings_severity_by_account[account["name"]][severity].get( finding_type, 0 ) finding_counts.append(count) t["finding_counts_by_account_chart"].append( { "label": finding_type, "data": finding_counts, "backgroundColor": COLOR_PALETTE[color_index], "borderWidth": 1, } ) color_index = (color_index + 1) % len(COLOR_PALETTE) t["findings"] = {} for finding in findings: conf = audit_config[finding.issue_id] group = t["findings"].get(conf["group"], {}) # Get the severity struct for severity in SEVERITIES: if severity["name"] == conf["severity"]: break issue = group.get( finding.issue_id, { "title": conf["title"], "description": conf.get("description", ""), "severity": conf["severity"], "severity_color": severity["color"], "is_global": conf.get("is_global", False), "accounts": {}, }, ) account_hits = issue["accounts"].get( finding.region.account.local_id, {"account_name": finding.region.account.name, "regions": {}}, ) region_hits = account_hits["regions"].get(finding.region.name, {"hits": []}) region_hits["hits"].append( { "resource": finding.resource_id, "details": json.dumps(finding.resource_details, indent=4), } ) account_hits["regions"][finding.region.name] = region_hits issue["accounts"][finding.region.account.local_id] = account_hits group[finding.issue_id] = issue t["findings"][conf["group"]] = group # Generate report from template with open(REPORT_OUTPUT_FILE, "w") as f: f.write(template.render(t=t)) print("Report written to {}".format(REPORT_OUTPUT_FILE))
def report(accounts, config, args): '''Create report''' # Create directory for output file if it doesn't already exists try: os.mkdir(os.path.dirname(REPORT_OUTPUT_FILE)) except OSError: # Already exists pass # Read template with open(os.path.join('templates', 'report.html'), 'r') as report_template: template = Template(report_template.read()) # Data to be passed to the template t = {} # Get account names and id's t['accounts'] = [] for account in accounts: t['accounts'].append({ 'name': account['name'], 'id': account['id'], 'collection_date': get_collection_date(account)}) # Get resource count info # Collect counts account_stats = {} print('* Getting resource counts') for account in accounts: account_stats[account['name']] = get_account_stats(account,args.stats_all_resources) print(' - {}'.format(account['name'])) # Get names of resources # TODO: Change the structure passed through here to be a dict of dict's like I do for the regions t['resource_names'] = [''] # Just look at the resource names of the first account as they are all the same first_account = list(account_stats.keys())[0] for name in account_stats[first_account]['keys']: t['resource_names'].append(name) # Create jinja data for the resource stats per account t['resource_stats'] = [] for account in accounts: for resource_name in t['resource_names']: if resource_name == '': resource_row = [account['name']] else: count = sum(account_stats[account['name']][resource_name].values()) resource_row.append(count) t['resource_stats'].append(resource_row) t['resource_names'].pop(0) # Get region names t['region_names'] = [] account = accounts[0] account = Account(None, account) for region in get_regions(account): region = Region(account, region) t['region_names'].append(region.name) # Get stats for the regions region_stats = {} region_stats_tooltip = {} for account in accounts: account = Account(None, account) region_stats[account.name] = {} region_stats_tooltip[account.name] = {} for region in get_regions(account): region = Region(account, region) count = 0 for resource_name in t['resource_names']: n = account_stats[account.name][resource_name].get(region.name, 0) count += n if n > 0: if region.name not in region_stats_tooltip[account.name]: region_stats_tooltip[account.name][region.name] = '' region_stats_tooltip[account.name][region.name] += '{}:{}<br>'.format(resource_name, n) if count > 0: has_resources = 'Y' else: has_resources = 'N' region_stats[account.name][region.name] = has_resources t['region_stats'] = region_stats t['region_stats_tooltip'] = region_stats_tooltip # Pass the account names t['account_names'] = [] for a in accounts: t['account_names'].append(a['name']) t['resource_data_set'] = [] # Pass data for the resource chart color_index = 0 for resource_name in t['resource_names']: resource_counts = [] for account_name in t['account_names']: resource_counts.append(sum(account_stats[account_name][resource_name].values())) resource_data = { 'label': resource_name, 'data': resource_counts, 'backgroundColor': COLOR_PALETTE[color_index], 'borderWidth': 1 } t['resource_data_set'].append(resource_data) color_index = (color_index + 1) % len(COLOR_PALETTE) # Get IAM access dat print('* Getting IAM data') t['iam_active_data_set'] = [ { 'label': 'Active users', 'stack': 'users', 'data': [], 'backgroundColor': 'rgb(162, 203, 249)', 'borderWidth': 1 }, { 'label': 'Inactive users', 'stack': 'users', 'data': [], 'backgroundColor': INACTIVE_COLOR, 'borderWidth': 1 }, { 'label': 'Active roles', 'stack': 'roles', 'data': [], 'backgroundColor': ACTIVE_COLOR, 'borderWidth': 1 }, { 'label': 'Inactive roles', 'stack': 'roles', 'data': [], 'backgroundColor': INACTIVE_COLOR, 'borderWidth': 1 } ] for account in accounts: account = Account(None, account) print(' - {}'.format(account.name)) account_stats = get_access_advisor_active_counts(account, args.max_age) # Add to dataset t['iam_active_data_set'][0]['data'].append(account_stats['users']['active']) t['iam_active_data_set'][1]['data'].append(account_stats['users']['inactive']) t['iam_active_data_set'][2]['data'].append(account_stats['roles']['active']) t['iam_active_data_set'][3]['data'].append(account_stats['roles']['inactive']) print('* Getting public resource data') # TODO Need to cache this data as this can take a long time t['public_network_resource_type_names'] = ['ec2', 'elb', 'rds', 'autoscaling', 'cloudfront', 'apigateway'] t['public_network_resource_types'] = {} t['public_ports'] = [] t['account_public_ports'] = {} for account in accounts: print(' - {}'.format(account['name'])) t['public_network_resource_types'][account['name']] = {} t['account_public_ports'][account['name']] = {} for type_name in t['public_network_resource_type_names']: t['public_network_resource_types'][account['name']][type_name] = 0 public_nodes, _ = get_public_nodes(account, config, use_cache=True) for public_node in public_nodes: if public_node['type'] in t['public_network_resource_type_names']: t['public_network_resource_types'][account['name']][public_node['type']] += 1 else: raise Exception('Unknown type {} of public node'.format(public_node['type'])) if public_node['ports'] not in t['public_ports']: t['public_ports'].append(public_node['ports']) t['account_public_ports'][account['name']][public_node['ports']] = t['account_public_ports'][account['name']].get(public_node['ports'], 0) + 1 # Pass data for the public port chart t['public_ports_data_set'] = [] color_index = 0 for ports in t['public_ports']: port_counts = [] for account_name in t['account_names']: port_counts.append(t['account_public_ports'][account_name].get(ports, 0)) # Fix the port range name for '' when ICMP is being allowed if ports == '': ports = 'ICMP only' port_data = { 'label': ports, 'data': port_counts, 'backgroundColor': COLOR_PALETTE[color_index], 'borderWidth': 1 } t['public_ports_data_set'].append(port_data) color_index = (color_index + 1) % len(COLOR_PALETTE) print('* Auditing accounts') findings = audit(accounts) with open("audit_config.yaml", 'r') as f: audit_config = yaml.safe_load(f) t['findings_severity_by_account_chart'] = [] # Figure out the counts of findings for each account # Create chart for finding type counts findings_severity_by_account = {} for account in accounts: findings_severity_by_account[account['name']] = {} for severity in SEVERITIES: findings_severity_by_account[account['name']][severity['name']] = {} for finding in findings: conf = audit_config[finding.issue_id] count = findings_severity_by_account[finding.account_name][conf['severity']].get(finding.issue_id, 0) findings_severity_by_account[finding.account_name][conf['severity']][finding.issue_id] = count + 1 t['findings_severity_by_account_chart'] = [] for severity in SEVERITIES: severity_counts_by_account = [] for account in accounts: severity_counts_by_account.append(len(findings_severity_by_account[finding.account_name][severity['name']])) t['findings_severity_by_account_chart'].append({ 'label': severity['name'], 'data': severity_counts_by_account, 'backgroundColor': severity['color'], 'borderWidth': 1 }) # Create list by severity t['severities'] = {} for severity in SEVERITIES: t['severities'][severity['name']] = {} for finding in findings: conf = audit_config[finding.issue_id] t['severities'][conf['severity']][finding.issue_id] = { 'title': conf['title'], 'id': finding.issue_id} #t['severities'][severity['name']] = severity_issue_list # Create chart for finding counts finding_type_set = {} for f in findings: finding_type_set[f.issue_id] = 1 t['finding_counts_by_account_chart'] = [] for finding_type in finding_type_set: finding_counts = [] for account in accounts: count = 0 for severity in findings_severity_by_account[account['name']]: count += findings_severity_by_account[account['name']][severity].get(finding_type, 0) finding_counts.append(count) t['finding_counts_by_account_chart'].append({ 'label': finding_type, 'data': finding_counts, 'backgroundColor': COLOR_PALETTE[color_index], 'borderWidth': 1 }) color_index = (color_index + 1) % len(COLOR_PALETTE) t['findings'] = {} for finding in findings: conf = audit_config[finding.issue_id] group = t['findings'].get(conf['group'], {}) # Get the severity struct for severity in SEVERITIES: if severity['name'] == conf['severity']: break issue = group.get(finding.issue_id, { 'title': conf['title'], 'description': conf.get('description', ''), 'severity': conf['severity'], 'severity_color': severity['color'], 'is_global': conf.get('is_global', False), 'accounts': {}}) account_hits = issue['accounts'].get(finding.region.account.local_id, { 'account_name': finding.region.account.name, 'regions': {} }) region_hits = account_hits['regions'].get(finding.region.name, { 'hits': []}) region_hits['hits'].append({ 'resource': finding.resource_id, 'details': json.dumps(finding.resource_details, indent=4) }) account_hits['regions'][finding.region.name] = region_hits issue['accounts'][finding.region.account.local_id] = account_hits group[finding.issue_id] = issue t['findings'][conf['group']] = group # Generate report from template with open(REPORT_OUTPUT_FILE, 'w') as f: f.write(template.render(t=t)) print('Report written to {}'.format(REPORT_OUTPUT_FILE))
def dashboard(accounts, config, args): '''Create dashboard''' # Create directory for output file if it doesn't already exists try: os.mkdir(os.path.dirname(REPORT_OUTPUT_FILE)) except OSError: # Already exists pass # Read template with open(os.path.join('templates', 'report.html'), 'r') as dashboard_template: template = Template(dashboard_template.read()) # Data to be passed to the template t = {} # Get account names and id's t['accounts'] = [] for account in accounts: t['accounts'].append({ 'name': account['name'], 'id': account['id'], 'collection_date': get_collection_date(account) }) # Get resource count info # Collect counts account_stats = {} print('* Getting resource counts') for account in accounts: account_stats[account['name']] = get_account_stats(account) print(' - {}'.format(account['name'])) # Get names of resources # TODO: Change the structure passed through here to be a dict of dict's like I do for the regions t['resource_names'] = [''] # Just look at the resource names of the first account as they are all the same first_account = list(account_stats.keys())[0] for name in account_stats[first_account]['keys']: t['resource_names'].append(name) # Create jinja data for the resource stats per account t['resource_stats'] = [] for account in accounts: for resource_name in t['resource_names']: if resource_name == '': resource_row = [account['name']] else: count = sum( account_stats[account['name']][resource_name].values()) resource_row.append(count) t['resource_stats'].append(resource_row) t['resource_names'].pop(0) # Get region names t['region_names'] = [] account = accounts[0] account = Account(None, account) for region in get_regions(account): region = Region(account, region) t['region_names'].append(region.name) # Get stats for the regions region_stats = {} region_stats_tooltip = {} for account in accounts: account = Account(None, account) region_stats[account.name] = {} region_stats_tooltip[account.name] = {} for region in get_regions(account): region = Region(account, region) count = 0 for resource_name in t['resource_names']: n = account_stats[account.name][resource_name].get( region.name, 0) count += n if n > 0: if region.name not in region_stats_tooltip[account.name]: region_stats_tooltip[account.name][region.name] = '' region_stats_tooltip[account.name][ region.name] += '{}:{}<br>'.format(resource_name, n) if count > 0: has_resources = 'Y' else: has_resources = 'N' region_stats[account.name][region.name] = has_resources t['region_stats'] = region_stats t['region_stats_tooltip'] = region_stats_tooltip # Pass the account names t['account_names'] = [] for a in accounts: t['account_names'].append(a['name']) t['resource_data_set'] = [] # Pass data for the resource chart color_index = 0 for resource_name in t['resource_names']: resource_counts = [] for account_name in t['account_names']: resource_counts.append( sum(account_stats[account_name][resource_name].values())) resource_data = { 'label': resource_name, 'data': resource_counts, 'backgroundColor': COLOR_PALETTE[color_index], 'borderWidth': 1 } t['resource_data_set'].append(resource_data) color_index = (color_index + 1) % len(COLOR_PALETTE) # Get IAM access dat print('* Getting IAM data') t['iam_active_data_set'] = [{ 'label': 'Active users', 'stack': 'users', 'data': [], 'backgroundColor': 'rgb(162, 203, 249)', 'borderWidth': 1 }, { 'label': 'Inactive users', 'stack': 'users', 'data': [], 'backgroundColor': INACTIVE_COLOR, 'borderWidth': 1 }, { 'label': 'Active roles', 'stack': 'roles', 'data': [], 'backgroundColor': ACTIVE_COLOR, 'borderWidth': 1 }, { 'label': 'Inactive roles', 'stack': 'roles', 'data': [], 'backgroundColor': INACTIVE_COLOR, 'borderWidth': 1 }] for account in accounts: account = Account(None, account) print(' - {}'.format(account.name)) account_stats = get_access_advisor_active_counts(account, args.max_age) # Add to dataset t['iam_active_data_set'][0]['data'].append( account_stats['users']['active']) t['iam_active_data_set'][1]['data'].append( account_stats['users']['inactive']) t['iam_active_data_set'][2]['data'].append( account_stats['roles']['active']) t['iam_active_data_set'][3]['data'].append( account_stats['roles']['inactive']) print('* Getting public resource data') # TODO Need to cache this data as this can take a long time t['public_network_resource_type_names'] = [ 'ec2', 'elb', 'rds', 'autoscaling', 'cloudfront', 'apigateway' ] t['public_network_resource_types'] = {} t['public_ports'] = [] t['account_public_ports'] = {} for account in accounts: print(' - {}'.format(account['name'])) t['public_network_resource_types'][account['name']] = {} t['account_public_ports'][account['name']] = {} for type_name in t['public_network_resource_type_names']: t['public_network_resource_types'][account['name']][type_name] = 0 public_nodes, _ = get_public_nodes(account, config, use_cache=True) for public_node in public_nodes: if public_node['type'] in t['public_network_resource_type_names']: t['public_network_resource_types'][account['name']][ public_node['type']] += 1 else: raise Exception('Unknown type {} of public node'.format( public_node['type'])) if public_node['ports'] not in t['public_ports']: t['public_ports'].append(public_node['ports']) t['account_public_ports'][account['name']][public_node[ 'ports']] = t['account_public_ports'][account['name']].get( public_node['ports'], 0) + 1 # Pass data for the public port chart t['public_ports_data_set'] = [] color_index = 0 for ports in t['public_ports']: port_counts = [] for account_name in t['account_names']: port_counts.append(t['account_public_ports'][account_name].get( ports, 0)) # Fix the port range name for '' when ICMP is being allowed if ports == '': ports = 'ICMP only' port_data = { 'label': ports, 'data': port_counts, 'backgroundColor': COLOR_PALETTE[color_index], 'borderWidth': 1 } t['public_ports_data_set'].append(port_data) color_index = (color_index + 1) % len(COLOR_PALETTE) # Generate report from template with open(REPORT_OUTPUT_FILE, 'w') as f: f.write(template.render(t=t)) print('Report written to {}'.format(REPORT_OUTPUT_FILE))