Securing the framework

To increase the security in your environment and enable traceability we recommend you to apply the following configuration:

  1. Applying an s3 bucket policy to attini-deployment-origin-${Region}-${AccountId} bucket.

  2. Applying an s3 bucket policy to attini-artifact-store-${Region}-${AccountId} bucket.

  3. Securely configure the Install the Attini Framework CloudFormation stack.

  4. Configure AWS CloudTrail data events on Attini resources (s3 and lambda).


IamRoleDescriptions


Bucket policies

By creating specific bucket policies on the attini-deployment-origin and the attini-artifact-store bucket you can ensure that you don’t give this access to anyone by mistake.

You should also consider adding a restriction on the s3:PutBucketPolicy API for these buckets so that no one can tamper with your security configuration.

Warning

By restricting access to the s3:PutBucketPolicy API you can lock yourself out of your buckets so be careful. You should never rely on AWS SSO Roles to manage this API because those roles might be replaces.

Attini deployment origin bucket

Anyone that have s3:PutObject access to the attini-deployment-origin bucket will be able to do deployments which is a very high privilege.

The attini-deployment-origin bucket policy need to allow the following:

  1. Give access to anyone that need to start a deployment, ex your build server or your DevOps personal.

  2. Allow the Attini framework to read the distribution (s3 object)

Deployment origin bucket policy example:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RestrictDeploymentAccess",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::attini-deployment-origin-${Region}-${AccountId}/*",
            "Condition": {
                "ArnNotEquals": {
                    "aws:PrincipalArn": "{ARN of the user or role that should be able to deploy}"
                }
            }
        }
    ]
}

Attini artifact store bucket

Anyone that have s3:PutObject access to attini-artifact-store bucket will be able to replace files that will be used in the next re-run of a deployment, that way they could tamper with your cloud environment. To manage this risk, it’s important to have cloud trail object level logging activated on the attini-artifact-store bucket and use least privilege IAM roles for your deployment steps.

If you have any distributions with sensitive information, you can also use bucket policies to restrict read access to specific paths in the attini buckets.

Artifact store policy example:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RestrictAccessToArtifactStore",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::attini-artifact-store-${Region}-${AccountId}/*",
            "Condition": {
                "ArnNotEquals": {
                    "aws:PrincipalArn": "arn:aws:iam::${AccountId}:role/attini/attini-init-deploy-lambda-role-${Region}"
                }
            }
        }
    ]
}

Bucket policies CloudFormation example

AWSTemplateFormatVersion: 2010-09-09
Description: Bucket policies for the Attini Framework

