diff --git a/src/AWSCredentials.jl b/src/AWSCredentials.jl index 7d605ae24b..6b505bebdd 100644 --- a/src/AWSCredentials.jl +++ b/src/AWSCredentials.jl @@ -114,7 +114,7 @@ function AWSCredentials(; profile=nothing, throw_cred_error=true) () -> dot_aws_config(profile), credentials_from_webtoken, ecs_instance_credentials, - ec2_instance_credentials + () -> ec2_instance_credentials(profile), ] # Loop through our search locations until we get credentials back @@ -235,32 +235,69 @@ ec2_instance_region() = ec2_instance_metadata("/latest/meta-data/placement/regio """ - ec2_instance_credentials() -> AWSCredentials + ec2_instance_credentials(profile::AbstractString) -> AWSCredentials Parse the EC2 metadata to retrieve AWSCredentials. """ -function ec2_instance_credentials() - info = ec2_instance_metadata("/latest/meta-data/iam/info") +function ec2_instance_credentials(profile::AbstractString) + path = dot_aws_config_file() + ini = Inifile() + if isfile(path) + ini = read(ini, path) + end - if info === nothing - return nothing + # Any profile except default must specify the credential_source as Ec2InstanceMetadata. + if profile != "default" + source = _get_ini_value(ini, profile, "credential_source") + source == "Ec2InstanceMetadata" || return nothing end + info = ec2_instance_metadata("/latest/meta-data/iam/info") + info === nothing && return nothing info = JSON.parse(info) + # Get credentials for the role associated to the instance via instance profile. name = ec2_instance_metadata("/latest/meta-data/iam/security-credentials/") creds = ec2_instance_metadata("/latest/meta-data/iam/security-credentials/$name") - new_creds = JSON.parse(creds) + parsed = JSON.parse(creds) + instance_profile_creds = AWSCredentials( + parsed["AccessKeyId"], + parsed["SecretAccessKey"], + parsed["Token"], + info["InstanceProfileArn"]; + expiry=DateTime(rstrip(parsed["Expiration"], 'Z')), + renew=() -> ec2_instance_credentials(profile), + ) - expiry = DateTime(rstrip(new_creds["Expiration"], 'Z')) + # Look for a role to assume and return instance profile credentials if there is none. + role_arn = _get_ini_value(ini, profile, "role_arn") + role_arn === nothing && return instance_profile_creds + # Assume the role. + role_session = _role_session_name( + "AWS.jl-role-", + basename(role_arn), + "-" * Dates.format(@mock(now(UTC)), dateformat"yyyymmdd\THHMMSS\Z"), + ) + params = Dict{String, Any}("RoleArn" => role_arn, "RoleSessionName" => role_session) + duration = _get_ini_value(ini, profile, "duration_seconds") + if duration !== nothing + params["DurationSeconds"] = parse(Int, duration) + end + resp = AWSServices.sts( + "AssumeRole", + params; + aws_config=AWSConfig(creds=instance_profile_creds), + ) + role_creds = resp["AssumeRoleResult"]["Credentials"] + role_user = resp["AssumeRoleResult"]["AssumedRoleUser"] return AWSCredentials( - new_creds["AccessKeyId"], - new_creds["SecretAccessKey"], - new_creds["Token"], - info["InstanceProfileArn"]; - expiry=expiry, - renew=ec2_instance_credentials + role_creds["AccessKeyId"], + role_creds["SecretAccessKey"], + role_creds["SessionToken"], + role_user["Arn"], + expiry=DateTime(rstrip(role_creds["Expiration"], 'Z')), + renew=() -> ec2_instance_credentials(profile), ) end diff --git a/test/AWSCredentials.jl b/test/AWSCredentials.jl index 39f85a3372..ab1a162922 100644 --- a/test/AWSCredentials.jl +++ b/test/AWSCredentials.jl @@ -470,7 +470,7 @@ end @testset "Instance - EC2" begin apply(_http_request_patch) do - result = ec2_instance_credentials() + result = ec2_instance_credentials("default") @test result.access_key_id == test_values["AccessKeyId"] @test result.secret_key == test_values["SecretAccessKey"] @test result.token == test_values["Token"]