Skip to content

Commit a2f6dbd

Browse files
aws_ssm - split S3 region/endpoint discovery into dedicated function (#1674) (#1677)
[PR #1674/8237ebb7 backport][stable-5] aws_ssm - split S3 region/endpoint discovery into dedicated function This is a backport of PR #1674 as merged into main (8237ebb). Depends-On: #1670 SUMMARY fixes: #1616 Newer AWS regions don't generate valid presigned URLs unless you explicitly pass the endpoint_url for the region (see also boto/boto3#3015) ISSUE TYPE Bugfix Pull Request COMPONENT NAME aws_ssm ADDITIONAL INFORMATION Reviewed-by: Markus Bergholz <[email protected]>
1 parent 026534a commit a2f6dbd

File tree

7 files changed

+91
-12
lines changed

7 files changed

+91
-12
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bugfixes:
2+
- aws_ssm - fixes bug with presigned S3 URLs in post-2019 AWS regions (https://github.com/ansible-collections/community.aws/issues/1616).

plugins/connection/aws_ssm.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,32 @@ def _vvv(self, message):
350350
def _vvvv(self, message):
351351
self._display(display.vvvv, message)
352352

353+
def _get_bucket_endpoint(self):
354+
# Fetch the correct S3 endpoint for use with our bucket.
355+
# If we don't explicitly set the endpoint then some commands will use the global
356+
# endpoint and fail
357+
# (new AWS regions and new buckets in a region other than the one we're running in)
358+
359+
region_name = self.get_option('region') or 'us-east-1'
360+
profile_name = self.get_option('profile') or ''
361+
self._vvvv("_get_bucket_endpoint: S3 (global)")
362+
tmp_s3_client = self._get_boto_client(
363+
's3', region_name=region_name, profile_name=profile_name,
364+
)
365+
# Fetch the location of the bucket so we can open a client against the 'right' endpoint
366+
# This /should/ always work
367+
bucket_location = tmp_s3_client.get_bucket_location(
368+
Bucket=(self.get_option('bucket_name')),
369+
)
370+
bucket_region = bucket_location['LocationConstraint']
371+
# Create another client for the region the bucket lives in, so we can nab the endpoint URL
372+
self._vvvv(f"_get_bucket_endpoint: S3 (bucket region) - {bucket_region}")
373+
s3_bucket_client = self._get_boto_client(
374+
's3', region_name=bucket_region, profile_name=profile_name,
375+
)
376+
377+
return s3_bucket_client.meta.endpoint_url, s3_bucket_client.meta.region_name
378+
353379
def _init_clients(self):
354380
self._vvvv("INITIALIZE BOTO3 CLIENTS")
355381
profile_name = self.get_option('profile') or ''
@@ -358,20 +384,17 @@ def _init_clients(self):
358384
# The SSM Boto client, currently used to initiate and manage the session
359385
# Note: does not handle the actual SSM session traffic
360386
self._vvvv("SETUP BOTO3 CLIENTS: SSM")
361-
ssm_client = self._get_boto_client('ssm', region_name=region_name, profile_name=profile_name)
387+
ssm_client = self._get_boto_client(
388+
'ssm', region_name=region_name, profile_name=profile_name,
389+
)
362390
self._client = ssm_client
363391

364-
region_name = self.get_option('region') or 'us-east-1'
365-
self._vvvv("SETUP BOTO3 CLIENTS: S3 (tmp)")
366-
tmp_s3_client = self._get_boto_client('s3', region_name=region_name, profile_name=profile_name)
367-
# Fetch the location of the bucket so we can open a client against the 'right' endpoint
368-
bucket_location = tmp_s3_client.get_bucket_location(
369-
Bucket=(self.get_option('bucket_name')),
392+
s3_endpoint_url, s3_region_name = self._get_bucket_endpoint()
393+
self._vvvv(f"SETUP BOTO3 CLIENTS: S3 {s3_endpoint_url}")
394+
s3_bucket_client = self._get_boto_client(
395+
's3', region_name=s3_region_name, endpoint_url=s3_endpoint_url, profile_name=profile_name,
370396
)
371-
bucket_region = bucket_location['LocationConstraint']
372-
# This is the S3 client we'll really be using
373-
self._vvvv(f"SETUP BOTO3 CLIENTS: S3 - {bucket_region}")
374-
s3_bucket_client = self._get_boto_client('s3', region_name=bucket_region, profile_name=profile_name)
397+
375398
self._s3_client = s3_bucket_client
376399

377400
def __init__(self, *args, **kwargs):
@@ -706,7 +729,7 @@ def _get_url(self, client_method, bucket_name, out_path, http_method, extra_args
706729
params.update(extra_args)
707730
return client.generate_presigned_url(client_method, Params=params, ExpiresIn=3600, HttpMethod=http_method)
708731

709-
def _get_boto_client(self, service, region_name=None, profile_name=None):
732+
def _get_boto_client(self, service, region_name=None, profile_name=None, endpoint_url=None):
710733
''' Gets a boto3 client based on the STS token '''
711734

712735
aws_access_key_id = self.get_option('access_key_id')
@@ -734,6 +757,7 @@ def _get_boto_client(self, service, region_name=None, profile_name=None):
734757

735758
client = session.client(
736759
service,
760+
endpoint_url=endpoint_url,
737761
config=Config(
738762
signature_version="s3v4",
739763
s3={'addressing_style': self.get_option('s3_addressing_style')}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
time=10m
2+
3+
cloud/aws
4+
connection_aws_ssm
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
- hosts: localhost
2+
roles:
3+
- role: ../setup_connection_aws_ssm
4+
vars:
5+
target_os: fedora
6+
s3_bucket_region: 'eu-central-1'
7+
# Post 2019 regions behave differently from other regions
8+
# they're worth testing but it's not possible in CI today.
9+
#s3_bucket_region: 'eu-south-1'
10+
test_suffix: crossregion
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- hosts: localhost
2+
tasks:
3+
- include_role:
4+
name: ../setup_connection_aws_ssm
5+
tasks_from: cleanup.yml
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies:
2+
- connection
3+
- setup_connection_aws_ssm
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
3+
PLAYBOOK_DIR=$(pwd)
4+
set -eux
5+
6+
CMD_ARGS=("$@")
7+
8+
# Destroy Environment
9+
cleanup() {
10+
11+
cd "${PLAYBOOK_DIR}"
12+
ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}"
13+
14+
}
15+
16+
trap "cleanup" EXIT
17+
18+
# Setup Environment
19+
ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@"
20+
21+
# Export the AWS Keys
22+
set +x
23+
. ./aws-env-vars.sh
24+
set -x
25+
26+
cd ../connection
27+
28+
# Execute Integration tests
29+
INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \
30+
-e target_hosts=aws_ssm \
31+
"$@"

0 commit comments

Comments
 (0)