Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix account priority in AWS script step #1412

Merged
merged 3 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 51 additions & 38 deletions source/Calamari.CloudAccounts/AwsEnvironmentGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class AwsEnvironmentGeneration

readonly ILog log;
readonly Func<Task<bool>> verifyLogin;
readonly string accountType;
readonly string region;
readonly string accessKey;
readonly string secretKey;
Expand Down Expand Up @@ -94,6 +95,7 @@ internal AwsEnvironmentGeneration(ILog log, IVariables variables, Func<Task<bool
// The lack of any keys means we rely on an EC2 instance role.
variables.Get("Octopus.Action.Amazon.AccessKey")?.Trim();
secretKey = variables.Get(account + ".SecretKey")?.Trim() ?? variables.Get("Octopus.Action.Amazon.SecretKey")?.Trim();
accountType = variables.Get("Octopus.Account.AccountType")?.Trim();

roleArn = variables.Get($"{account}.RoleArn")?.Trim() ??
variables.Get("Octopus.Action.Amazon.RoleArn")?.Trim();
Expand Down Expand Up @@ -161,62 +163,73 @@ void PopulateCommonSettings()
{
EnvironmentVars["AWS_DEFAULT_REGION"] = region;
EnvironmentVars["AWS_REGION"] = region;
}
}

/// <summary>
/// If the keys were explicitly supplied, use them directly
/// </summary>
/// <exception cref="Exception">The supplied keys were not valid</exception>
async Task<bool> PopulateSuppliedKeys()
{
if (!String.IsNullOrEmpty(accessKey))
switch (accountType)
{
// Prioritise auth flows based on account type
case "AmazonWebServicesAccount" when await TryPopulateKeysDirectly():
case "AmazonWebServicesOidcAccount" when await TryPopulateKeysUsingOidc():
return true;
default:
// Default priority if no matching account type
return await TryPopulateKeysDirectly() || await TryPopulateKeysUsingOidc();
}
}