Resources:
  AttiniDeploymentOriginBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Sub attini-deployment-origin-${AWS::Region}-${AWS::AccountId}
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: RestrictDeploymentAccess
            Principal: "*"
            Action:
              - "s3:PutObject"
            Effect: Deny
            Resource:
              - !Sub arn:aws:s3:::attini-deployment-origin-${AWS::Region}-${AWS::AccountId}/*
            Condition:
              ArnNotLike:
                !Sub aws:PrincipalArn: arn:aws:iam::${AWS::AccountId}:role/aws-reserved/sso.amazonaws.com/${AwsSsoRegion}/AWSReservedSSO_AdministratorAccess_*
                # Fill in any IAM Role or User that should be able to do deployments



  AttiniArtifactStoreBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Sub attini-artifact-store-${AWS::Region}-${AWS::AccountId}
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: RestrictAccessToArtifactStore
            Principal: "*"
            Action:
              - "s3:PutObject"
            Effect: Deny
            Resource:
              - !Sub arn:aws:s3:::attini-artifact-store-${AWS::Region}-${AWS::AccountId}/*
            Condition:
              ArnNotEquals:
                aws:PrincipalArn: !Sub arn:aws:iam::${AWS::AccountId}:role/attini/attini-init-deploy-lambda-role-${AWS::Region}
                # Fill in any IAM Role or User that should be able to manipulate files in the artifact store

How can I configure the S3 Bucket policies using AWS SSO Roles?

If you use AWS SSO, the underlying AWS IAM roles have unpredictable names making it hard to configure. AWS SSO also replaces the roles sometimes meaning that you should never use AWS SSO Roles as a Principal in any IAM policy because that trust can be broken and sometimes hard to restore.

To reference an AWS SSO Role in an IAM/S3 Policy in a maintainable manner you have 2 alternatives:

  1. Use an ArnLike/ArnNotLike condition in your bucket policy.

  2. Use Attini’s Open Source project that will provide the SSO Role arn via AWS ParameterStore.

ArnLike/ArnNotLike condition

Warning

AWS have in no way guaranteed that the naming convention for these roles, so there is a risk that they will change and break your permission configuration. If you are worried about this, please see the second option (Attini’s SSO Role name mapper).

Hint: To get the current IAM role arn you are using you can find your current AWS role in the IAM console.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RestrictDeploymentAccess",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::attini-deployment-origin-${Region}-${AccountId}/*",
            "Condition": {
                "ArnNotLike": {
                    "aws:PrincipalArn": "arn:aws:iam::${AccountId}:role/aws-reserved/sso.amazonaws.com/${SsoRegion}/AWSReservedSSO_${PermissionSetName}_*"
                }
            }
        }
    ]
}

Attini’s SSO Role name mapper

As a workaround, we built an open-source project that will populate AWS SSM ParameterStore with the arn of the SSO roles. Find more information in the GitHub repository.

Warning

If you configure the s3:PutBucketPolicy API in a bucket policy, make sure you always have a backup Principal that can update the BucketPolicy if the AWS SSO role is replaced.

When you have all the AWS SSO roles a with predictable names in AWS SSM ParameterStore you can use CloudFormations SSM ParameterStore integration to automatically configure your bucket policy.

Warning

Anyone that can update these parameters use can tamper with your security configuration.


Securely configure the attini-setup CloudFormation stack and my deployment plans.

This chapter is about configuring the attini-setup CloudFormation stack and the underlying Attini DeploymentPlans <api-reference_attini-deployment-plan>.

When configuring a CI/CD system you often have a “catch 22” situation. You need certain resources (in Attini’s case it is IAM roles) for the CI/CD system to work, but you want to use the CI/CD system to create the resources you need.

To work around this you can do one of the following:

  1. Create the resources yourself, then deploy the Install the Attini Framework CloudFormation stack using the IAM roles you just created.

  2. Install the Attini Framework with high privileges, then use Attini to create and configure the required resources for you. After that you can re-configure the Attini Framework with your least privilege IAM roles.

Keep in mind that you can reconfigure attini-setup at any time so you can adjust add or remove privileges as needed.


InitDeployRole

InitDeploy Role is configured by the CreateInitDeployDefaultRole and InitDeployRoleArn parameters in the attini-setup

These 2 parameters configure the access for the InitDeploy. Meaning the CloudFormation stack that Attini automatically creates when a distribution is deployed. This CloudFormation stack is intended to create CI/CD resources like (Attini deployment plans, AWS CodeBuild Projects, CloudFormation IAM stack roles etc).

Note

If you set the “CreateInitDeployDefaultRole”=”true” attini-setup will create a default role with high privileges to make it easy to get started with the Attini Framework, however, in production environments we recommend that you create your own InitDeploy Role. Then you can control which resources can be created by the Attini InitDeploy.

If you create an IAM Role and configure the InitDeployRoleArn parameter, this role have to:

  1. Be a AWS Lambda service role. Assume role document:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    
  2. Add whatever privileges you might need for your deployment resources (to create Attini deployment plans this role needs permission to create and update AWS StepFunction and CloudWatch events).

Note

attini-setup will add an least privilege inline IAM policy to your InitDeploy Role that will enable it to work with the Attini Framework.


DeploymentPlan Role

The DeploymentPlan Role is configured via the RoleArn parameter in the Attini::Deploy::DeploymentPlan resource. By configuring this role you can allow the Attini DeploymentPlan to do anything you might need in AWS while sticking to the least privilege principal.

The “CreateDeploymentPlanDefaultRole” parameter in the attini-setup sets up a default role for the DeploymentPlans with quite broad permission to make the Attini Framework a bit easier to work with. In production environments we recommend to set “CreateDeploymentPlanDefaultRole”=”false”


ExecutionRole

The ExecutionRole is configured via the ExecutionRoleArn parameter in the Attini::Deploy::DeploymentPlan AttiniCfn resource. By configuring this role you can allow the AttiniCfn step to do anything you might need in AWS while sticking to the least privilege principal.

These ExecutionRole permissions are automatically transferred to the AWS CloudFormation stack that the AttiniCfn is deploying unless you also specify a StackRoleArn, then CloudFormation will update its resources with the StackRole credentials. So if the StackRoleArn is configured, the ExecutionRole will only need permissions to create/update the CloudFormation stack.

Configuring the ExecutionRole you can also deploy CloudFormation stacks to other AWS Accounts, find more info here.


StackRole

The StackRole is configured via the StackRoleArn parameter in the Attini::Deploy::DeploymentPlan AttiniCfn resource. By configuring this role you can apply a AWS CloudFormation service role.

To allow a Attini DeploymentPlan to apply a stack role you have to configure AwsServiceRolesContainsString parameter in the attini-setup correctly. If you configure an ExecutionRole in combination with StackRole, the ExecutionRole will need iam:PassRole permission to the StackRole.

StackRoles can be a powerful tool if you ever need to give out granular access to different members of your organization.

For example, let’s say that you have a development team in your organization that should be able to update a few specific ECS Services. Instead of giving the developers esc:UpdateService permission, you can give them cloudformation:UpdateStack permission and then control which stacks via AWS IAM Resource or Condition configuration.

Then you give the StackRole Permissions to do esc:UpdateService. This lets your staff escalate their own privileges in a controlled way.

If you have a few CloudFormation Parameters that you want to be manually configured/maintained, please see fallback configuration in the CloudFormation ParameterValue config.

Warning

This is actual privilege escalation which comes with certain risks. If the StackRole for example have the permission to do iam:UpdateRole, nothing will stop it from giving the IAM Role AdministerAccess and give a potential hacker the ability to assume the role. So please be careful with IAM permission on StackRoles.

A way to work around the issue is with IAM Permissions Boundarys in combination with IAM Condition key iam:PermissionsBoundary.

ExecutionRole vs StackRole

From an Attini deployment perspective, it does not matter if ExecutionRoles or StackRoles are being used, however, StackRoles comes with extra capability’s which often create unnecessary complexity.

Therefore Attini recommends you to use ExecutionRoles as default and only apply a StackRoles when you have a specific use case for it.


Activate CloudTrail

Activating CloudTrail on the Attini resources gives you a much better audibility and its therefore highly recommend. If you have activated data events on all S3 buckets and Lambdas you are already done.

Warning

Be aware that data events comes with an additional cost

We at Attini strongly recommend that you activate CloudTrail data events on the following Attini resources to give your proper traceability:

  • The attini-deployment-origin S3 bucket

  • The attini-artifact-store S3 bucket

  • The attini-action Lambda

  • The attini-init-deploy Lambda

  • The attini-auto-update Lambda

  • The attini-step-guard Lambda

This will log any new deploys which is critical information if you are looking for an intruder, it will also trace any tampering with your current artifacts or trigger of Lambda functions.

Note

These data events does not appear in the normal cloud trail logs, you have to find them in the logs shipped to s3.

Minimum recommend CloudTrail logging:

[
  {
    "name": "Minimum logs",
    "fieldSelectors": [
      {
        "field": "eventCategory",
        "equals": [
          "Data"
        ]
      },
      {
        "field": "resources.type",
        "equals": [
          "AWS::S3::Object"
        ]
      },
      {
        "field": "resources.ARN",
        "startsWith": [
          "arn:aws:s3:::attini-deployment-origin",
          "arn:aws:s3:::attini-artifact-store",
          "arn:aws:s3:::prod-attini-support-logs",
          "arn:aws:s3:::acc-attini-support-logs"
        ]
      }
    ]
  },
  {
    "name": "Lambda events",
    "fieldSelectors": [
      {
        "field": "eventCategory",
        "equals": [
          "Data"
        ]
      },
      {
        "field": "resources.type",
        "equals": [
          "AWS::Lambda::Function"
        ]
      },
      {
        "field": "resources.ARN",
        "endsWith": [
          "attini-action",
          "attini-init-deploy",
          "attini-auto-update",
          "attini-step-guard"
        ]
      }
    ]
  }
]

Security considerations

Administrating our IT environments with a high level of automation brings many advantages, and done right it can increase your security by:

  1. Decreasing the privileges your staff need in their day to day activities

  2. Easier to maintain a minimum privileges access model

  3. Easier to implement tests that can find vulnerabilities

  4. Easier to update and patch the underlying infrastructure

  5. Easier to implement and maintain security resources like Alarms and AWS Config Rules

The drawback to this is that the tools you need to Administer you IT environment needs Admin access to your resources, which of course is associated with risks.

Anyone that can put distributions (s3 files) into attini-deployment-origin bucket can use the framework to deploy new IAM roles, which means that in the worst case scenario someone can deploy a new admin role that only they can assume and therefore escalate their own privileges.

Warning

The possibility to escalate privileges is always a risk you take when you automate the creation and updates of IAM entities (Roles, Users, and Policy). For this reason, you have to be very careful when you use deployment tools for IAM configuration, ex CloudFormation stack roles.

For example, if you give a CloudFormation stack IAM privileges and then give someone access to the “CloudFormation:StackUpdate” API, that person can give themselves AdministratorAccess by creating or updating a role that is created by the CloudFormation stack. For this reason we at Attini always recommend you to have separate CloudFormation stacks for security-related resources (IAM, KMS, EC2 Security groups etc) so that you can easily separate access to them.

Another Recommended way to avoid escalation of privileges is AWS Permissions boundaries.