codebuild/sign_artifacts.py (114 lines of code) (raw):
import os
import time
import sys
import botocore
import boto3
import shutil
MAX_WAIT_CYCLES = 180
# Get script variables
account_id = os.environ['SIGNER_ACCOUNT_ID']
access_role = os.environ['SIGNER_ARTIFACT_ACCESS_ROLE']
external_id = os.environ['EXTERNAL_ID']
profile_identifier = os.environ['SIGNER_PROFILE_IDENTIFIER']
platform_identifier = os.environ['SIGNER_PLATFORM_IDENTIFIER']
codebuild_id = os.environ['CODEBUILD_BUILD_ID'][-64:].replace(':', '-')
# Get the list of artifacts to sign
from os import listdir
from os.path import isfile, join
artifacts_to_sign = [f for f in listdir('unsigned-artifacts') if isfile(join('unsigned-artifacts', f))]
print("Artifacts found to sign: " + str(artifacts_to_sign))
# Assume the artifact access role
sts = boto3.client('sts')
role_arn = 'arn:aws:iam::' + account_id + ':role/' + access_role
print(f'Assuming role:')
print(f' RoleArn: {role_arn}')
print(f' RoleSessionName: {codebuild_id}')
print(f' ExternalId: {external_id}')
assume_role_result = sts.assume_role(
RoleArn = role_arn,
RoleSessionName = codebuild_id,
DurationSeconds = 3600,
ExternalId = external_id
)
print("Assumed appropriate role for signing")
# Upload the artifacts
print("Starting to upload artifacts")
s3 = boto3.client(
's3',
aws_access_key_id = assume_role_result['Credentials']['AccessKeyId'],
aws_secret_access_key = assume_role_result['Credentials']['SecretAccessKey'],
aws_session_token = assume_role_result['Credentials']['SessionToken']
)
for artifact in artifacts_to_sign:
with open('unsigned-artifacts/' + artifact, 'rb') as artifact_file:
key = profile_identifier + '/' + platform_identifier + '/' + artifact
bucket = account_id + '-unsigned-bucket'
print(f' Uploading: {artifact}')
print(f' Key: {key}')
print(f' Bucket: {bucket}')
s3.put_object(
ACL = 'bucket-owner-full-control',
Bucket = bucket,
Key = key,
Body = artifact_file
)
print("Upload complete")
# Get the tags for the artifact
print("Starting to poll for signer-job-id tags")
signer_job_id_map = {}
current_wait_cycle = 1
artifacts_to_fetch = artifacts_to_sign.copy()
while len(signer_job_id_map) != len(artifacts_to_sign) and current_wait_cycle < MAX_WAIT_CYCLES:
artifacts_to_remove = []
for artifact in artifacts_to_fetch:
get_object_tagging_result = s3.get_object_tagging(
Bucket = account_id + '-unsigned-bucket',
Key = profile_identifier + '/' + platform_identifier + '/' + artifact
)
for tag in get_object_tagging_result['TagSet']:
if tag['Key'] == 'signer-job-id':
artifacts_to_remove.append(artifact)
signer_job_id_map[artifact] = tag['Value']
print("\tTag " + tag['Value'] + " found for: " + artifact)
break
for artifact in artifacts_to_remove:
artifacts_to_fetch.remove(artifact)
current_wait_cycle += 1
if len(signer_job_id_map) != len(artifacts_to_sign):
time.sleep(10)
if len(signer_job_id_map) != len(artifacts_to_sign):
sys.exit('Not all items were appropriately tagged, the following were not: ' + str(artifacts_to_fetch))
print("All tags found")
# Poll the destination bucket until the signed items are made available
print("Starting to poll for signed artifacts")
current_wait_cycle = 1
artifacts_to_fetch = []
shutil.rmtree('signed-artifacts', ignore_errors=True)
os.mkdir('signed-artifacts')
for item in signer_job_id_map.items():
isJar = item[0].endswith('.jar')
if isJar:
artifacts_to_fetch.append(item[0] + '-' + item[1] + '.jar')
else:
artifacts_to_fetch.append(item[0] + '-' + item[1])
while artifacts_to_fetch and current_wait_cycle < MAX_WAIT_CYCLES:
artifacts_to_remove = []
for artifact in artifacts_to_fetch:
try:
bucket = account_id + '-signed-bucket'
key = profile_identifier + '/' + platform_identifier + '/' + artifact
print(f' Downloading: {artifact}')
print(f' Key: {key}')
print(f' Bucket: {bucket}')
get_object_result = s3.get_object(Bucket = bucket, Key = key)
signed_artifact_file_name = artifact[:artifact.index('.jar') + 4].replace('-unsigned', '-signed')
with open('signed-artifacts/' + signed_artifact_file_name, 'wb') as artifact_file:
artifact_file.write(get_object_result['Body'].read())
artifacts_to_remove.append(artifact)
print("Signed artifact downloaded for: " + artifact)
except botocore.exceptions.ClientError as exn:
pass
for artifact in artifacts_to_remove:
artifacts_to_fetch.remove(artifact)
current_wait_cycle += 1
if artifacts_to_fetch:
time.sleep(10)
if current_wait_cycle >= MAX_WAIT_CYCLES:
print('Maximum wait cycles reached while downloading artifacts!')
if artifacts_to_fetch:
sys.exit('Not all items were able to be downloaded, the following were not: ' + str(artifacts_to_fetch))
print("All signed artifacts downloaded!")