async Task<bool> TryPopulateKeysDirectly()
{
if(string.IsNullOrEmpty(accessKey)) return false;
EnvironmentVars["AWS_ACCESS_KEY_ID"] = accessKey;
EnvironmentVars["AWS_SECRET_ACCESS_KEY"] = secretKey;
if (!await verifyLogin())
{
throw new Exception("AWS-LOGIN-ERROR-0005: Failed to verify the credentials. "
+ "Please check the keys assigned to the Amazon Web Services Account associated with this step. "
+ $"For more information visit {log.FormatLink("https://oc.to/U4WA8x")}");
}

return true;
}

async Task<bool> TryPopulateKeysUsingOidc()
{
if(string.IsNullOrEmpty(oidcJwt)) return false;
try
{
EnvironmentVars["AWS_ACCESS_KEY_ID"] = accessKey;
EnvironmentVars["AWS_SECRET_ACCESS_KEY"] = secretKey;
var client = new AmazonSecurityTokenServiceClient(new AnonymousAWSCredentials());
var assumeRoleWithWebIdentityResponse = await client.AssumeRoleWithWebIdentityAsync(new AssumeRoleWithWebIdentityRequest
{
RoleArn = roleArn,
DurationSeconds = int.TryParse(sessionDuration, out var seconds) ? seconds : 3600,
RoleSessionName = DefaultSessionName,
WebIdentityToken = oidcJwt
});

EnvironmentVars["AWS_ACCESS_KEY_ID"] = assumeRoleWithWebIdentityResponse.Credentials.AccessKeyId;
EnvironmentVars["AWS_SECRET_ACCESS_KEY"] = assumeRoleWithWebIdentityResponse.Credentials.SecretAccessKey;
EnvironmentVars["AWS_SESSION_TOKEN"] = assumeRoleWithWebIdentityResponse.Credentials.SessionToken;
if (!await verifyLogin())
{
throw new Exception("AWS-LOGIN-ERROR-0005: Failed to verify the credentials. "
+ "Please check the keys assigned to the Amazon Web Services Account associated with this step. "
+ "Please check the Role ARN assigned to the Amazon Web Services Account associated with this step. "
+ $"For more information visit {log.FormatLink("https://oc.to/U4WA8x")}");
}

return true;
}

if (!string.IsNullOrEmpty(oidcJwt))
catch (Exception ex)
{
try
{
var client = new AmazonSecurityTokenServiceClient(new AnonymousAWSCredentials());
var assumeRoleWithWebIdentityResponse = await client.AssumeRoleWithWebIdentityAsync(new AssumeRoleWithWebIdentityRequest
{
RoleArn = roleArn,
DurationSeconds = int.TryParse(sessionDuration, out var seconds) ? seconds : 3600,
RoleSessionName = DefaultSessionName,
WebIdentityToken = oidcJwt
});

EnvironmentVars["AWS_ACCESS_KEY_ID"] = assumeRoleWithWebIdentityResponse.Credentials.AccessKeyId;
EnvironmentVars["AWS_SECRET_ACCESS_KEY"] = assumeRoleWithWebIdentityResponse.Credentials.SecretAccessKey;
EnvironmentVars["AWS_SESSION_TOKEN"] = assumeRoleWithWebIdentityResponse.Credentials.SessionToken;
if (!await verifyLogin())
{
throw new Exception("AWS-LOGIN-ERROR-0005: Failed to verify the credentials. "
+ "Please check the Role ARN assigned to the Amazon Web Services Account associated with this step. "
+ $"For more information visit {log.FormatLink("https://oc.to/U4WA8x")}");
}

return true;
}
catch (Exception ex)
{
// catch the exception and fallback to returning false
throw new Exception("AWS-LOGIN-ERROR-0005.1: Failed to verify OIDC credentials. "
+ $"Error: {ex}");
}
// catch the exception and fallback to returning false
throw new Exception("AWS-LOGIN-ERROR-0005.1: Failed to verify OIDC credentials. "
+ $"Error: {ex}");
}

return false;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ async Task ValidateCloudFormation(Func<AmazonCloudFormationClient, Task> execute
public async Task DeployTemplate(string resourceName, string templateFilePath, IVariables variables)
{
var variablesFile = Path.GetTempFileName();
variables.Set("Octopus.Account.AccountType", "AmazonWebServicesAccount");
variables.Set("Octopus.Action.AwsAccount.Variable", "AWSAccount");
variables.Set("AWSAccount.AccessKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3AccessKey, CancellationToken.None));
variables.Set("AWSAccount.SecretKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3SecretKey, CancellationToken.None));
Expand Down Expand Up @@ -175,6 +176,7 @@ public async Task DeployTemplate(string resourceName, string templateFilePath, I
public async Task DeployTemplateS3(string resourceName, IVariables variables)
{
var variablesFile = Path.GetTempFileName();
variables.Set("Octopus.Account.AccountType", "AmazonWebServicesAccount");
variables.Set("Octopus.Action.AwsAccount.Variable", "AWSAccount");
variables.Set("AWSAccount.AccessKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3AccessKey, CancellationToken.None));
variables.Set("AWSAccount.SecretKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3SecretKey, CancellationToken.None));
Expand Down Expand Up @@ -226,6 +228,7 @@ public async Task DeleteStack(string stackName)
{
var variablesFile = Path.GetTempFileName();
var variables = new CalamariVariables();
variables.Set("Octopus.Account.AccountType", "AmazonWebServicesAccount");
variables.Set("Octopus.Action.AwsAccount.Variable", "AWSAccount");
variables.Set("AWSAccount.AccessKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3AccessKey, CancellationToken.None));
variables.Set("AWSAccount.SecretKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3SecretKey, CancellationToken.None));
Expand Down
1 change: 1 addition & 0 deletions source/Calamari.Tests/AWS/S3Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ protected async Task<string> Upload(string packageName, List<S3TargetPropertiesB
var variablesFile = Path.GetTempFileName();

variables.Set("Octopus.Action.AwsAccount.Variable", "AWSAccount");
variables.Set("Octopus.Account.AccountType", "AmazonWebServicesAccount");
variables.Set("AWSAccount.AccessKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3AccessKey, cancellationToken));
variables.Set("AWSAccount.SecretKey", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3SecretKey, cancellationToken));
variables.Set("Octopus.Action.Aws.Region", region);
Expand Down