Как я могу (безопасно) загрузить частный актив S3 на новый экземпляр EC2 с облачным?

Я использую CloudFormation для управления стеком веб-серверов Tomcat, но я устал от выполнения управления AMI для новых версий приложений. Я бы хотел двигаться в направлении шеф-повара, но сейчас у меня нет времени. Вместо этого я пытаюсь покорить простую проблему с созданием веб-сервера: как загрузить "текущую" WAR при росте новых машин?

Моя мысль заключалась в использовании частного ведра S3 и cloudinit, но я немного озадачен тем, что делать с учетными данными IAM. Я мог бы помещать их в пользовательские данные шаблона, но я не хочу этого делать, особенно потому, что я контролирую этот файл. Единственная альтернатива, которую я могу придумать, - это использовать переменные среды в AMI. Они должны быть откровенными текстами, но... а если вы можете проникнуть в мой экземпляр, вы можете застегнуть и загрузить весь мой веб-сервер. До тех пор, пока пользователь IAM не будет повторно использоваться для чего-либо еще и будет регулярно вращаться, представляется разумным способом решить проблему. Я что-то пропустил? Как я могу безопасно загружать частный ресурс S3 с помощью cloudinit?

Ответ 1

Недавно Amazon анонсировала новую функцию, в которой вы можете дать "роли IAM" в своих экземплярах EC2. Это позволяет довольно просто разрешить определенным экземплярам иметь разрешение на чтение определенных ресурсов S3.

Здесь их сообщение в блоге, объявляющее о новой функции:

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

Здесь раздел в документации EC2:

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

Здесь раздел в документации IAM:

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

Роли IAM делают учетные данные доступными для экземпляра через HTTP, поэтому любые пользователи или процессы, запущенные в экземпляре, могут видеть их.

Ответ 2

Чтобы немного обновить ответы на этот вопрос:

Наряду с ролями IAM новый клиент командной строки AWS делает выборку этих активов тривиальной. Он автоматически выведет учетные данные AWS, предоставленные через IAM из среды, и обработает обновление этих учетных данных.

Здесь пример получения одного актива из безопасного ведра S3 в пользовательских данных 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

Просто. Вы также можете скопировать содержимое всего каталога с S3 и загрузить его и т.д. Подробнее см. справочный материал.

Ответ 3

У экземпляра с ролью IAM есть временные учетные данные безопасности, которые автоматически поворачиваются. Они доступны через http в http://169.254.169.254/latest/meta-data/iam/security-credentials/RoleName, где RoleName - это то, что вы назвали своей ролью. Поэтому их легко получить из вашего экземпляра, но они истекают регулярно.

Использование их немного жестко. CloudFormation не может использовать временные учетные данные напрямую. Amazon Linux AMI имеет установленный Python boto, и теперь он достаточно умен, чтобы автоматически находить и использовать эти учетные данные для вас. Здесь один лайнер вы можете поместить в script для извлечения файла из ведра S3 b, ключевого k в локальный файл f:

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

boto находит и использует временные учетные данные для вас, что делает его очень простым в использовании.

Ответ 4

Чтобы безопасно загрузить частный актив S3 на новый экземпляр EC2, вы должны использовать IAM Roles для EC2, чтобы предоставить необходимое разрешение S3 для ваш экземпляр EC2, затем вызовите aws s3 cp в вашем экземпляре UserData cloudinit script для загрузки актива.

Чтобы настроить роль IAM для EC2 из шаблона CloudFormation, используйте ресурс AWS::IAM::InstanceProfile, ссылаясь на AWS::IAM::Role с AssumeRolePolicyDocument делегированием доступа к ec2.amazonaws.com с политикой, разработанной для предоставлять наименьшие привилегии (в этом случае разрешено 's3:GetObject' только для загрузки конкретного объекта S3).

Вот полный примерный шаблон, который загружает ресурс S3 на новый экземпляр EC2 с использованием cloudinit, возвращая его содержимое как 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