Securing Attini

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

  1. Applying a s3 bucket policy to attini-deployment-origin-${Region}-${AccountId} bucket.
  2. Applying a s3 bucket policy to attini-artifact-store-${Region}-${AccountId} bucket.
  3. Securely configure the attini-setup CloudFormation stack.
  4. Configure AWS CloudTrail data events on Attini resources (s3 and lambda).

IamRoleDescriptions 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 replaced.

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 action.

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 can use ArnLike/ArnNotLike condition in your bucket policy.

ArnLike/ArnNotLike condition

Warning

AWS has 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.

Therefore, we recommend always having a break the glass User/Role that does not rely on AWS SSO.

Hint: To get the current IAM role arn you are using, you can find your current AWS role in the IAM console our using the AWS CLI command:

aws sts get-caller-identity
Example
{
    "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}_*"
                }
            }
        }
    ]
}

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

This chapter is about configuring the attini-setup CloudFormation stack and Attini DeploymentPlans.

When configuring a CI/CD system, you often have a “catch 22” situation. You need certain resources (in Attinis’ 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 attini-setup CloudFormation stack using the IAM roles you just created.
  2. Deploy attini-setup 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 at any time, so you can adjust, add or remove privileges as needed.


InitDeployRole

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

These 2 parameters configure the access for the InitDeploy, which is the CloudFormation stack that Attini automatically creates when a distribution is deployed. This CloudFormation stack is intended to create CI/CD resources like the deployment plan.

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 Attini. 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 a 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 deployment plan 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 deployment plans with quite broad permission to make the Attini Framework a bit easier to work with. In production environments, we recommend setting “CreateDeploymentPlanDefaultRole”=“false”


ExecutionRole

The ExecutionRole is configured via the ExecutionRoleArn parameter in the AttiniCfn step. 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 AttiniCfn step. By configuring this role, you can apply an AWS CloudFormation service role.

To allow a Attini deployment plan 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 be careful with IAM permissions on StackRoles.

A way to work around the issue is with IAM Permissions Boundaries 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, StackRole comes with extra capabilities which often create unnecessary complexity.

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


Activate CloudTrail

Activating CloudTrail on the Attini resources gives you a much better auditability, and it’s therefore highly recommended. If you have activated data events on all S3 buckets and Lambdas, you are already done.

Note

Be aware that data can generate additional cost

We at Attini 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 deployments which are critical information if you are looking for an intruder, it will also trace any tampering with your current artifacts or lambda functions invocations.

Note

These data events do 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 your IT environment need 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.

For example, if you give a CloudFormation stack a Service role with IAM privileges, 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.