Securing Jenkins access to AWS (part II)

 In aws, DevOps, Security

Recommended setup

If you followed the steps in my previous post, you have:

  • IAM users than can only assume a role
  • AWS access keys for those users stored in Jenkins (using AWS Credentials plugin)
  • An MFA device assigned to each user
  • A condition that forces MFA when assuming roles

Example pipeline

With all this, we can now run a pipeline that takes advantage of this settings. Something like:

def getAWSUser() {
  wrap([$class: "BuildUser"]) {
    return env.BUILD_USER_ID
  }
}

def getCredentialsId() {
  wrap([$class: "BuildUser"]) {
    return env.BUILD_USER_ID
  }
}

def assumeRole(String credentials, String userName,
  String accountId = "YOUR_AWS_ACCOUNT_ID", String role = "role_to_be_assumed") {
  def String trustedAccount = "YOUR_AWS_ACCOUNT_ID"

  def mfa = input(
    message: "Enter MFA Token",
    parameters: [[$class: 'StringParameterDefinition', name: 'mfa', trim: true]]
  )

  withCredentials([[
    $class: 'AmazonWebServicesCredentialsBinding',
    credentialsId: "${credentials}",
    accessKeyVariable: 'AWS_ACCESS_KEY_ID',
    secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'
  ]]) {
    return sh(script: """
      aws sts assume-role \
        --role-arn arn:aws:iam::${accountId}:role/${role} \
        --serial-number arn:aws:iam::${trustedAccount}:mfa/${userName} \
        --query 'Credentials' \
        --token-code ${mfa} \
        --role-session-name ${userName}
    """, returnStdout: true)
  }
}

pipeline {
  agent any

  stages {
    stage('Setup') {
      steps {
        script {
          AWSUser = getAWSUser()
          credentialsId = getCredentialsId()
        }
      }
    }

    stage('Get credentials') {
      steps {
        script {
          jsonCreds = assumeRole("${credentialsId}", "${AWSUser}")
          creds = readJSON text: "${jsonCreds}"
        }
      }
    }

    stage('Use credentials') {
      steps {
        withEnv([
            "AWS_ACCESS_KEY_ID=${creds.AccessKeyId}",
            "AWS_SECRET_ACCESS_KEY=${creds.SecretAccessKey}",
            "AWS_SESSION_TOKEN=${creds.SessionToken}"
          ]) {
            sh """
              aws sts get-caller-identity
            """
        }
      }
    }
  }
}

When running the pipeline, Jenkins will ask you to enter your MFA code in order to assume the desired role:

And after that, use those credentials to perform whatever AWS-related stuff:

Key points

  • If you followed my advice, your IAM user name and credentials ID will be the same as your Jenkins user name. Otherwise, you’ll have to modify getAWSUser and/or getCredentialsId functions accordingly.
  • This example assumes a privileged role in the same account as the IAM user, but the setup can be used to assume a role in another account. Just pass the destination account ID as a parameter to the assumeRole function call.
Recent Posts

Leave a Comment

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.