Skip to content

Commit

Permalink
aws_ssm connection: add SSE encryption parameters. (#763) (#823)
Browse files Browse the repository at this point in the history
aws_ssm connection: add SSE encryption parameters.

SUMMARY
Add the following parameters to aws_ssm.py connection plugin:

ansible_aws_ssm_bucket_sse_mode
ansible_aws_ssm_bucket_sse_kms_key_id

ISSUE TYPE

Feature Pull Request

COMPONENT NAME
aws_ssm connection plugin
ADDITIONAL INFORMATION
This allows the connection plugin to work when encryption parameters are required for uploads on the file transfer bucket by policy / SCP (see here for an example).

Reviewed-by: Brian Scholer <None>
Reviewed-by: Maxime <None>
Reviewed-by: Jill R <None>
Reviewed-by: Markus Bergholz <[email protected]>
Reviewed-by: None <None>
(cherry picked from commit 08f95cc)

Co-authored-by: Maxime <[email protected]>
  • Loading branch information
patchback[bot] and fh-maxime-froment authored Dec 5, 2021
1 parent fd6032f commit c1ef7a9
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- aws_ssm connection plugin - add parameters to explicitly specify SSE mode and KMS key id for uploads on the file transfer bucket. (https://github.com/ansible-collections/community.aws/pull/763)
58 changes: 51 additions & 7 deletions plugins/connection/aws_ssm.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@
type: integer
vars:
- name: ansible_aws_ssm_timeout
bucket_sse_mode:
description: Server-side encryption mode to use for uploads on the S3 bucket used for file transfer.
choices: [ 'AES256', 'aws:kms' ]
required: false
version_added: 2.2.0
vars:
- name: ansible_aws_ssm_bucket_sse_mode
bucket_sse_kms_key_id:
description: KMS key id to use when encrypting objects using C(bucket_sse_mode=aws:kms). Ignored otherwise.
version_added: 2.2.0
vars:
- name: ansible_aws_ssm_bucket_sse_kms_key_id
'''

EXAMPLES = r'''
Expand Down Expand Up @@ -162,6 +174,20 @@
state: directory
# Execution: ansible-playbook win_file.yaml -i aws_ec2.yml
# The playbook tasks will get executed on the instance ids returned from the dynamic inventory plugin using ssm connection.
# Install a Nginx Package on Linux Instance; with specific SSE for file transfer
- name: Install a Nginx Package
vars:
ansible_connection: aws_ssm
ansible_aws_ssm_bucket_name: nameofthebucket
ansible_aws_ssm_region: us-west-2
ansible_aws_ssm_bucket_sse_mode: 'aws:kms'
ansible_aws_ssm_bucket_sse_kms_key_id: alias/kms-key-alias
tasks:
- name: Install a Nginx Package
yum:
name: nginx
state: present
'''

import os
Expand Down Expand Up @@ -506,11 +532,14 @@ def _flush_stderr(self, subprocess):

return stderr

def _get_url(self, client_method, bucket_name, out_path, http_method, profile_name):
def _get_url(self, client_method, bucket_name, out_path, http_method, profile_name, extra_args=None):
''' Generate URL for get_object / put_object '''
region_name = self.get_option('region') or 'us-east-1'
client = self._get_boto_client('s3', region_name=region_name, profile_name=profile_name)
return client.generate_presigned_url(client_method, Params={'Bucket': bucket_name, 'Key': out_path}, ExpiresIn=3600, HttpMethod=http_method)
params = {'Bucket': bucket_name, 'Key': out_path}
if extra_args is not None:
params.update(extra_args)
return client.generate_presigned_url(client_method, Params=params, ExpiresIn=3600, HttpMethod=http_method)

def _get_boto_client(self, service, region_name=None, profile_name=None):
''' Gets a boto3 client based on the STS token '''
Expand Down Expand Up @@ -554,14 +583,29 @@ def _file_transport_command(self, in_path, out_path, ssm_action):

profile_name = self.get_option('profile')

put_args = dict()
put_headers = dict()
if self.get_option('bucket_sse_mode'):
put_args['ServerSideEncryption'] = self.get_option('bucket_sse_mode')
put_headers['x-amz-server-side-encryption'] = self.get_option('bucket_sse_mode')
if self.get_option('bucket_sse_mode') == 'aws:kms' and self.get_option('bucket_sse_kms_key_id'):
put_args['SSEKMSKeyId'] = self.get_option('bucket_sse_kms_key_id')
put_headers['x-amz-server-side-encryption-aws-kms-key-id'] = self.get_option('bucket_sse_kms_key_id')

if self.is_windows:
put_command = "Invoke-WebRequest -Method PUT -InFile '%s' -Uri '%s' -UseBasicParsing" % (
in_path, self._get_url('put_object', self.get_option('bucket_name'), s3_path, 'PUT', profile_name))
put_command_headers = "; ".join(["'%s' = '%s'" % (h, v) for h, v in put_headers.items()])
put_command = "Invoke-WebRequest -Method PUT -Headers @{%s} -InFile '%s' -Uri '%s' -UseBasicParsing" % (
put_command_headers, in_path,
self._get_url('put_object', self.get_option('bucket_name'), s3_path, 'PUT', profile_name,
extra_args=put_args))
get_command = "Invoke-WebRequest '%s' -OutFile '%s'" % (
self._get_url('get_object', self.get_option('bucket_name'), s3_path, 'GET', profile_name), out_path)
else:
put_command = "curl --request PUT --upload-file '%s' '%s'" % (
in_path, self._get_url('put_object', self.get_option('bucket_name'), s3_path, 'PUT', profile_name))
put_command_headers = "".join(["-H '%s: %s' " % (h, v) for h, v in put_headers.items()])
put_command = "curl --request PUT %s--upload-file '%s' '%s'" % (
put_command_headers, in_path,
self._get_url('put_object', self.get_option('bucket_name'), s3_path, 'PUT', profile_name,
extra_args=put_args))
get_command = "curl '%s' -o '%s'" % (
self._get_url('get_object', self.get_option('bucket_name'), s3_path, 'GET', profile_name), out_path)

Expand All @@ -572,7 +616,7 @@ def _file_transport_command(self, in_path, out_path, ssm_action):
client.download_fileobj(self.get_option('bucket_name'), s3_path, data)
else:
with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as data:
client.upload_fileobj(data, self.get_option('bucket_name'), s3_path)
client.upload_fileobj(data, self.get_option('bucket_name'), s3_path, ExtraArgs=put_args)
(returncode, stdout, stderr) = self.exec_command(get_command, in_data=None, sudoable=False)

# Remove the files from the bucket after they've been transferred
Expand Down
6 changes: 0 additions & 6 deletions tests/integration/targets/connection_aws_ssm/aliases
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
# reason: slow
# This test suite can take almost 25 minutes (on a good day)
unstable

cloud/aws

destructive
non_local
needs/root
needs/target/connection
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ instance_type: t3.micro
linux_ami_name: amzn-ami-hvm-2018.03*x86_64-ebs
# Windows AMIs get replaced every few months, don't be too specific
windows_ami_name: Windows_Server-2019-English-Full-Base-*

# see:
# - https://github.com/mattclay/aws-terminator/pull/181
# - https://github.com/ansible-collections/community.aws/pull/763
s3_bucket_name: ssm-encrypted-test-bucket
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,30 @@
linux_ami_id: '{{ latest_linux_ami.image_id }}'
windows_ami_id: '{{ latest_windows_ami.image_id }}'

- name: Install Session Manager Plugin for Debian/Ubuntu
- name: Install Session Manager Plugin for Fedora/Debian/Ubuntu
include_tasks: debian.yml
when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
register: install_plugin_debian

- name: Install Session Manager Plugin for RedHat/Amazon
include_tasks: redhat.yml
when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat" or ansible_distribution == "Amazon"
when: ansible_distribution in ["CentOS", "RedHat", "Amazon", "Fedora"]
register: install_plugin_redhat

- name: Fail if the plugin was not installed
fail:
msg: The distribution does not contain the required Session Manager Plugin
when:
- install_plugin_debian is skipped
- install_plugin_redhat is skipped
- block:
- name: Fail if the plugin was not installed
fail:
msg: The distribution does not contain the required Session Manager Plugin
when:
- install_plugin_debian is skipped
- install_plugin_redhat is skipped
always:
- debug:
var: ansible_distribution

- name: Ensure IAM instance role exists
iam_role:
name: "ansible-test-{{resource_prefix}}-aws-ssm-role"
name: "ansible-test-{{tiny_prefix}}-aws-ssm-role"
assume_role_policy_document: "{{ lookup('file','ec2-trust-policy.json') }}"
state: present
create_instance_profile: yes
Expand Down Expand Up @@ -99,10 +103,16 @@
wait_for_connection:
delay: 360

- name: Create S3 bucket
s3_bucket:
name: "{{resource_prefix}}-aws-ssm-s3"
register: s3_output
- name: create a key
aws_kms:
alias: '{{ resource_prefix }}-kms'
tags:
ansible-test: '{{ resource_prefix }}'

# - name: Create S3 bucket
# s3_bucket:
# name: "{{resource_prefix}}-aws-ssm-s3"
# register: s3_output

- name: Create Inventory file
template:
Expand All @@ -128,11 +138,11 @@
src: ec2_windows_vars_to_delete.yml.j2
ignore_errors: yes

- name: Create S3 vars_to_delete.yml
template:
dest: "{{playbook_dir}}/s3_vars_to_delete.yml"
src: s3_vars_to_delete.yml.j2
ignore_errors: yes
# - name: Create S3 vars_to_delete.yml
# template:
# dest: "{{playbook_dir}}/s3_vars_to_delete.yml"
# src: s3_vars_to_delete.yml.j2
# ignore_errors: yes

- name: Create IAM Role vars_to_delete.yml
template:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
mode: '0440'
tags: setup_infra
- name: Install SSM Plugin
become: true
yum:
name: /tmp/session-manager-plugin.rpm
state: present
disable_gpg_check: true
tags: setup_infra
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ aws_ssm_linux

[aws_ssm:vars]
ansible_connection=community.aws.aws_ssm
ansible_aws_ssm_bucket_name={{s3_output.name}}
ansible_aws_ssm_bucket_name={{s3_bucket_name}}
ansible_aws_ssm_plugin=/usr/local/sessionmanagerplugin/bin/session-manager-plugin
ansible_python_interpreter=/usr/bin/env python
local_tmp=/tmp/ansible-local-
ansible_aws_ssm_bucket_sse_mode='aws:kms'
ansible_aws_ssm_bucket_sse_kms_key_id=alias/{{ resource_prefix }}-kms

# support tests that target testhost
[testhost:children]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
bucket_name: {{s3_output.name}}
#bucket_name: {{s3_output.name}}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@
ignore_errors: yes
when: iam_role_vars_file.stat.exists == true

- name: Delete the KMS key
aws_kms:
state: absent
alias: '{{ resource_prefix }}-kms'

- name: Delete AWS keys environement
file:
path: "{{playbook_dir}}/aws-env-vars.sh"
Expand Down

0 comments on commit c1ef7a9

Please sign in to comment.