Skip to content

Commit

Permalink
AWS_DEFAULT_PROFILE is respected; made formatting updates; made some …
Browse files Browse the repository at this point in the history
…path fixes that should help the windows issues (#239)
  • Loading branch information
kmcquade authored Dec 7, 2021
1 parent f448ed9 commit efbd87c
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 218 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ _notes
results.*
default.json
default-iam-report.csv
private/default-iam-results.json
private/default-iam-results.json0
default-results-summary.csv
iam-new-principal-policy-mapping-example.json
iam-findings-example.json
Expand Down
15 changes: 3 additions & 12 deletions cloudsplaining/command/create_exclusions_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# For full license text, see the LICENSE file in the repo root
# or https://opensource.org/licenses/BSD-3-Clause
import os
from pathlib import Path
import logging
import click
from cloudsplaining.shared.constants import EXCLUSIONS_TEMPLATE
Expand All @@ -22,14 +21,7 @@
context_settings=dict(max_content_width=160),
short_help="Creates a YML file to be used as a custom exclusions template",
)
@click.option(
"--output-file",
"-o",
type=click.Path(exists=False),
default=os.path.join(os.getcwd(), "exclusions.yml"),
required=True,
help="Relative path to output file where we want to store the exclusions template.",
)
@click.option("-o", "--output-file", type=click.Path(exists=False), default=os.path.join(os.getcwd(), "exclusions.yml"), required=True, help="Relative path to output file where we want to store the exclusions template.")
@click.option("--verbose", "-v", "verbosity", count=True)
def create_exclusions_file(output_file: str, verbosity: int) -> None:
"""
Expand All @@ -38,11 +30,10 @@ def create_exclusions_file(output_file: str, verbosity: int) -> None:
"""
set_log_level(verbosity)

filename = Path(output_file).resolve()
with open(filename, "a") as file_obj:
with open(output_file, "a") as file_obj:
for line in EXCLUSIONS_TEMPLATE:
file_obj.write(line)
utils.print_green(f"Success! Exclusions template file written to: {filename}")
utils.print_green(f"Success! Exclusions template file written to: {output_file}")
print(
"Make sure you download your account authorization details before running the scan."
"Set your AWS access keys as environment variables then run: "
Expand Down
31 changes: 9 additions & 22 deletions cloudsplaining/command/create_multi_account_config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# For full license text, see the LICENSE file in the repo root
# or https://opensource.org/licenses/BSD-3-Clause
import os
from pathlib import Path
import logging
import click
from cloudsplaining.shared.constants import MULTI_ACCOUNT_CONFIG_TEMPLATE
Expand All @@ -24,41 +23,29 @@
context_settings=dict(max_content_width=160),
short_help="Creates a YML file to be used for multi-account scanning",
)
@click.option(
"--output-file",
"-o",
"output_file",
type=click.Path(exists=False),
default=os.path.join(os.getcwd(), "multi-account-config.yml"),
required=True,
help="Relative path to output file where we want to store the multi account config template.",
)
@click.option(
"-v", "--verbose", "verbosity", count=True,
)
@click.option("-o", "--output-file", "output_file", type=click.Path(exists=False), default=os.path.join(os.getcwd(), "multi-account-config.yml"), required=True, help="Relative path to output file where we want to store the multi account config template.")
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def create_multi_account_config_file(output_file: str, verbosity: int) -> None:
"""
Creates a YML file to be used as a multi-account config template, so users can scan many different accounts.
"""
set_log_level(verbosity)

filename = Path(output_file).resolve()

if filename.exists():
if os.path.exists(output_file):
logger.debug(
"%s exists. Removing the file and replacing its contents.", filename
"%s exists. Removing the file and replacing its contents.", output_file
)
filename.unlink()
os.remove(output_file)

with open(filename, "a") as file_obj:
with open(output_file, "a") as file_obj:
for line in MULTI_ACCOUNT_CONFIG_TEMPLATE:
file_obj.write(line)
utils.print_green(
f"Success! Multi-account config file written to: {os.path.relpath(filename)}"
f"Success! Multi-account config file written to: {os.path.relpath(output_file)}"
)
print(
f"\nMake sure you edit the {os.path.relpath(filename)} file and then run the scan-multi-account command, as shown below."
f"\nMake sure you edit the {os.path.relpath(output_file)} file and then run the scan-multi-account command, as shown below."
)
print(
f"\n\tcloudsplaining scan-multi-account --exclusions-file exclusions.yml -c {os.path.relpath(filename)} -o ./"
f"\n\tcloudsplaining scan-multi-account --exclusions-file exclusions.yml -c {os.path.relpath(output_file)} -o ./"
)
38 changes: 10 additions & 28 deletions cloudsplaining/command/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# or https://opensource.org/licenses/BSD-3-Clause
import json
import logging
from pathlib import Path
import os
from typing import Dict, List, Any

