diff --git a/util/org-multi-account/ProwlerEC2.yaml b/util/org-multi-account/ProwlerEC2.yaml new file mode 100644 index 00000000..ad9390e4 --- /dev/null +++ b/util/org-multi-account/ProwlerEC2.yaml @@ -0,0 +1,365 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: Create Prowler EC2 with UserData (Shell Scripts, & AWS CLI Profiles) + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: Prowler EC2 Instance Settings + Parameters: + - BuildNumber + - ProwlerEc2Name + - InstanceType + - KeyPair + - SubnetId + - VpcId + - Ec2Role + - LatestAmazonLinux2AmiId + - ProwlerCron + - Label: + default: S3 Settings + Parameters: + - ProwlerS3 + - ProwlerS3Account + - Label: + default: CrossAccount Role + Parameters: + - AwsOrgId + - CrossAccountRole + +Parameters: + BuildNumber: + Type: String + Description: Enter Build Number (increment with Updates for cfn-init) + AllowedPattern: ^\d*$ + ConstraintDescription: Build Number must be a numeric string. + Default: 1 + ProwlerEc2Name: + Type: String + Description: Enter Name for Prowler EC2 Instance to create + AllowedPattern: ^[\w\s_.\/=+-]{1,128}$ + ConstraintDescription: Max 128 alphanumeric characters. Also special characters supported [whitespace, _, ., /, =, +, -] + Default: Prowler-EC2 + InstanceType: + Description: Enter Instance Type + Type: String + Default: t2.micro + KeyPair: + Description: Choose a KeyPair + Type: AWS::EC2::KeyPair::KeyName + SubnetId: + Description: Choose Subnet + Type: AWS::EC2::Subnet::Id + VpcId: + Description: Choose VPC + Type: AWS::EC2::VPC::Id + Ec2Role: + Description: Enter Name for EC2 Instance Role to create and attach to Prowler EC2 Instance + Type: String + AllowedPattern: ^[\w+=,.@-]{1,64}$ + ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -] + Default: ProwlerEC2-Role + ProwlerCron: + Description: Enter cron schedule. Default, runs everyday at 1am. See https://crontab.guru/, for syntax help. + Type: String + Default: "0 1 * * *" + LatestAmazonLinux2AmiId: + Type: AWS::SSM::Parameter::Value + Description: Latest AMI ID for Amazon Linux 2 (via AWS Publis SSM Parameters. See https://tinyurl.com/aws-public-ssm-parameters. + Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-ebs + + ProwlerS3: + Type: String + Description: Enter S3 Bucket for Prowler Reports. prefix-awsaccount-awsregion + AllowedPattern: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$ + ConstraintDescription: Max 63 characters. Can't start or end with dash. Can use numbers and lowercase letters. + Default: prowler-123456789012-us-east-1 + ProwlerS3Account: + Type: String + Description: Enter AWS Account Number where Prowler S3 Bucket resides. + AllowedPattern: ^\d{12}$ + ConstraintDescription: An AWS Account Number must be a 12 digit numeric string. + Default: 123456789012 + + AwsOrgId: + Type: String + Description: Enter AWS Organizations ID + AllowedPattern: ^o-[a-z0-9]{10,32}$ + ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters. + Default: o-abcde12345 + CrossAccountRole: + Type: String + Description: Enter CrossAccount Role Prowler will be using to assess AWS Accounts in the AWS Organization. (ProwlerCrossAccountRole) + AllowedPattern: ^[\w+=,.@-]{1,64}$ + ConstraintDescription: Max 64 alphanumeric characters. Also special characters [+, =, ., @, -] + Default: ProwlerXA-Role + +Resources: + ProwlerEc2: + Type: AWS::EC2::Instance + CreationPolicy: + ResourceSignal: + Timeout: PT5M + Properties: + KeyName: !Ref KeyPair + ImageId: !Ref LatestAmazonLinux2AmiId + IamInstanceProfile: !Ref ProwlerInstanceProfile + InstanceType: !Ref InstanceType + SubnetId: !Ref SubnetId + SecurityGroupIds: + - !Ref ProwlerSecurityGroup + BlockDeviceMappings: + - DeviceName: /dev/xvda + Ebs: + Encrypted: true + KmsKeyId: alias/aws/ebs + VolumeType: standard + DeleteOnTermination: true + VolumeSize: 8 + Tags: + - Key: Name + Value: !Ref ProwlerEc2Name + - Key: App + Value: Prowler + UserData: + Fn::Base64: + !Sub | + #!/bin/bash + yum update -y aws-cfn-bootstrap + /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProwlerEc2 --configsets onfirstboot --region ${AWS::Region} + yum -y update + /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ProwlerEc2 --region ${AWS::Region} + Metadata: + AWS::CloudFormation::Authentication: + S3AccessCreds: + type: S3 + buckets: + - !Ref ProwlerS3 + roleName: + Ref: ProwlerEc2Role + AWS::CloudFormation::Init: + configSets: + onfirstboot: + - build-number + - configure-cfn + - prowler-prereqs + - prowler-reports + - prowler-schedule + onupdate: + - build-number + - prowler-prereqs + - prowler-reports + - prowler-schedule + build-number: + commands: + show-build-number: + command: !Sub | + echo "BUILDNUMBER: ${BuildNumber}" + configure-cfn: + files: + /etc/cfn/hooks.d/cfn-auto-reloader.conf: + content: !Sub | + [cfn-auto-reloader-hook] + triggers=post.update + path=Resources.ProwlerEc2.Metadata.AWS::CloudFormation::Init + action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProwlerEc2 --configsets onupdate --region ${AWS::Region} + runas=root + mode: "000400" + owner: root + group: root + /etc/cfn/cfn-hup.conf: + content: !Sub | + [main] + stack=${AWS::StackId} + region=${AWS::Region} + verbose=true + interval=5 + mode: "000400" + owner: root + group: root + services: + sysvinit: + cfn-hup: + enabled: true + ensureRunning: true + files: + - /etc/cfn/cfn-hup.conf + - /etc/cfn/hooks.d/cfn-auto-reloader.conf + prowler-prereqs: + files: + /home/ec2-user/.awsvariables: + content: !Sub | + export S3=s3://${ProwlerS3} + export S3ACCOUNT=${ProwlerS3Account} + export ROLE=${CrossAccountRole} + mode: "000600" + owner: ec2-user + group: ec2-user + commands: + 01-install-prowler-prereqs-yum: + command: | + yum install python-pip git jq -y + 02-install-prowler-prereqs-pip: + command: | + sudo -u ec2-user pip install --user boto3 awscli detect-secrets + prowler-reports: + files: + /home/ec2-user/run-prowler-reports.sh: + source: !Sub https://${ProwlerS3}.s3.${AWS::Region}.amazonaws.com/run-prowler-reports.sh + mode: "000700" + owner: ec2-user + group: ec2-user + prowler-schedule: + files: + /home/ec2-user/mycron-prowler: + content: !Sub | + ${ProwlerCron} bash -lc ./run-prowler-reports.sh > mycron-prowler.log + mode: "000600" + owner: ec2-user + group: ec2-user + commands: + 01-create-prowler-cron-job: + command: | + sudo -u ec2-user crontab /home/ec2-user/mycron-prowler + + ProwlerSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupName: Prowler-EC2-RemoteAdministration + GroupDescription: Allow Remote Administration + Tags: + - Key: App + Value: Prowler + VpcId: !Ref VpcId + SecurityGroupEgress: + - Description: Allow HTTP Outbound + IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + - Description: Allow HTTPS Outbound + IpProtocol: tcp + FromPort: 443 + ToPort: 443 + CidrIp: 0.0.0.0/0 + Metadata: + cfn_nag: + rules_to_suppress: + - id: W5 + reason: "Using http/https to Internet for updates." + - id: W28 + reason: "Using a defined Security Group Name." + + ProwlerInstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Roles: + - !Ref ProwlerEc2Role + + ProwlerEc2Role: + Type: AWS::IAM::Role + Properties: + Description: Prowler EC2 Instance Role + RoleName: !Ref Ec2Role + Tags: + - Key: App + Value: Prowler + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: SSM-Agent + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowSsmAgent + Effect: Allow + Resource: "*" + Action: + - ssm:UpdateInstanceInformation + - ssm:ListInstanceAssociations + - ssm:UpdateInstanceAssociationStatus + - ssm:PutConfigurePackageResult + - ssm:GetManifest + - ssm:PutComplianceItems + - ec2messages:AcknowledgeMessage + - ec2messages:DeleteMessage + - ec2messages:FailMessage + - ec2messages:GetEndpoint + - ec2messages:GetMessages + - ec2messages:SendReply + - PolicyName: SSM-Inventory + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowPutInventory + Effect: Allow + Resource: "*" + Action: + - ssm:PutInventory + - Sid: AllowGatherInventory + Effect: Allow + Resource: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-GatherSoftwareInventory + Action: + - ssm:GetDocument + - ssm:DescribeDocument + - PolicyName: SSM-SessionManager + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowSessionManager + Effect: Allow + Resource: "*" + Action: + - ssmmessages:CreateControlChannel + - ssmmessages:CreateDataChannel + - ssmmessages:OpenControlChannel + - ssmmessages:OpenDataChannel + - PolicyName: Prowler-S3-Reports + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowGetPutListObject + Effect: Allow + Resource: + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3} + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/* + Action: + - s3:GetObject + - s3:PutObject + - s3:ListBucket + - PolicyName: Prowler-CrossAccount-AssumeRole + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowStsAssumeRole + Effect: Allow + Resource: !Sub arn:${AWS::Partition}:iam::*:role/${CrossAccountRole} + Action: sts:AssumeRole + Condition: + StringEquals: + aws:PrincipalOrgId: !Ref AwsOrgId + Metadata: + cfn_nag: + rules_to_suppress: + - id: W28 + reason: "Using a defined Role Name." + - id: W11 + reason: "Needed for SSM features." + +Outputs: + ProwlerEc2Account: + Description: AWS Account Number where Prowler EC2 Instance resides. + Value: !Ref AWS::AccountId + ProwlerEc2Role: + Description: Instance Role given to the Prowler EC2 Instance (needed to grant sts:AssumeRole rights). + Value: !Ref ProwlerEc2Role + ProwlerS3: + Description: S3 Bucket for Prowler Reports + Value: !Ref ProwlerS3 diff --git a/util/org-multi-account/ProwlerRole.yaml b/util/org-multi-account/ProwlerRole.yaml new file mode 100644 index 00000000..544cf1cd --- /dev/null +++ b/util/org-multi-account/ProwlerRole.yaml @@ -0,0 +1,123 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: Create the Cross-Account IAM Prowler Role + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: EC2 Settings + Parameters: + - ProwlerEc2Account + - ProwlerEc2Role + - Label: + default: S3 Settings + Parameters: + - ProwlerS3 + - Label: + default: CrossAccount Role + Parameters: + - ProwlerCrossAccountRole + +Parameters: + ProwlerS3: + Type: String + Description: Enter S3 Bucket for Prowler Reports. prefix-awsaccount-awsregion + AllowedPattern: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$ + Default: prowler-123456789012-us-east-1 + ProwlerEc2Account: + Type: String + Description: Enter AWS Account Number where Prowler EC2 Instance will reside. + AllowedPattern: ^\d{12}$ + ConstraintDescription: An AWS Account Number must be a 12 digit numeric string. + ProwlerEc2Role: + Type: String + Description: Enter Instance Role that will be given to the Prowler EC2 Instance (needed to grant sts:AssumeRole rights). + AllowedPattern: ^[\w+=,.@-]{1,64}$ + ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -] + Default: ProwlerEC2-Role + ProwlerCrossAccountRole: + Type: String + Description: Enter Name for CrossAccount Role to be created for Prowler to assess all Accounts in the AWS Organization. + AllowedPattern: ^[\w+=,.@-]{1,64}$ + ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -] + Default: ProwlerXA-Role + +Resources: + ProwlerRole: + Type: AWS::IAM::Role + Properties: + Description: Provides Prowler EC2 instance permissions to assess security of Accounts in AWS Organization + RoleName: !Ref ProwlerCrossAccountRole + Tags: + - Key: App + Value: Prowler + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + AWS: + - !Sub arn:${AWS::Partition}:iam::${ProwlerEc2Account}:root + Action: + - sts:AssumeRole + Condition: + StringLike: + aws:PrincipalArn: !Sub arn:${AWS::Partition}:iam::${ProwlerEc2Account}:role/${ProwlerEc2Role} + ManagedPolicyArns: + - !Sub arn:${AWS::Partition}:iam::aws:policy/SecurityAudit + - !Sub arn:${AWS::Partition}:iam::aws:policy/job-function/ViewOnlyAccess + Policies: + - PolicyName: Prowler-Additions-Policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowMoreReadForProwler + Effect: Allow + Resource: "*" + Action: + - access-analyzer:List* + - apigateway:Get* + - apigatewayv2:Get* + - aws-marketplace:ViewSubscriptions + - dax:ListTables + - ds:ListAuthorizedApplications + - ds:DescribeRoles + - ec2:GetEbsEncryptionByDefault + - ecr:Describe* + - lambda:GetAccountSettings + - lambda:GetFunctionConfiguration + - lambda:GetLayerVersionPolicy + - lambda:GetPolicy + - opsworks-cm:Describe* + - opsworks:Describe* + - secretsmanager:ListSecretVersionIds + - sns:List* + - sqs:ListQueueTags + - states:ListActivities + - support:Describe* + - tag:GetTagKeys + - PolicyName: Prowler-S3-Reports + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowGetPutListObject + Effect: Allow + Resource: + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3} + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/* + Action: + - s3:GetObject + - s3:PutObject + - s3:ListBucket + Metadata: + cfn_nag: + rules_to_suppress: + - id: W11 + reason: "Prowler requires these rights to perform its Security Assessment." + - id: W28 + reason: "Using a defined Role Name." + +Outputs: + ProwlerCrossAccountRole: + Description: CrossAccount Role to be used by Prowler to assess AWS Accounts in the AWS Organization. + Value: !Ref ProwlerCrossAccountRole diff --git a/util/org-multi-account/ProwlerS3.yaml b/util/org-multi-account/ProwlerS3.yaml new file mode 100644 index 00000000..17b9f8b3 --- /dev/null +++ b/util/org-multi-account/ProwlerS3.yaml @@ -0,0 +1,105 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: Create Prowler S3 Bucket for Prowler Reports + +Parameters: + AwsOrgId: + Type: String + Description: > + Enter AWS Organizations ID. + This is used to restrict permissions to least privilege. + AllowedPattern: ^o-[a-z0-9]{10,32}$ + ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters. + Default: o-abcde12345 + S3Prefix: + Type: String + Description: > + Enter S3 Bucket Name Prefix (in lowercase). + Bucket will be named: prefix-awsaccount-awsregion (i.e., prowler-123456789012-us-east-1) + AllowedPattern: ^[a-z0-9][a-z0-9-]{1,33}[a-z0-9]$ + ConstraintDescription: > + Max 35 characters, as "-awsaccount-awsregion" will be added, and max name is 63 characters. + Can't start or end with dash. Can use numbers and lowercase letters. + Default: prowler + +Resources: + ProwlerS3: + Type: AWS::S3::Bucket + Properties: + BucketName: !Sub ${S3Prefix}-${AWS::AccountId}-${AWS::Region} + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: "AES256" + AccessControl: Private + PublicAccessBlockConfiguration: + BlockPublicAcls: True + BlockPublicPolicy: True + IgnorePublicAcls: True + RestrictPublicBuckets: True + VersioningConfiguration: + Status: Enabled + Tags: + - Key: App + Value: Prowler + Metadata: + cfn_nag: + rules_to_suppress: + - id: W35 + reason: "This S3 Bucket is only being used by the AWS Organization to download/upload prowler reports." + + ProwlerS3BucketPolicy: + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref ProwlerS3 + PolicyDocument: + Statement: + - Sid: AllowGetPutListObject + Effect: Allow + Principal: "*" + Action: + - s3:GetObject + - s3:PutObject + - s3:ListBucket + Resource: + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3} + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/* + Condition: + StringEquals: + aws:PrincipalOrgId: !Ref AwsOrgId + - Sid: DenyNonSSLRequests + Effect: Deny + Action: s3:* + Resource: + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3} + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/* + Principal: "*" + Condition: + Bool: + aws:SecureTransport: false + - Sid: DenyIncorrectEncryptionHeader + Effect: Deny + Principal: "*" + Action: s3:PutObject + Resource: + - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/* + # Allow uploads with No Encryption, as S3 Default Encryption still applies. + # If Encryption is set, only allow uploads with AES256. + Condition: + "Null": + s3:x-amz-server-side-encryption: false + StringNotEquals: + s3:x-amz-server-side-encryption: AES256 + Metadata: + cfn_nag: + rules_to_suppress: + - id: F16 + reason: "This S3 Bucket Policy has a condition that only allows access to the AWS Organization." + + +Outputs: + ProwlerS3: + Description: S3 Bucket for Prowler Reports + Value: !Ref ProwlerS3 + ProwlerS3Account: + Description: AWS Account Number where Prowler S3 Bucket resides. + Value: !Ref AWS::AccountId diff --git a/util/org-multi-account/README.md b/util/org-multi-account/README.md new file mode 100644 index 00000000..71c6faad --- /dev/null +++ b/util/org-multi-account/README.md @@ -0,0 +1,151 @@ +# Example Solution: Organizational Prowler Deployment + +Deploys [Prowler](https://github.com/toniblyx/prowler) to assess all Accounts in an AWS Organization on a schedule, creates assessment reports in HTML, and stores them in an S3 bucket. + +--- + +## Example Solution Goals + +- Using minimal technologies, so solution can be more easily adopted, and further enhanced as needed. + - [Amazon EC2](https://aws.amazon.com/ec2/), to run Prowler + - [Amazon S3](https://aws.amazon.com/s3/), to store Prowler script & reports. + - [AWS CloudFormation](https://aws.amazon.com/cloudformation/), to provision the AWS resources. + - [AWS Systems Manager Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html), Optional, but recommended, to manage the Prowler EC2 instance, without having to allow inbound ssh. +- Staying cohesive with Prowler, for scripting, only leveraging: + - Bash Shell + - AWS CLI +- Adhering to the principle of least privilege. +- Supporting an AWS Multi-Account approach + - Runs Prowler against All accounts in the AWS Organization +- ***NOTE: If using this solution, you are responsible for making your own independent assessment of the solution and ensuring it complies with your company security and operational standards.*** + +--- + +## Components + +1. [ProwlerS3.yaml](ProwlerS3.yaml) + - Creates Private S3 Bucket for Prowler script and reports. + - Enables [Amazon S3 Block Public Access](https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html) + - Enables SSE-S3 with [Amazon S3 Default Encryption](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html) + - Versioning Enabled + - Bucket Policy limits API actions to Principals from the same AWS Organization. +1. [ProwlerRole.yaml](ProwlerRole.yaml) + - Creates Cross-Account Role for Prowler to assess accounts in AWS Organization + - Allows Role to be assumed by the Prowler EC2 instance role in the AWS account where Prowler EC2 resides (preferably the Audit/Security account). + - Role has [permissions](https://github.com/toniblyx/prowler#custom-iam-policy) needed for Prowler to assess accounts. + - Role has rights to Prowler S3 from Component #1. +1. [ProwlerEC2.yaml](ProwlerEC2.yaml) + - Creates Prowler EC2 instance + - Uses the Latest Amazon Linux 2 AMI + - Uses ```t2.micro``` Instance Type + - Encrypts Root Volume with AWS Managed Key "aws/ebs" + - Uses [cfn-init](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html) for prepping the Prowler EC2 + - Installs necessary [packages](https://github.com/toniblyx/prowler#requirements-and-installation) for Prowler + - Downloads [run-prowler-reports.sh](src/run-prowler-reports.sh) script from Prowler S3 from Component #1. + - Creates ```/home/ec2-user/.awsvariables```, to store CloudFormation data as variables to be used in script. + - Creates cron job for Prowler to run on a schedule. + - Creates Prowler Security Group + - Denies inbound access. If using ssh to manage Prowler, then update Security Group with pertinent rule. + - Allows outbound 80/443 for updates, and Amazon S3 communications - + - Creates Instance Role that is used for Prowler EC2 + - Role has permissions for [Systems Manager Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) communications, and [Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html) + - Role has rights to Prowler S3 from Component #1. + - Role has rights to Assume Cross-Account Role from Component #2. +1. [run-prowler-reports.sh](src/run-prowler-reports.sh) + - Script is documented accordingly. + - Script loops through all AWS Accounts in AWS Organization, and by default, Runs Prowler as follows: + - -R: used to specify Cross-Account role for Prowler to assume to run its assessment. + - -A: used to specify AWS Account number for Prowler to run assessment against. + - -g cislevel1: used to specify cislevel1 checks for Prowler to assess + + ```bash + ./prowler/prowler -R "$ROLE" -A "$accountId" -g cislevel1 -M html + ``` + + - NOTE: Script can be modified to run Prowler as desired. + - Script runs Prowler against 1 AWS Account at a time. + - Update PARALLEL_ACCOUNTS variable in script, to specify how many Accounts to assess with Prowler in parallel. + - If running against multiple AWS Accounts in parallel, monitor performance, and upgrade Instance Type as necessary. + + ```bash + PARALLEL_ACCOUNTS="1" + ``` + + - In summary: + - Download latest version of [Prowler](https://github.com/toniblyx/prowler) + - Find AWS Master Account + - Lookup All Accounts in AWS Organization + - Run Prowler against All Accounts in AWS Organization + - Save Reports to reports prefix in S3 from Component #1 + - Report Names: date+time-accountid-report.html + +--- + +## Instructions + +1. Deploy [ProwlerS3.yaml](ProwlerS3.yaml) in the Logging Account. + - Could be deployed to any account in the AWS Organizations, if desired. + - See [How to get AWS Organization ID](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_org_details.html#orgs_view_org) + - Take Note of CloudFormation Outputs, that will be needed in deploying the below CloudFormation templates. +1. Upload [run-prowler-reports.sh](src/run-prowler-reports.sh) to the root of the S3 Bucket created in Step #1. +1. Deploy [ProwlerRole.yaml](ProwlerRole.yaml) in the Master Account + - Use CloudFormation Stacks, to deploy to Master Account, as organizational StackSets don't apply to the Master Account. + - Use CloudFormation StackSet, to deploy to all Member Accounts. See [Create Stack Set with Service-Managed Permissions](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-getting-started-create.html#stacksets-orgs-associate-stackset-with-org) + - Take Note of CloudFormation Outputs, that will be needed in deploying the below CloudFormation templates. +1. Deploy [ProwlerEC2.yaml](ProwlerEC2.yaml) in the Audit/Security Account + - Could be deployed to any account in the AWS Organizations, if desired. +1. Prowler will run against all Accounts in AWS Organization, per the schedule you provided, and set in a cron job for ```ec2-user``` + +--- + +## Post-Setup + +### Run Prowler on a Schedule against all Accounts in AWS Organization + +1. Prowler will run on the Schedule you provided. +1. Cron job for ```ec2-user``` is managing the schedule. +1. This solution implemented this automatically. Nothing for you to do. + +### Ad hoc Run Prowler against all Accounts in AWS Organization + +1. Connect to Prowler EC2 Instance + - If using Session Manager, then after login, switch to ```ec2-user```, via: ```sudo bash``` and ```su - ec2-user``` + - If using SSH, then login as ```ec2-user``` +1. Run Prowler Script + + ```bash + cd /home/ec2-user + ./run-prowler-reports.sh + ``` + +### Ad hoc Run Prowler Interactively + +1. Connect to Prowler EC2 Instance + - If using Session Manager, then after login, switch to ```ec2-user```, via: ```sudo bash``` and ```su - ec2-user``` + - If using SSH, then login as ```ec2-user``` +1. See Cross-Account Role and S3 Bucket being used for Prowler + + ```bash + cd /home/ec2-user + cat .awsvariables + ``` + +1. Run Prowler interactively. See [Usage Examples](https://github.com/toniblyx/prowler#usage) + + ```bash + cd /home/ec2-user + ./prowler/prowler + ``` + +### Upgrading Prowler to Latest Version + +1. Connect to Prowler EC2 Instance + - If using Session Manager, then after login, switch to ```ec2-user```, via: ```sudo bash``` and ```su - ec2-user``` + - If using SSH, then login as ```ec2-user``` +1. Delete the existing version of Prowler, and download the latest version of Prowler + + ```bash + cd /home/ec2-user + rm -rf prowler + git clone https://github.com/toniblyx/prowler.git + ``` diff --git a/util/org-multi-account/src/run-prowler-reports.sh b/util/org-multi-account/src/run-prowler-reports.sh new file mode 100644 index 00000000..54201b84 --- /dev/null +++ b/util/org-multi-account/src/run-prowler-reports.sh @@ -0,0 +1,109 @@ +#!/bin/bash -e +# +# Run Prowler against All AWS Accounts in an AWS Organization + +# Change Directory (rest of the script, assumes your in the ec2-user home directory) +cd /home/ec2-user || exit + +# Show Prowler Version, and Download Prowler, if it doesn't already exist +if ! ./prowler/prowler -V 2>/dev/null; then + git clone https://github.com/toniblyx/prowler.git + ./prowler/prowler -V +fi + +# Source .awsvariables (to read in Environment Variables from CloudFormation Data) +# shellcheck disable=SC1091 +source .awsvariables + +# Get Values from Environment Variables Created on EC2 Instance from CloudFormation Data +echo "S3: $S3" +echo "S3ACCOUNT: $S3ACCOUNT" +echo "ROLE: $ROLE" + +# CleanUp Last Ran Prowler Reports, as they are already stored in S3. +rm -rf prowler/output/*.html + +# Function to unset AWS Profile Variables +unset_aws() { + unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN +} +unset_aws + +# Find THIS Account AWS Number +CALLER_ARN=$(aws sts get-caller-identity --output text --query "Arn") +PARTITION=$(echo "$CALLER_ARN" | cut -d: -f2) +THISACCOUNT=$(echo "$CALLER_ARN" | cut -d: -f5) +echo "THISACCOUNT: $THISACCOUNT" +echo "PARTITION: $PARTITION" + +# Function to Assume Role to THIS Account & Create Session +this_account_session() { + unset_aws + role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$THISACCOUNT":role/"$ROLE" --role-session-name ProwlerRun --output json) + AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId) + AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey) + AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken) + export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN +} + +# Find AWS Master Account +this_account_session +AWSMASTER=$(aws organizations describe-organization --query Organization.MasterAccountId --output text) +echo "AWSMASTER: $AWSMASTER" + +# Function to Assume Role to Master Account & Create Session +master_account_session() { + unset_aws + role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$AWSMASTER":role/"$ROLE" --role-session-name ProwlerRun --output json) + AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId) + AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey) + AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken) + export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN +} + +# Lookup All Accounts in AWS Organization +master_account_session +ACCOUNTS_IN_ORGS=$(aws organizations list-accounts --query Accounts[*].Id --output text) + +# Function to Assume Role to S3 Account & Create Session +s3_account_session() { + unset_aws + role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$S3ACCOUNT":role/"$ROLE" --role-session-name ProwlerRun --output json) + AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId) + AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey) + AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken) + export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN +} + +# Run Prowler against Accounts in AWS Organization +echo "AWS Accounts in Organization" +echo "$ACCOUNTS_IN_ORGS" +PARALLEL_ACCOUNTS="1" +for accountId in $ACCOUNTS_IN_ORGS; do + # shellcheck disable=SC2015 + test "$(jobs | wc -l)" -ge $PARALLEL_ACCOUNTS && wait || true + { + START_TIME=$SECONDS + # Unset AWS Profile Variables + unset_aws + # Run Prowler + echo -e "Assessing AWS Account: $accountId, using Role: $ROLE on $(date)" + # remove -g cislevel for a full report and add other formats if needed + ./prowler/prowler -R "$ROLE" -A "$accountId" -g cislevel1 -M html + echo "Report stored locally at: prowler/output/ directory" + # Upload Prowler Report to S3 + s3_account_session + aws s3 cp prowler/output/ "$S3/reports/" --recursive --include "*.html" + TOTAL_SEC=$((SECONDS - START_TIME)) + echo -e "Completed AWS Account: $accountId, using Role: $ROLE on $(date)" + printf "Completed AWS Account: $accountId in %02dh:%02dm:%02ds" $((TOTAL_SEC / 3600)) $((TOTAL_SEC % 3600 / 60)) $((TOTAL_SEC % 60)) + echo "" + } & +done + +# Wait for All Prowler Processes to finish +wait +echo "Prowler Assessments Completed against All Accounts in the AWS Organization" + +# Unset AWS Profile Variables +unset_aws