Skip to content

Commit d6b1d71

Browse files
authored
Merge pull request #23 from Keyfactor/release-2.1
Release 2.1.0 merge to main
2 parents ddd99a5 + dfd39b3 commit d6b1d71

File tree

6 files changed

+152
-66
lines changed

6 files changed

+152
-66
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
2.1.0
2+
* Allow EC2 default credentials to also run the Assume Role command
3+
* Add sts:ExtenalId parameter option to Assume Role calls (not applicable when using OAuth)
4+
15
2.0.2
26
* Return parity to original AWS store type organization - differentiating based on AWS Account ID
37

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,6 @@ AWS Certificate Manager is a service that lets you easily provision, manage, and
114114
- Inventory Root Certificates
115115
- Inventory Certificates with Public and Private Keys
116116

117-
### Assumptions:
118-
- In order for the Certificates and Keys to renew or reenroll correctly, they need to derive of the <alias> which is passed into the any agent. The <alias> drives the files and object creation and is essentially how we are able to relate them to each other.
119-
120117
### Not Implemented/Supported
121118
- Reenrollment, Management, Discovery
122119

@@ -131,7 +128,7 @@ Options for authenticating:
131128
2. IAM User Auth configuration (refer to `AwsCerManA` below)
132129
3. EC2 Role Auth or other default method supported by the [AWS SDK](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/creds-assign.html)
133130

134-
As one option for #3, to set up Role Auth for an EC2 instance, follow the steps below. Note, this applies specifically __when the orchestrator is running `ACM-AWS` inside of an EC2 instance__. Additionally, the EC2 credentials do not use the AWS Account ID specified in the certificate store and only use the single account/role indicated by the EC2 settings.
131+
As one option for #3, to set up Role Auth for an EC2 instance, follow the steps below. Note, this applies specifically __when the orchestrator is running `ACM-AWS` inside of an EC2 instance__. When the option to assume an EC2 role is selected, the Account ID and Role will be assumed using the default credentials supplied in the EC2 instance via the AWS SDK.
135132
1. Assign or note the existing IAM Role assigned to the EC2 instance running
136133
2. Make sure that role has access to ACM
137134
3. When configuring the `AWS-ACM` store, do not select either IAM or OAuth methods in the store's settings. This will make it use the AWS SDK to lookup EC2 credentials.
@@ -173,13 +170,16 @@ target server containing the certificate store to be managed
173170

174171
Name|Display Name|Type|Default Value|Depends On|Required|Description
175172
---|---|---|---|---|---|---
173+
UseEC2AssumeRole | Assume new Account / Role in EC2 | boolean | False | N/A | Yes | A switch to enable the store to assume a new Account ID and Role when using EC2 credentials
176174
UseOAuth | Use OAuth 2.0 Provider | boolean | False | N/A | Yes | A switch to enable the store to use an OAuth provider workflow to authenticate with AWS ACM
177175
UseIAM | Use IAM User Auth | boolean | False | N/A | Yes | A switch to enable the store to use IAM User auth to assume a role when authenticating with AWS ACM
176+
EC2AssumeRole | AWS Role to Assume (EC2) | string | N/A | UseEC2AssumeRole | The AWS Role to assume using the EC2 instance credentials
178177
OAuthScope | OAuth Scope | string | N/A | Use OAuth 2.0 Provider | No | This is the OAuth Scope needed for Okta OAuth, defined in Okta
179178
OAuthGrantType | OAuth Grant Type | string | client_credentials | Use OAuth 2.0 Provider | No | In OAuth 2.0, the term “grant type” refers to the way an application gets an access token. In Okta this is `client_credentials`
180179
OAuthUrl | OAuth URL | string | https://***/oauth2/default/v1/token | Use OAuth 2.0 Provider | No | The URL to request a token from your OAuth Provider. Fill this out with the correct URL.
181180
OAuthAssumeRole | AWS Role to Assume (OAuth) | string | N/A | Use OAuth 2.0 Provider | No | The AWS Role to assume after getting an OAuth token.
182181
IAMAssumeRole | AWS Role to Assume (IAM) | string | N/A | Use IAM User Auth | No | The AWS Role to assume as the IAM User.
182+
ExternalId | sts:ExternalId | string | N/A | N/A | No | An optional parameter sts:ExternalId to pass with Assume Role calls
183183

