diff --git a/barman/clients/cloud_cli.py b/barman/clients/cloud_cli.py index b583aae21..edb00ffdd 100644 --- a/barman/clients/cloud_cli.py +++ b/barman/clients/cloud_cli.py @@ -177,6 +177,12 @@ def create_argument_parser(description, source_or_destination=UrlArgumentType.so "--endpoint-url", help="Override default S3 endpoint URL with the given one", ) + s3_arguments.add_argument( + "--aws-irsa", + help="bypasses credentials/profile and uses iam service account", + action="store_true", + default=False, + ) s3_arguments.add_argument( "-P", "--aws-profile", diff --git a/barman/cloud_providers/__init__.py b/barman/cloud_providers/__init__.py index ed2a75085..d6a9f859b 100644 --- a/barman/cloud_providers/__init__.py +++ b/barman/cloud_providers/__init__.py @@ -47,6 +47,7 @@ def _make_s3_cloud_interface(config, cloud_interface_kwargs): cloud_interface_kwargs.update( { + "aws_irsa" : config.aws_irsa, "profile_name": config.aws_profile, "endpoint_url": config.endpoint_url, "read_timeout": config.read_timeout, diff --git a/barman/cloud_providers/aws_s3.py b/barman/cloud_providers/aws_s3.py index cbd2360a1..1880fc608 100644 --- a/barman/cloud_providers/aws_s3.py +++ b/barman/cloud_providers/aws_s3.py @@ -18,6 +18,7 @@ import logging import math +import os import shutil from io import RawIOBase @@ -102,6 +103,7 @@ def __init__( self, url, encryption=None, + aws_irsa=False, jobs=2, profile_name=None, endpoint_url=None, @@ -116,6 +118,8 @@ def __init__( :param str url: Full URL of the cloud destination/source :param str|None encryption: Encryption type string + :param bool|False aws_irsa: Amazon aws iam role for service account + should be used instead of profile_name :param int jobs: How many sub-processes to use for asynchronous uploading, defaults to 2. :param str profile_name: Amazon auth profile identifier @@ -134,6 +138,7 @@ def __init__( tags=tags, delete_batch_size=delete_batch_size, ) + self.aws_irsa = aws_irsa self.profile_name = profile_name self.encryption = encryption self.endpoint_url = endpoint_url @@ -161,7 +166,24 @@ def _reinit_session(self): config_kwargs["read_timeout"] = self.read_timeout config = Config(**config_kwargs) - session = boto3.Session(profile_name=self.profile_name) + if self.aws_irsa: + client = boto3.client('sts') + with open(os.getenv("AWS_WEB_IDENTITY_TOKEN_FILE"), 'r') as content_file: + web_identity_token = content_file.read() + + response = client.assume_role_with_web_identity( + RoleArn=os.environ['AWS_ROLE_ARN'], + RoleSessionName='barman', + WebIdentityToken=web_identity_token, + # DurationSeconds=3600 # defaults to an hour, must not be greater than + # the iam role max duration session (this is also default 1 hour) + ) + credentials = response['Credentials'] + session = boto3.Session( aws_access_key_id=credentials['AccessKeyId'], + aws_secret_access_key=credentials['SecretAccessKey'], + aws_session_token=credentials['SessionToken']) + else: + session = boto3.Session(profile_name=self.profile_name) self.s3 = session.resource("s3", endpoint_url=self.endpoint_url, config=config) @property @@ -463,7 +485,7 @@ class AwsCloudSnapshotInterface(CloudSnapshotInterface): https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-creating-snapshot.html """ - def __init__(self, profile_name=None, region=None, await_snapshots_timeout=3600): + def __init__(self, aws_irsa=False, profile_name=None, region=None, await_snapshots_timeout=3600): """ Creates the client necessary for creating and managing snapshots. @@ -472,7 +494,25 @@ def __init__(self, profile_name=None, region=None, await_snapshots_timeout=3600) :param int await_snapshots_timeout: The maximum time in seconds to wait for snapshots to complete. """ - self.session = boto3.Session(profile_name=profile_name) + if aws_irsa: + client = boto3.client('sts') + with open(os.getenv("AWS_WEB_IDENTITY_TOKEN_FILE"), 'r') as content_file: + web_identity_token = content_file.read() + + response = client.assume_role_with_web_identity( + RoleArn=os.environ['AWS_ROLE_ARN'], + RoleSessionName='barman', + WebIdentityToken=web_identity_token, + # DurationSeconds=3600 # defaults to an hour, must not be greater than + # the iam role max duration session (this is also default 1 hour) + ) + credentials = response['Credentials'] + self.session = boto3.Session( aws_access_key_id=credentials['AccessKeyId'], + aws_secret_access_key=credentials['SecretAccessKey'], + aws_session_token=credentials['SessionToken']) + else: + self.session = boto3.Session(profile_name=profile_name) + # If a specific region was provided then this overrides any region which may be # defined in the profile self.region = region or self.session.region_name diff --git a/doc/barman-cloud-backup.1.md b/doc/barman-cloud-backup.1.md index bd5b7701e..2e2e634ff 100644 --- a/doc/barman-cloud-backup.1.md +++ b/doc/barman-cloud-backup.1.md @@ -134,6 +134,8 @@ Extra options for the aws-s3 cloud provider: file) --profile AWS_PROFILE profile name (deprecated: replaced by --aws-profile) + --aws-irsa aws_irsa + bypasses credentials/profile and uses iam service account --read-timeout READ_TIMEOUT the time in seconds until a timeout is raised when waiting to read from a connection (defaults to 60