import boto3
Expand All @@ -22,29 +22,10 @@
short_help="Runs aws iam get-authorization-details on all accounts specified in the aws credentials "
"file, and stores them in account-alias.json"
)
@click.option(
"--profile",
"-p",
type=str,
required=False,
help="Specify 'all' to authenticate to AWS and analyze *all* existing IAM policies. Specify a non-default "
"profile here. Defaults to the 'default' profile.",
)
@click.option(
"--output",
"-o",
type=click.Path(exists=True),
default=Path.cwd(),
help="Path to store the output. Defaults to current directory.",
)
@click.option(
"--include-non-default-policy-versions",
is_flag=True,
default=False,
help="When downloading AWS managed policy documents, also include the non-default policy versions."
" Note that this will dramatically increase the size of the downloaded file.",
)
@click.option("--verbose", "-v", "verbosity", count=True)
@click.option("-p", "--profile", type=str, required=False, envvar="AWS_DEFAULT_PROFILE", help="Specify 'all' to authenticate to AWS and scan from *all* AWS credentials profiles. Specify a non-default profile here. Defaults to the 'default' profile.")
@click.option("-o", "--output", type=click.Path(exists=True), default=os.getcwd(), help="Path to store the output. Defaults to current directory.")
@click.option("--include-non-default-policy-versions", is_flag=True, default=False, help="When downloading AWS managed policy documents, also include the non-default policy versions. Note that this will dramatically increase the size of the downloaded file.")
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def download(
profile: str, output: str, include_non_default_policy_versions: bool, verbosity: int
) -> int:
Expand All @@ -59,15 +40,16 @@ def download(

if profile:
session_data["profile_name"] = profile
output_filename = Path(output) / f"{profile}.json"
output_filename = os.path.join(output, f"{profile}.json")
else:
output_filename = Path("default.json")
output_filename = os.path.join(output, f"default.json")

results = get_account_authorization_details(
session_data, include_non_default_policy_versions
)

output_filename.write_text(json.dumps(results, indent=4, default=str))
with open(output_filename, "w") as f:
json.dump(results, f, indent=4, default=str)
# output_filename.write_text(json.dumps(results, indent=4, default=str))
print(f"Saved results to {output_filename}")
return 1

Expand Down
10 changes: 2 additions & 8 deletions cloudsplaining/command/expand_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,8 @@
@click.command(
short_help="Expand the * Actions in IAM policy files to improve readability"
)
@click.option(
"--input-file",
"-i",
type=click.Path(exists=True),
required=True,
help="Path to the JSON policy file.",
)
@click.option("--verbose", "-v", "verbosity", count=True)
@click.option("-i", "--input-file", type=click.Path(exists=True), required=True, help="Path to the JSON policy file.")
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def expand_policy(input_file: str, verbosity: int) -> None:
"""
Expand the * Actions in IAM policy files to improve readability
Expand Down
56 changes: 11 additions & 45 deletions cloudsplaining/command/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,54 +24,17 @@
from cloudsplaining.output.report import HTMLReport
from cloudsplaining import set_log_level

logger = logging.getLogger(__name__)


@click.command(
short_help="Scan a single file containing AWS IAM account authorization details and generate report on "
"IAM security posture. "
)
@click.option(
"--input-file",
"-i",
type=click.Path(exists=True),
required=True,
help="Path of IAM account authorization details file",
)
@click.option(
"--exclusions-file",
"-e",
help="A yaml file containing a list of policy names to exclude from the scan.",
type=click.Path(exists=True),
required=False,
default=EXCLUSIONS_FILE,
)
@click.option(
"--output",
"-o",
required=False,
type=click.Path(exists=True),
default=os.getcwd(),
help="Output directory.",
)
@click.option(
"--skip-open-report",
"-s",
required=False,
default=False,
is_flag=True,
help="Don't open the HTML report in the web browser after creating. "
"This helps when running the report in automation.",
)
@click.option(
"--minimize",
"-m",
required=False,
default=False,
is_flag=True,
help="Reduce the size of the HTML Report by pulling the Cloudsplaining Javascript code over the internet.",
)
@click.option("--verbose", "-v", "verbosity", count=True)
@click.option("-i", "--input-file", type=click.Path(exists=True), required=True, help="Path of IAM account authorization details file")
@click.option("-e", "--exclusions-file", help="A yaml file containing a list of policy names to exclude from the scan.", type=click.Path(exists=True), required=False, default=EXCLUSIONS_FILE)
@click.option("-o", "--output", required=False, type=click.Path(exists=True), default=os.getcwd(), help="Output directory.")
@click.option("-s", "--skip-open-report", required=False, default=False, is_flag=True, help="Don't open the HTML report in the web browser after creating. This helps when running the report in automation.")
@click.option("-m", "--minimize", required=False, default=False, is_flag=True, help="Reduce the size of the HTML Report by pulling the Cloudsplaining Javascript code over the internet.")
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def scan(
input_file: str,
exclusions_file: str,
Expand All @@ -98,7 +61,7 @@ def scan(
exclusions = DEFAULT_EXCLUSIONS

if os.path.isfile(input_file):
account_name = Path(input_file).stem
account_name = os.path.basename(input_file).split(".")[0]
with open(input_file) as f:
contents = f.read()
account_authorization_details_cfg = json.loads(contents)
Expand Down Expand Up @@ -137,7 +100,7 @@ def scan(
contents = f.read()
account_authorization_details_cfg = json.loads(contents)

account_name = Path(file).stem
account_name = os.path.basename(input_file).split(".")[0]
# Scan the Account Authorization Details config
rendered_html_report = scan_account_authorization_details(
account_authorization_details_cfg,
Expand All @@ -164,6 +127,9 @@ def scan(
webbrowser.open(url, new=2)


logger = logging.getLogger(__name__)


def scan_account_authorization_details(
account_authorization_details_cfg: Dict[str, Any],
exclusions: Exclusions,
Expand Down
79 changes: 12 additions & 67 deletions cloudsplaining/command/scan_multi_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,71 +41,16 @@ def _accounts(self) -> Dict[str, str]:


@click.command(short_help="Scan multiple AWS Accounts using a config file")
@click.option(
"--config",
"-c",
"config_file",
type=click.Path(exists=True),
required=True,
help="Path of the multi-account config file",
)
@click.option(
"--profile",
"-p",
"profile",
type=str,
required=False,
help="Specify the AWS IAM profile.",
envvar="AWS_PROFILE",
)
@click.option(
"--role-name",
"-r",
"role_name",
type=str,
required=True,
help="The name of the IAM role to assume in target accounts. Must be the same name in all target accounts.",
)
@click.option(
"--exclusions-file",
"-e",
"exclusions_file",
help="A yaml file containing a list of policy names to exclude from the scan.",
type=click.Path(exists=True),
required=False,
default=EXCLUSIONS_FILE,
)
@optgroup.group(
"Output Target Options", help="",
)
@optgroup.option(
"--output-directory",
"-o",
"output_directory",
type=click.Path(exists=True),
help="Output directory. Supply this and/or --bucket.",
)
@optgroup.option(
"--output-bucket",
"-b",
"output_bucket",
type=str,
help="The S3 bucket to save the results. Supply this and/or --output-directory.",
)
@optgroup.group(
"Other Options", help="",
)
@optgroup.option(
"--write-data-file",
"-w",
is_flag=True,
required=False,
default=False,
help="Save the cloudsplaining JSON-formatted data results.",
)
@click.option(
"-v", "--verbose", "verbosity", count=True,
)
@click.option("--config", "-c", "config_file", type=click.Path(exists=True), required=True, help="Path of the multi-account config file")
@click.option("-p", "--profile", type=str, required=False, envvar="AWS_DEFAULT_PROFILE", help="Specify the AWS IAM profile")
@click.option("-r", "--role-name", "role_name", type=str, required=True, help="The name of the IAM role to assume in target accounts. Must be the same name in all target accounts.")
@click.option("-e", "--exclusions-file", "exclusions_file", help="A yaml file containing a list of policy names to exclude from the scan.", type=click.Path(exists=True), required=False, default=EXCLUSIONS_FILE)
@optgroup.group("Output Target Options", help="")
@optgroup.option("-o", "--output-directory", "output_directory", type=click.Path(exists=True), help="Output directory. Supply this and/or --bucket.")
@optgroup.option("-b", "--output-bucket", "output_bucket", type=str, help="The S3 bucket to save the results. Supply this and/or --output-directory.")
@optgroup.group("Other Options", help="")
@optgroup.option("-w", "--write-data-file", is_flag=True, required=False, default=False, help="Save the cloudsplaining JSON-formatted data results.")
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def scan_multi_account(
config_file: str,
profile: str,
Expand Down Expand Up @@ -191,8 +136,8 @@ def scan_accounts(
)
if output_directory:
# Write the HTML file
html_output_file = Path(output_directory) / f"{target_account_name}.html"
html_output_file.write_text(rendered_report)
html_output_file = os.path.join(output_directory, f"{target_account_name}.html")
utils.write_file(html_output_file, rendered_report)
utils.print_green(
f"Saved the HTML report to: {os.path.relpath(html_output_file)}"
)
Expand Down
Loading

0 comments on commit efbd87c

Please sign in to comment.