184184

185185
**Entry Parameters:**
@@ -204,13 +204,16 @@ Cert Store Settings
204204
| User Name | See Below | See Below |
205205
| Password | See Below | See Below |
206206
| Store Path | us-east-1,us-east-2,...,etc. | The AWS Region, or a comma-separated list of multiple regions, the store will operate in. |
207+
| Assume new Account / Role in EC2 | Use the EC2 credentials to make an Assume Role request to change Role and Account ID in use | Set to true to use EC2 credentials and assume a different role than the default EC2 role. |
207208
| Use OAuth 2.0 Provider | Use an OAuth provider to authenticate with AWS | Set to true to enable OAuth usage and display additional OAuth fields |
208209
| Use IAM User Auth | Use an IAM user's credentials to assume a role | Set to true to enable IAM user auth and the IAM Account ID field. |
210+
| AWS Role to Assume (EC2) | AWS Role | Displayed and required when using Assume new Account / Role in EC2. This Role is assumed using the EC2 default credentials. |
209211
| OAuth Scope | Look in OAuth provider for Scope | Displayed and required when using OAuth 2.0 Provider. OAuth scope setup in the Okta Application or other OAuth provider |
210212
| OAuth Grant Type | client_credentials | Displayed and required when using OAuth 2.0 Provider. This may vary depending on Okta setup but will most likely be this value. |
211213
| OAuth URL | https://***/oauth2/default/v1/token | Displayed and required when using OAuth 2.0 Provider. URL to request token from OAuth provider. Example given is for an Okta token. |
212214
| AWS Role to Assume (OAuth) | AWS Role | Displayed and required when using OAuth 2.0 Provider. This Role is assumed after getting an OAuth token. |
213215
| AWS Role to Assume (IAM) | AWS Role | Displayed and required when using IAM User Auth. This Role is assumed with the IAM credentials. |
216+
| sts:ExternalId | Configured AWS External ID | An optional field to enter an External ID value set in AWS, which may be required to make Assume Role calls. |
214217

215218
The User Name and Password fields are used differently based on the auth method you intend to use. The three options for auth are IAM User, OAuth, or default auth.
216219

@@ -220,8 +223,8 @@ The User Name and Password fields are used differently based on the auth method
220223
| IAM User | Password | Set to the IAM User's AWS `Access Secret` |
221224
| OAuth 2.0 | User Name | Set to the OAuth `Client ID` |
222225
| OAuth 2.0 | Password | Set to the OAuth `Client Secret` |
223-
| Default (SDK) | User Name | No Value |
224-
| Default (SDK) | Password | No Value |
226+
| Default EC2 (SDK) | User Name | No Value |
227+
| Default EC2 (SDK) | Password | No Value |
225228

226229
</details>
227230
</details>

aws-orchestrator-core/AuthUtilities.cs

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030

