Skip to content

Commit

Permalink
Merge pull request #1412 from OctopusDeploy/fnm/fix-account-type-sele…
Browse files Browse the repository at this point in the history
…ction

Fix account priority in AWS script step
  • Loading branch information
octopus-hideaki authored Dec 20, 2024
2 parents 54b8bae + bf361a6 commit db589f1
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 38 deletions.
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 @@ -96,6 +97,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 @@ -163,62 +165,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

0 comments on commit db589f1

Please sign in to comment.