Jenkins is an open source automation server used to accelerate the software delivery process and has become the de facto DevOps engine, specially for its scripted Jenkinsfile pipelines committed to source control. To accomplish its function, Jenkins needs to interface with some external systems, such as GitHub or, the reason for this article, AWS.

One way to grant Jenkins access to AWS is to run it on an EC2 instance with an attached IAM role. This approach has some pros:

But also some cons:

If the cons beat the pros in your case, you can adopt another strategy.

Step 1: Create IAM users and roles

Create one IAM user for each Jenkins user that needs to run AWS-related jobs. The IAM user name should be easily guessed from Jenkins user name (if it can be the same, the better) and only have the following policy attached (more on this later):

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Effect": "Allow",
   "Action": "sts:AssumeRole",
   "Resource": "*"
  }
 ]
}

Create IAM roles (the number and permissions attached to them will depend on your needs: admin, developer, sre, read-only, …) and edit trust relationship in order to allow previously generated users to assume these roles.

Step 2: Generate Access keys

Create AWS access keys for each user and store them in the Jenkins server using the AWS Credentials plugin. Make sure you set an ID to these credentials that can be easily guessed from the user name (as before, if it can be the same, the better).

Jenkins “Add Credentials” screen

From this point, we solved all cons stated above:

But, let’s face it, Jenkins is far from the most secure tool in the world. So:

How can we solve this?

Step 3: MFA to the rescue

AWS STS provides two API operations that let users pass MFA information: GetSessionToken and, what we need, AssumeRole.

Assign a MFA device to each user and attach the following policy to each role (those created in step 1):

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Sid": "ForceMFA",
   "Effect": "Allow",
   "Principal": {"AWS": "arn:aws:iam:::root"},
   "Action": "sts:AssumeRole",
   "Condition": {
    "StringEquals": { "aws:username": [  ] },
    "Bool": { "aws:MultiFactorAuthPresent": true }
   }
  }
 ]
}

This policy forces the use of MFA to assume the role. Thereby, as credentials can only be used to assume a role (do you remember the only policy attached to the users?), even if they are compromised, an attacker could do little without access to the MFA device.

I’ll explain how to make use of this approach in a Jenkins pipeline in the next post.

👉I hope you’ve enjoyed this post and I encourage you to check our blog for other posts that you might find helpful, such as What is the cloud?

Do not hesitate to contact us if you would like us to help you on your projects.

Leave a Reply

Your email address will not be published. Required fields are marked *