3131
namespace Keyfactor.AnyAgent.AwsCertificateManager
3232
{
33-
public class AuthUtilities
34-
{
33+
public class AuthUtilities
34+
{
3535
private readonly ILogger _logger;
3636
private readonly IPAMSecretResolver _pam;
3737

@@ -41,8 +41,8 @@ public AuthUtilities(IPAMSecretResolver pam, ILogger logger)
4141
_logger = logger;
4242
}
4343

44-
public Credentials GetCredentials(ACMCustomFields customFields, JobConfiguration jobConfiguration, CertificateStore certStore)
45-
{
44+
public Credentials GetCredentials(ACMCustomFields customFields, JobConfiguration jobConfiguration, CertificateStore certStore)
45+
{
4646
_logger.MethodEntry();
4747
_logger.LogDebug("Selecting credential method.");
4848

@@ -60,7 +60,7 @@ public Credentials GetCredentials(ACMCustomFields customFields, JobConfiguration
6060
_logger.LogDebug($"Using AWS Account ID - {awsAccountId} - from the ClientMachine field");
6161

6262
_logger.LogTrace("Attempting to authenticate with AWS using IAM access credentials.");
63-
return AwsAuthenticate(accessKey, accessSecret, awsAccountId, awsRole);
63+
return AwsAuthenticate(accessKey, accessSecret, awsAccountId, awsRole, customFields.ExternalId);
6464
}
6565
else if (customFields.UseOAuth)
6666
{
@@ -88,6 +88,21 @@ public Credentials GetCredentials(ACMCustomFields customFields, JobConfiguration
8888
_logger.LogTrace("Attempting to authenticate with AWS using OAuth response.");
8989
return AwsAuthenticateWithWebIdentity(authResponse, awsAccountId, awsRole);
9090
}
91+
else if (customFields.UseEC2AssumeRole)
92+
{
93+
// use SDK credential resolution, but run Assume Role
94+
_logger.LogInformation("Using default AWS SDK credential resolution with Assume Role for creating AWS Credentials.");
95+
string accessKey = null;
96+
string accessSecret = null;
97+
98+
string awsRole = customFields.EC2AssumeRole;
99+
_logger.LogDebug($"Assuming AWS Role - {awsRole}");
100+
101+
_logger.LogDebug($"Using AWS Account ID - {awsAccountId} - from the ClientMachine field");
102+
103+
_logger.LogTrace("Attempting to assume new Role with AWS using default AWS SDK credential.");
104+
return AwsAuthenticate(accessKey, accessSecret, awsAccountId, awsRole, customFields.ExternalId);
105+
}
91106
else // use default SDK credential resolution
92107
{
93108
_logger.LogInformation("Using default AWS SDK credential resolution for creating AWS Credentials.");
@@ -96,23 +111,23 @@ public Credentials GetCredentials(ACMCustomFields customFields, JobConfiguration
96111
}
97112
}
98113

99-
public Credentials AwsAuthenticateWithWebIdentity(OAuthResponse authResponse, string awsAccount, string awsRole)
100-
{
114+
public Credentials AwsAuthenticateWithWebIdentity(OAuthResponse authResponse, string awsAccount, string awsRole)
115+
{
101116
_logger.MethodEntry();
102-
Credentials credentials = null;
103-
try
104-
{
105-
var account = awsAccount;
117+
Credentials credentials = null;
118+
try
119+
{
120+
var account = awsAccount;
106121
_logger.LogTrace($"Using AWS Account - {account}");
107-
var stsClient = new AmazonSecurityTokenServiceClient(new AnonymousAWSCredentials());
122+
var stsClient = new AmazonSecurityTokenServiceClient(new AnonymousAWSCredentials());
108123
_logger.LogTrace("Created AWS STS client with anonymous credentials.");
109-
var assumeRequest = new AssumeRoleWithWebIdentityRequest
110-
{
111-
WebIdentityToken = authResponse?.AccessToken,
112-
RoleArn = $"arn:aws:iam::{account}:role/{awsRole}",
113-
RoleSessionName = "KeyfactorSession",
114-
DurationSeconds = Convert.ToInt32(authResponse?.ExpiresIn)
115-
};
124+
var assumeRequest = new AssumeRoleWithWebIdentityRequest
125+
{
126+
WebIdentityToken = authResponse?.AccessToken,
127+
RoleArn = $"arn:aws:iam::{account}:role/{awsRole}",
128+
RoleSessionName = "KeyfactorSession",
129+
DurationSeconds = Convert.ToInt32(authResponse?.ExpiresIn)
130+
};
116131
var logAssumeRequest = new
117132
{
118133
WebIdentityToken = "**redacted**",
@@ -123,58 +138,85 @@ public Credentials AwsAuthenticateWithWebIdentity(OAuthResponse authResponse, st
123138
_logger.LogDebug($"Prepared Assume Role With Web Identity request with fields: {logAssumeRequest}");
124139

125140
_logger.LogTrace("Submitting Assume Role With Web Identity request.");
126-
var assumeResult = AsyncHelpers.RunSync(() => stsClient.AssumeRoleWithWebIdentityAsync(assumeRequest));
141+
var assumeResult = AsyncHelpers.RunSync(() => stsClient.AssumeRoleWithWebIdentityAsync(assumeRequest));
127142
_logger.LogTrace("Received response to Assume Role With Web Identity request.");
128-
credentials = assumeResult.Credentials;
129-
}
130-
catch (Exception e)
131-
{
143+
credentials = assumeResult.Credentials;
144+
}
145+
catch (Exception e)
146+
{
132147
_logger.LogError($"Error Occurred in AwsAuthenticateWithWebIdentity: {e.Message}");
133148

134149
throw;
135-
}
150+
}
136151

137-
return credentials;
138-
}
152+
return credentials;
153+
}
139154

140-
public Credentials AwsAuthenticate(string accessKey, string accessSecret, string awsAccount, string awsRole)
141-
{
155+
public Credentials AwsAuthenticate(string accessKey, string accessSecret, string awsAccount, string awsRole, string externalId = null)
156+
{
142157
_logger.MethodEntry();
143-
Credentials credentials = null;
144-
try
145-
{
146-
var account = awsAccount;
158+
Credentials credentials = null;
159+
try
160+
{
161+
var account = awsAccount;
147162
_logger.LogTrace($"Using AWS Account - {account}");
148-
var stsClient = new AmazonSecurityTokenServiceClient(accessKey, accessSecret);
149-
_logger.LogTrace("Created AWS STS client with IAM user credentials.");
150-
var assumeRequest = new AssumeRoleRequest
151-
{
152-
RoleArn = $"arn:aws:iam::{account}:role/{awsRole}",
153-
RoleSessionName = "KeyfactorSession"
154-
};
155163

156-
var logAssumeRequest = new
164+
AmazonSecurityTokenServiceClient stsClient;
165+
if (accessKey != null && accessSecret != null)
157166
{
158-
assumeRequest.RoleArn,
159-
assumeRequest.RoleSessionName
167+
stsClient = new AmazonSecurityTokenServiceClient(accessKey, accessSecret);
168+
_logger.LogTrace("Created AWS STS client with IAM user credentials.");
169+
}
170+
else
171+
{
172+
stsClient = new AmazonSecurityTokenServiceClient();
173+
_logger.LogTrace("Created AWS STS client with default credential resolution.");
174+
}
175+
176+
var assumeRequest = new AssumeRoleRequest
177+
{
178+
RoleArn = $"arn:aws:iam::{account}:role/{awsRole}",
179+
RoleSessionName = "KeyfactorSession",
160180
};
161-
_logger.LogDebug($"Prepared Assume Role request with fields: {logAssumeRequest}");
162181

182+
if (string.IsNullOrWhiteSpace(externalId))
183+
{
184+
// no sts:ExternalId
185+
var logAssumeRequest = new
186+
{
187+
assumeRequest.RoleArn,
188+
assumeRequest.RoleSessionName
189+
};
190+
_logger.LogDebug($"Prepared Assume Role request with fields: {logAssumeRequest}");
191+
}
192+
else
193+
{
194+
// include sts:ExternalId with assume role request
195+
assumeRequest.ExternalId = externalId;
196+
var logAssumeRequestWithExternalId = new
197+
{
198+
assumeRequest.RoleArn,
199+
assumeRequest.RoleSessionName,
200+
assumeRequest.ExternalId
201+
};
202+
_logger.LogDebug($"Prepared Assume Role request with fields: {logAssumeRequestWithExternalId}");
203+
204+
}
163205
_logger.LogTrace("Submitting Assume Role request.");
164206
var assumeResult = AsyncHelpers.RunSync(() => stsClient.AssumeRoleAsync(assumeRequest));
165207
_logger.LogTrace("Received response to Assume Role request.");
166-
credentials = assumeResult.Credentials;
167-
}
168-
catch (Exception e)
169-
{
208+
credentials = assumeResult.Credentials;
209+
}
210+
catch (Exception e)
211+
{
170212
_logger.LogError($"Error Occurred in AwsAuthenticate: {e.Message}");
171-
throw;
172-
}
213+
throw;
214+
}
173215

174-
return credentials;
175-
}
216+
return credentials;
217+
}
176218

177-
public OAuthResponse OAuthAuthenticate(OAuthParameters parameters)
219+
public OAuthResponse OAuthAuthenticate(OAuthParameters parameters)
178220
{
179221
try
180222
{
@@ -242,5 +284,5 @@ public string ResolvePamField(string field, string fieldName)
242284
return field;
243285
}
244286
}
245-
}
287+
}
246288
}

aws-orchestrator-core/CustomFields.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public class IAMCustomFields : CustomFields
4949

5050
public class ACMCustomFields
5151
{
52+
[JsonProperty("UseEC2AssumeRole")]
53+
[DefaultValue(false)]
54+
public bool UseEC2AssumeRole { get; set; }
55+
5256
[JsonProperty("UseOAuth")]
5357
[DefaultValue(false)]
5458
public bool UseOAuth { get; set; }
@@ -57,6 +61,9 @@ public class ACMCustomFields
5761
[DefaultValue(false)]
5862
public bool UseIAM { get; set; }
5963

64+
[JsonProperty("EC2AssumeRole")]
65+
public string EC2AssumeRole { get; set; }
66+
6067
[JsonProperty("OAuthAssumeRole")]
6168
public string OAuthAssumeRole { get; set; }
6269

@@ -71,5 +78,8 @@ public class ACMCustomFields
7178

7279
[JsonProperty("IAMAssumeRole")]
7380
public string IAMAssumeRole { get; set; }
81+
82+
[JsonProperty("ExternalId")]
83+
public string ExternalId { get; set; }
7484
}
7585
}

integration-manifest.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@
4545
"Remove": true
4646
},
4747
"Properties": [
48+
{
49+
"Name": "UseEC2AssumeRole",
50+
"DisplayName": "Assume new Account / Role in EC2",
51+
"Type": "Bool",
52+
"DependsOn": null,
53+
"DefaultValue": "false",
54+
"Required": true
55+
},
4856
{
4957
"Name": "UseOAuth",
5058
"DisplayName": "Use OAuth 2.0 Provider",
@@ -61,6 +69,14 @@
6169
"DefaultValue": "false",
6270
"Required": true
6371
},
72+
{
73+
"Name": "EC2AssumeRole",
74+
"DisplayName": "AWS Role to Assume (EC2)",
75+
"Type": "String",
76+
"DependsOn": "UseEC2AssumeRole",
77+
"DefaultValue": null,
78+
"Required": false
79+
},
6480
{
6581
"Name": "OAuthScope",
6682
"DisplayName": "OAuth Scope",
@@ -101,6 +117,14 @@
101117
"DefaultValue": null,
102118
"Required": false
103119
},
120+
{
121+
"Name": "ExternalId",
122+
"DisplayName": "sts:ExternalId",
123+
"Type": "String",
124+
"DependsOn": null,
125+
"DefaultValue": null,
126+
"Required": false
127+
},
104128
{
105129
"Name": "ServerUsername",
106130
"DisplayName": "Server Username",

0 commit comments

Comments
 (0)