From 72baecdfcc4930082fa139eac825af985b065cb2 Mon Sep 17 00:00:00 2001 From: Kostas Stamatakis Date: Tue, 16 Apr 2024 15:22:52 +0300 Subject: [PATCH] Cloudformation direct access key (#2123) --- .github/workflows/cloudformation-ci.yml | 67 +++++++ deploy/aws/cloudbeat-aws.yml | 2 +- ...nt-direct-access-key-cspm-organization.yml | 174 ++++++++++++++++++ .../elastic-agent-direct-access-key-cspm.yml | 39 ++++ scripts/publish_cft.sh | 2 + 5 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 deploy/cloudformation/elastic-agent-direct-access-key-cspm-organization.yml create mode 100644 deploy/cloudformation/elastic-agent-direct-access-key-cspm.yml diff --git a/.github/workflows/cloudformation-ci.yml b/.github/workflows/cloudformation-ci.yml index d68d5f887f..9296a75150 100644 --- a/.github/workflows/cloudformation-ci.yml +++ b/.github/workflows/cloudformation-ci.yml @@ -9,6 +9,13 @@ on: paths: - deploy/cloudformation/*.yml - .github/workflows/cloudformation-ci.yml + push: + branches: + - main + - "[0-9]+.[0-9]+" + paths: + - deploy/cloudformation/*.yml + - .github/workflows/cloudformation-ci.yml env: WORKING_DIR: deploy/test-environments @@ -115,3 +122,63 @@ jobs: terraform destroy --auto-approve -target="module.ec_deployment" -target="module.ec_project" aws cloudformation delete-stack --stack-name ${{ env.CNVM_STACK_NAME }} aws cloudformation wait stack-delete-complete --stack-name ${{ env.CNVM_STACK_NAME }} + + + Deploy-CloudFormation-DirectKeys: + name: "Deploy CloudFormation DirectKeys" + runs-on: ubuntu-latest + timeout-minutes: 40 + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Hermit Environment + uses: ./.github/actions/hermit + with: + init-tools: 'true' + + - name: Set up unique deployment names + run: | + suffix="$(date +%s | tail -c 3)" + echo "DIRECT_KEY_STACK_NAME=direct-key-stack-pr${{ github.event.number }}-$suffix" >> $GITHUB_ENV + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_TEST_ACC }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_TEST_ACC }} + aws-region: "eu-west-1" + + - name: Deploy CloudFormation stack + env: + CF_FILE: 'deploy/cloudformation/elastic-agent-direct-access-key-cspm.yml' + run: | + aws cloudformation validate-template --template-body file://${{ env.CF_FILE }} + aws cloudformation create-stack --stack-name ${{ env.DIRECT_KEY_STACK_NAME }} --template-body file://${{ env.CF_FILE }} --capabilities CAPABILITY_NAMED_IAM + aws cloudformation wait stack-create-complete --stack-name ${{ env.DIRECT_KEY_STACK_NAME }} + + - name: Get Direct Keys + id: direct-keys + shell: bash + run: | + BODY="$(aws cloudformation describe-stacks --stack-name ${{ env.DIRECT_KEY_STACK_NAME }} --query 'Stacks[0].Outputs' --output json)" + NEW_ACCESS_KEY_ID="$(echo "${BODY}" | jq -r '.[] | select(.OutputKey | test("AccessKeyId")) | .OutputValue')" + echo "::add-mask::$NEW_ACCESS_KEY_ID" + NEW_SECRET_ACCESS_KEY="$(echo "${BODY}" | jq -r '.[] | select(.OutputKey | test("SecretAccessKey")) | .OutputValue')" + echo "::add-mask::$NEW_SECRET_ACCESS_KEY" + echo "NEW_ACCESS_KEY_ID=${NEW_ACCESS_KEY_ID}" >> $GITHUB_OUTPUT + echo "NEW_SECRET_ACCESS_KEY=${NEW_SECRET_ACCESS_KEY}" >> $GITHUB_OUTPUT + + - name: Run AWS integration tests + uses: ./.github/actions/aws-ci + with: + elk-version: ${{ env.ELK_VERSION }} + aws-access-key-id: ${{ steps.direct-keys.outputs.NEW_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ steps.direct-keys.outputs.NEW_SECRET_ACCESS_KEY }} + aws-account-type: single-account + + - name: Cleanup Environment + if: always() + run: | + aws cloudformation delete-stack --stack-name ${{ env.DIRECT_KEY_STACK_NAME }} + aws cloudformation wait stack-delete-complete --stack-name ${{ env.DIRECT_KEY_STACK_NAME }} diff --git a/deploy/aws/cloudbeat-aws.yml b/deploy/aws/cloudbeat-aws.yml index f61ee84230..b4a773a7ef 100644 --- a/deploy/aws/cloudbeat-aws.yml +++ b/deploy/aws/cloudbeat-aws.yml @@ -6,7 +6,7 @@ cloudbeat: credentials: access_key_id: ${AWS_ACCESS_KEY_ID:""} secret_access_key: ${AWS_SECRET_ACCESS_KEY:""} - account_type: ${AWS_ACCOUNT_TYPE:""} + account_type: ${AWS_ACCOUNT_TYPE:""} type: cloudbeat/cis_aws # Defines how often an event is sent to the output period: 30s diff --git a/deploy/cloudformation/elastic-agent-direct-access-key-cspm-organization.yml b/deploy/cloudformation/elastic-agent-direct-access-key-cspm-organization.yml new file mode 100644 index 0000000000..c58ab2b66c --- /dev/null +++ b/deploy/cloudformation/elastic-agent-direct-access-key-cspm-organization.yml @@ -0,0 +1,174 @@ +AWSTemplateFormatVersion: "2010-09-09" + +Description: Creates elastic-agent cspm user, role, and access key, and outputs the access key + +Parameters: + OrganizationalUnitIds: + Description: | + Comma-separated list of organizational units to deploy the IAM roles to. + Specify the unique IDs of the organizational units where the roles should be deployed. + Example: ou-abc123,ou-def456,ou-ghi789 + Type: CommaDelimitedList + AllowedPattern: ^(ou-[0-9a-z]{4,32}-[a-z0-9]{8,32}|r-[0-9a-z]{4,32})$ + + ScanManagementAccount: + Description: | + When set to "Yes", the Management Account resources will be scanned, + regardless of selected Organizational Unit IDs. Likewise, when set to + "No", the Management Account resources will not be scanned, even if + the Management Account belongs to a selected Organizational Unit. + Type: String + AllowedValues: + - "Yes" + - "No" + Default: "Yes" + ConstraintDescription: Must specify "Yes" or "No" + +Conditions: + ScanManagementAccountEnabled: !Equals + - !Ref ScanManagementAccount + - "Yes" + +Resources: + ElasticCSPMUser: + Type: AWS::IAM::User + Properties: + UserName: !Join + - '-' + - - elasticagent-user-cspm + - !Select + - 2 + - !Split + - / + - !Ref AWS::StackId + Path: / + + CloudbeatRootRole: + Type: AWS::IAM::Role + Properties: + RoleName: cloudbeat-root + Description: Role that cloudbeat uses to assume roles in other accounts + Tags: + - Key: cloudbeat_scan_management_account + Value: !Ref ScanManagementAccount + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + AWS: !Ref AWS::AccountId + Action: + - sts:AssumeRole + - Effect: Allow + Principal: + AWS: !GetAtt ElasticCSPMUser.Arn + Action: + - sts:AssumeRole + - Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: cloudbeat-root-permissions + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - iam:GetRole + - iam:ListAccountAliases + - iam:ListGroup + - iam:ListRoles + - iam:ListUsers + Resource: '*' + - Effect: Allow + Action: + - organizations:List* + - organizations:Describe* + Resource: '*' + - Effect: Allow + Action: + - sts:AssumeRole + Resource: '*' + + CloudbeatRoleStackSet: + Type: AWS::CloudFormation::StackSet + Properties: + StackSetName: cloudbeat-role-stackset + Description: StackSet for deploying the cloudbeat-securityaudit IAM role to member accounts in the specified organizational units. + AutoDeployment: + Enabled: true + RetainStacksOnAccountRemoval: false + Capabilities: + - CAPABILITY_NAMED_IAM + ManagedExecution: + Active: true + Parameters: + - ParameterKey: RootRoleArn + ParameterValue: !GetAtt CloudbeatRootRole.Arn + PermissionModel: SERVICE_MANAGED + StackInstancesGroup: + - DeploymentTargets: + OrganizationalUnitIds: !Ref OrganizationalUnitIds + Regions: + - !Ref AWS::Region + TemplateBody: | + AWSTemplateFormatVersion: '2010-09-09' + Description: Creates IAM roles needed for multi-account access + Parameters: + RootRoleArn: + Type: String + Resources: + CloudbeatMemberRole: + Type: 'AWS::IAM::Role' + Properties: + RoleName: cloudbeat-securityaudit + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + AWS: !Ref RootRoleArn + Action: + - sts:AssumeRole + Path: / + ManagedPolicyArns: + - arn:aws:iam::aws:policy/SecurityAudit + + CloudbeatManagementAccountAuditRole: + Type: AWS::IAM::Role + Properties: + RoleName: cloudbeat-securityaudit + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + AWS: !GetAtt CloudbeatRootRole.Arn + Action: + - sts:AssumeRole + Path: / + ManagedPolicyArns: + - arn:aws:iam::aws:policy/SecurityAudit + Condition: ScanManagementAccountEnabled + + ElasticCSPMAccessKey: + Type: AWS::IAM::AccessKey + Properties: + UserName: !Ref ElasticCSPMUser + +Outputs: + AccessKeyId: + Description: Access Key ID + Value: !Ref ElasticCSPMAccessKey + Export: + Name: AccessKeyId + + SecretAccessKey: + Description: Secret Access Key + Value: !GetAtt ElasticCSPMAccessKey.SecretAccessKey + Export: + Name: SecretAccessKey diff --git a/deploy/cloudformation/elastic-agent-direct-access-key-cspm.yml b/deploy/cloudformation/elastic-agent-direct-access-key-cspm.yml new file mode 100644 index 0000000000..896100c37f --- /dev/null +++ b/deploy/cloudformation/elastic-agent-direct-access-key-cspm.yml @@ -0,0 +1,39 @@ +AWSTemplateFormatVersion: "2010-09-09" + +Description: Creates elastic-agent cspm user, role, and access key, and outputs the access key + +Parameters: {} + +Resources: + ElasticCSPMUser: + Type: AWS::IAM::User + Properties: + UserName: !Join + - '-' + - - elasticagent-user-cspm + - !Select + - 2 + - !Split + - / + - !Ref AWS::StackId + ManagedPolicyArns: + - arn:aws:iam::aws:policy/SecurityAudit + Path: / + + ElasticCSPMAccessKey: + Type: AWS::IAM::AccessKey + Properties: + UserName: !Ref ElasticCSPMUser + +Outputs: + AccessKeyId: + Description: Access Key ID + Value: !Ref ElasticCSPMAccessKey + Export: + Name: AccessKeyId + + SecretAccessKey: + Description: Secret Access Key + Value: !GetAtt ElasticCSPMAccessKey.SecretAccessKey + Export: + Name: SecretAccessKey diff --git a/scripts/publish_cft.sh b/scripts/publish_cft.sh index d4a8b28887..7921c57f90 100755 --- a/scripts/publish_cft.sh +++ b/scripts/publish_cft.sh @@ -18,3 +18,5 @@ version=$(grep defaultBeatVersion version/version.go | cut -f2 -d "\"") upload_file deploy/cloudformation/elastic-agent-ec2-cnvm.yml "cloudformation-cnvm" "$version" upload_file deploy/cloudformation/elastic-agent-ec2-cspm.yml "cloudformation-cspm-single-account" "$version" upload_file deploy/cloudformation/elastic-agent-ec2-cspm-organization.yml "cloudformation-cspm-organization-account" "$version" +upload_file deploy/cloudformation/elastic-agent-direct-access-key-cspm.yml "cloudformation-cspm-direct-access-key-single-account" "$version" +upload_file deploy/cloudformation/elastic-agent-direct-access-key-cspm-organization.yml "cloudformation-cspm-direct-access-key-organization-account" "$version"