How can I (securely) download a private S3 asset onto a new EC2 instance with cloudinit?

19,506

Solution 1

Amazon recently announced a new feature where you can give "IAM roles" to your EC2 instances. This makes it fairly easy to allow specific instances to have permission to read specific S3 resources.

Here's their blog post announcing the new feature:

http://aws.typepad.com/aws/2012/06/iam-roles-for-ec2-instances-simplified-secure-access-to-aws-service-apis-from-ec2.html

Here's the section in the EC2 documentation:

http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/UsingIAM.html#UsingIAMrolesWithAmazonEC2Instances

Here's the section in the IAM documentation:

http://docs.amazonwebservices.com/IAM/latest/UserGuide/WorkingWithRoles.html

IAM roles make the credentials available to the instance through HTTP, so any users or processes running on the instance can see them.

Solution 2

To update the answers to this question a bit:

Along with IAM roles, the new AWS command-line client makes fetching these assets trivial. It will automatically pull AWS credentials bestowed via IAM from the environment and handle the refreshing of those credentials.

Here's an example of fetching a single asset from a secure S3 bucket in a user-data script:

# Install the AWS command-line tools
pip install awscli

# Fetch the asset
aws s3 cp --region us-east-1 s3://my-private-bucket/a-folder/an-asset.zip /some/destination

Simple as that. You can also copy entire directory contents from S3 and uploaded, etc. See the reference material for more details and options.

Solution 3

To securely download a private S3 asset onto a new EC2 instance, you should use IAM Roles for EC2 to grant the necessary S3 permission to your EC2 instance, then call aws s3 cp in your instance's UserData cloudinit script to download the asset.

To setup an IAM Role for EC2 from a CloudFormation template, use the AWS::IAM::InstanceProfile resource, referencing an AWS::IAM::Role resource with an AssumeRolePolicyDocument delegating access to ec2.amazonaws.com, with a Policy designed to grant least privilege (in this case, allowing 's3:GetObject' only for the specific S3 asset being downloaded).

Here's a full example template that downloads an S3 asset onto a new EC2 instance using cloudinit, returning its contents as a Stack Output:

Launch Stack

Description: (securely) download a private S3 asset onto a new EC2 instance with cloudinit
Parameters:
  S3Bucket:
    Description: S3 bucket name
    Type: String
  S3Key:
    Description: S3 object key
    Type: String
Mappings:
  # amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
  RegionMap:
    us-east-1:
      "64": "ami-9be6f38c"
Resources:
  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal: {Service: [ ec2.amazonaws.com ]}
          Action: ["sts:AssumeRole"]
      Path: /
      Policies:
      - PolicyName: EC2Policy
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: ['s3:GetObject']
            Resource: !Sub 'arn:aws:s3:::${S3Bucket}/${S3Key}'
  RootInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles: [ !Ref EC2Role ]
  WebServer:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap [ RegionMap, !Ref "AWS::Region", 64 ]
      InstanceType: m3.medium
      IamInstanceProfile: !Ref RootInstanceProfile
      UserData:
        "Fn::Base64":
          !Sub |
            #!/bin/bash
            DATA=$(aws s3 cp s3://${S3Bucket}/${S3Key} -)
            /opt/aws/bin/cfn-signal \
              -e $? \
              -d "$DATA" \
              '${Handle}'
  Handle:
    Type: AWS::CloudFormation::WaitConditionHandle
  Wait:
    Type: AWS::CloudFormation::WaitCondition
    Properties:
      Handle: !Ref Handle
      Timeout: 300
Outputs:
  Result:
    Value: !GetAtt Wait.Data

Solution 4

An instance with an IAM Role has temporary security credentials that are automatically rotated. They're available via http at http://169.254.169.254/latest/meta-data/iam/security-credentials/RoleName, where RoleName is whatever you called your role. So they're easy to get from your instance, but they expire regularly.

Using them is a bit tough. CloudFormation can't use temporary credentials directly. The Amazon Linux AMI has Python boto installed, and it's now smart enough to find and use those credentials for you automatically. Here's a one-liner you can put in a script to fetch a file from S3 bucket b, key k to local file f:

python -c "import boto;boto.connect_s3().get_bucket('b').get_key('k').get_contents_to_filename('f')"

boto finds and uses the role's temporary credentials for you, which makes it really easy to use.

Share:
19,506

Related videos on Youtube

Christopher
Author by

Christopher

Updated on June 04, 2022

Comments

  • Christopher
    Christopher almost 2 years

    I'm using CloudFormation to manage a Tomcat webserver stack but am tired of doing raw AMI management for new application versions. I'd like to move in the direction of Chef but don't have the time right now. Instead, I'm trying to conquer a simple problem in webserver instantiation: How can I download a "current" WAR when new machines spin-up?

    My thought was to utilize a private S3 bucket and cloudinit, but I'm a little stumped by what to do with IAM credentials. I could put them in the template's user data, but I'm loathe to do so, particularly because I'm version controlling that file. The only alternative I can think of is to use environment variables in the AMI itself. They'd have to be plaintext, but... eh, if you can break into my instance, you could zip up and download my entire webserver. As long as the IAM user isn't reused for anything else and is rotated regularly, it seems like a reasonable way to solve the problem. Am I missing anything? How can I securely download a private S3 asset using cloudinit?

  • TeaCupApp
    TeaCupApp over 10 years
    What is the "key k"? is it a file with temporary credentials?
  • TeaCupApp
    TeaCupApp over 10 years
    For future users, the key k is the file name you want from S3. If you have folder structure then the key will be, foldername/file.sh
  • Christopher
    Christopher over 10 years
    Yeah the new cli tools are based on botocore, which appears to be the groundwork for boto v3. Overall botocore is a great interface if you know the structure of the AWS API.
  • AJB
    AJB almost 9 years
    Excellent, succinct answer @James van Dyke. But I still have doubts about how secure this is, illustrated in my similar question here: stackoverflow.com/questions/29932355/…
  • pt12lol
    pt12lol almost 7 years
    Is there any complete from-beginning-to-end AWS CLI example or tutorial showing how to setup and use IAM mechanism? This references look so complicated and complete example following command-by-command would be very appreciated.