Skip to content

Support for IAM roles for service accounts in kubernetes #72

@bclodius

Description

@bclodius

Background

We have a project of modernizing our docker image builds and are building them inside kubernetes pods. We leverage AWS IAM roles for service accounts (IRSA).

Some our images use apt-transport-s3 to source debian packages from our s3 buckets.

Using IRSA our pods do not have access to IMDSv2 or directly have access to aws creds. During our docker image builds we don't want to pass creds directly to the build as that could lead to secrets leaking into the image layers.

Proposal

I would like to contribute an additional way of gathering aws creds from the environment variables AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE.

High level sample code. I've not yet tested this code and will edit accordingly after I get a working POC.

import json
import urllib.request
import urllib.parse
import datetime

# Set your environment variables or manually configure
ROLE_ARN = 'arn:aws:iam::123456789012:role/YourRole'
WEB_IDENTITY_TOKEN_FILE = '/path/to/your/web-identity-token'  # Path to your web identity token file
REGION = 'us-west-2'
SERVICE = 'sts'
HOST = f'{SERVICE}.{REGION}.amazonaws.com'

# Load the web identity token from the specified file
with open(WEB_IDENTITY_TOKEN_FILE, 'r') as f:
    web_identity_token = f.read().strip()

# Set up parameters for the assume-role-with-web-identity request
params = {
    'Action': 'AssumeRoleWithWebIdentity',
    'RoleArn': ROLE_ARN,
    'RoleSessionName': 'YourSessionName',
    'WebIdentityToken': web_identity_token,
    'Version': '2011-06-15',  # Specify the API version
}

# Set up the datetime for signature
now = datetime.datetime.utcnow()
timestamp = now.strftime('%Y%m%dT%H%M%SZ')
date = now.strftime('%Y%m%d')

# Create the canonical request
canonical_querystring = urllib.parse.urlencode(sorted(params.items()))
canonical_headers = f'host:{HOST}\nx-amz-date:{timestamp}\n'
signed_headers = 'host;x-amz-date'
payload_hash = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'  # Empty body, hashed
canonical_request = f'GET\n/\n{canonical_querystring}\n{canonical_headers}\n{signed_headers}\n{payload_hash}'

# Create the string to sign
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = f'{date}/{REGION}/{SERVICE}/aws4_request'
string_to_sign = f'{algorithm}\n{timestamp}\n{credential_scope}\n{hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()}'

# Create the signing key
def sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

def get_signature_key(secret_key, date_stamp, region_name, service_name):
    k_date = sign(('AWS4' + secret_key).encode('utf-8'), date_stamp)
    k_region = sign(k_date, region_name)
    k_service = sign(k_region, service_name)
    k_signing = sign(k_service, 'aws4_request')
    return k_signing

# Normally you'd sign using your AWS secret key, but since you don't need it, skip it for Web Identity Token

# Prepare headers
headers = {
    'x-amz-date': timestamp,
    'Authorization': '',  # No need to sign this request as the web identity token is the signature
}

# Construct the URL
url = f'https://{HOST}/?{canonical_querystring}'

# Send the HTTP request to AWS STS
request = urllib.request.Request(url, headers=headers)

# Make the HTTP request and parse the response
try:
    with urllib.request.urlopen(request) as response:
        response_data = response.read()
        credentials = json.loads(response_data)

        # Output the temporary credentials
        print("Temporary Credentials:", json.dumps(credentials, indent=4))

except urllib.error.HTTPError as e:
    print(f"Error: {e.code}, {e.read().decode('utf-8')}")

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions