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

Updated credential providers to provide Async version for generating credentials. #3681

Open
wants to merge 5 commits into
base: v4-development
Choose a base branch
from
Open
Changes from 1 commit
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
Next Next commit
Updated credential providers to provide Async version for generating …
…credentials.
  • Loading branch information
ashishdhingra committed Mar 6, 2025
commit 685cb8632fa91657de354fc80e1014f78d4e433e
18 changes: 18 additions & 0 deletions generator/.DevConfigs/9fa1d027-8266-430e-b4c0-a541eb733f8e.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"core": {
"changeLogMessages": [
"Updated credential providers to provide Async version for generating credentials."
],
"type": "patch",
"updateMinimum": true
},
"services": [
{
"serviceName": "SecurityToken",
"type": "patch",
"changeLogMessages": [
"Added implementation for ICoreAmazonSTS_SAML/ICoreAmazonSTS.CredentialsFromSAMLAuthenticationAsync() and ICoreAmazonSTS.CredentialsFromAssumeRoleAuthenticationAsync()."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the change log something users will understand which don't directly interact with the ICoreX interfaces. Something like add async code paths for the SAML and Assume role credentials providers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

]
}
]
}
2 changes: 0 additions & 2 deletions sdk/src/Core/Amazon.Runtime/Credentials/AWSCredentials.cs
Original file line number Diff line number Diff line change
@@ -35,11 +35,9 @@ public abstract class AWSCredentials : BaseIdentity
/// </summary>
protected virtual void Validate() { }

#if AWS_ASYNC_API
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we removing AWS_ASYNC_API? (By the way I think this constant is redundant since every target supports async but it's in a lot of other places too)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it based on discussion with @normj since it is now redundant. And based on discussion, the current scope of removal was limited to credential providers.

public virtual System.Threading.Tasks.Task<ImmutableCredentials> GetCredentialsAsync()
{
return System.Threading.Tasks.Task.FromResult<ImmutableCredentials>(this.GetCredentials());
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
using System.Net;
using System.Diagnostics.CodeAnalysis;
using ThirdParty.RuntimeBackports;
using System.Threading.Tasks;

namespace Amazon.Runtime
{
@@ -91,13 +92,32 @@ public AssumeRoleAWSCredentials(AWSCredentials sourceCredentials, string roleArn
PreemptExpiryTime = TimeSpan.FromMinutes(15);
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026",
Justification = "Reflection code is only used as a fallback in case the SDK was not trimmed. Trimmed scenarios should register dependencies with Amazon.RuntimeDependencyRegistry.GlobalRuntimeDependencyRegistry")]
protected override CredentialsRefreshState GenerateNewCredentials()
{
var region = FallbackRegionFactory.GetRegionEndpoint() ?? DefaultSTSClientRegion;
ICoreAmazonSTS coreSTSClient = GlobalRuntimeDependencyRegistry.Instance.GetInstance<ICoreAmazonSTS>(ServiceClientHelpers.STS_ASSEMBLY_NAME, ServiceClientHelpers.STS_SERVICE_CLASS_NAME,
new CreateInstanceContext(new SecurityTokenServiceClientContext {Action = SecurityTokenServiceClientContext.ActionContext.AssumeRoleAWSCredentials, Region = region, ProxySettings = Options?.ProxySettings } ));
ICoreAmazonSTS coreSTSClient = GetSTSClient(region);

var credentials = coreSTSClient.CredentialsFromAssumeRoleAuthentication(RoleArn, RoleSessionName, Options);
_logger.InfoFormat("New credentials created for assume role that expire at {0}", credentials.Expiration.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", CultureInfo.InvariantCulture));
return new CredentialsRefreshState(credentials, credentials.Expiration);
}

protected override async Task<CredentialsRefreshState> GenerateNewCredentialsAsync()
{
var region = FallbackRegionFactory.GetRegionEndpoint() ?? DefaultSTSClientRegion;
ICoreAmazonSTS coreSTSClient = GetSTSClient(region);

var credentials = await coreSTSClient.CredentialsFromAssumeRoleAuthenticationAsync(RoleArn, RoleSessionName, Options).ConfigureAwait(false);
_logger.InfoFormat("New credentials created for assume role that expire at {0}", credentials.Expiration.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", CultureInfo.InvariantCulture));
return new CredentialsRefreshState(credentials, credentials.Expiration);
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026",
Justification = "Reflection code is only used as a fallback in case the SDK was not trimmed. Trimmed scenarios should register dependencies with Amazon.RuntimeDependencyRegistry.GlobalRuntimeDependencyRegistry")]
private ICoreAmazonSTS GetSTSClient(RegionEndpoint region)
{
ICoreAmazonSTS coreSTSClient = GlobalRuntimeDependencyRegistry.Instance.GetInstance<ICoreAmazonSTS>(ServiceClientHelpers.STS_ASSEMBLY_NAME, ServiceClientHelpers.STS_SERVICE_CLASS_NAME,
new CreateInstanceContext(new SecurityTokenServiceClientContext { Action = SecurityTokenServiceClientContext.ActionContext.AssumeRoleAWSCredentials, Region = region, ProxySettings = Options?.ProxySettings }));

if (coreSTSClient == null)
{
@@ -130,11 +150,7 @@ protected override CredentialsRefreshState GenerateNewCredentials()
}
}



var credentials = coreSTSClient.CredentialsFromAssumeRoleAuthentication(RoleArn, RoleSessionName, Options);
_logger.DebugFormat("New credentials created for assume role that expire at {0}", credentials.Expiration.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", CultureInfo.InvariantCulture));
return new CredentialsRefreshState(credentials, credentials.Expiration);
return coreSTSClient;
}
}
}
Original file line number Diff line number Diff line change
@@ -25,9 +25,7 @@
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
#if AWS_ASYNC_API
using System.Threading.Tasks;
#endif

namespace Amazon.Runtime
{
@@ -181,7 +179,6 @@ protected override CredentialsRefreshState GenerateNewCredentials()
return new CredentialsRefreshState(credentials, credentials.Expiration);
}

#if AWS_ASYNC_API
protected override async Task<CredentialsRefreshState> GenerateNewCredentialsAsync()
{
string token = null;
@@ -218,7 +215,6 @@ protected override async Task<CredentialsRefreshState> GenerateNewCredentialsAsy
_logger.DebugFormat("New credentials created using assume role with web identity that expire at {0}", credentials.Expiration.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", CultureInfo.InvariantCulture));
return new CredentialsRefreshState(credentials, credentials.Expiration);
}
#endif

/// <summary>
/// Gets a client to be used for AssumeRoleWithWebIdentity requests.
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ private DefaultInstanceProfileAWSCredentials()

_logger = Logger.GetLogger(typeof(DefaultInstanceProfileAWSCredentials));

_credentialsRetrieverTimer = new Timer(RenewCredentials, null, TimeSpan.Zero, _neverTimespan);
_credentialsRetrieverTimer = new Timer(RenewCredentials, null, TimeSpan.Zero, _neverTimespan); // This invokes synchronous calls in seperate thread.
}

#region Overrides
@@ -174,15 +174,94 @@ public override ImmutableCredentials GetCredentials()
return credentials;
}

#if AWS_ASYNC_API
/// <summary>
/// Returns a copy of the most recent instance profile credentials.
/// </summary>
public override Task<ImmutableCredentials> GetCredentialsAsync()
public override async Task<ImmutableCredentials> GetCredentialsAsync()
{
return Task.FromResult<ImmutableCredentials>(GetCredentials());
CheckIsIMDSEnabled();
ImmutableCredentials credentials = null;

// Try to acquire read lock. The thread would be blocked if another thread has write lock.
if (_credentialsLock.TryEnterReadLock(_credentialsLockTimeout))
{
try
{
if (null != _lastRetrievedCredentials)
{
if (_lastRetrievedCredentials.IsExpiredWithin(TimeSpan.Zero) &&
!_imdsRefreshFailed)
{
// this is the first failure - immediately try to renew
_imdsRefreshFailed = true;
_lastRetrievedCredentials = await FetchCredentialsAsync().ConfigureAwait(false);
}

// if credentials are expired, we'll still return them, but log a message about
// them being expired.
if (_lastRetrievedCredentials.IsExpiredWithin(TimeSpan.Zero))
{
_logger.InfoFormat(_usingExpiredCredentialsFromIMDS);
}
else
{
_imdsRefreshFailed = false;
}

return _lastRetrievedCredentials?.Credentials.Copy();
}
}
finally
{
_credentialsLock.ExitReadLock();
}
}

// If there's no credentials cached, hit IMDS directly. Try to acquire write lock.
if (_credentialsLock.TryEnterWriteLock(_credentialsLockTimeout))
{
try
{
// Check for last retrieved credentials again in case other thread might have already fetched it.
if (null == _lastRetrievedCredentials)
{
_lastRetrievedCredentials = await FetchCredentialsAsync().ConfigureAwait(false);
}

if (_lastRetrievedCredentials.IsExpiredWithin(TimeSpan.Zero) &&
!_imdsRefreshFailed)
{
// this is the first failure - immediately try to renew
_imdsRefreshFailed = true;
_lastRetrievedCredentials = await FetchCredentialsAsync().ConfigureAwait(false);
}

// if credentials are expired, we'll still return them, but log a message about
// them being expired.
if (_lastRetrievedCredentials.IsExpiredWithin(TimeSpan.Zero))
{
_logger.InfoFormat(_usingExpiredCredentialsFromIMDS);
}
else
{
_imdsRefreshFailed = false;
}

credentials = _lastRetrievedCredentials.Credentials?.Copy();
}
finally
{
_credentialsLock.ExitWriteLock();
}
}

if (credentials == null)
{
throw new AmazonServiceException(FailedToGetCredentialsMessage);
}

return credentials;
}
#endif
#endregion

#region Private members
@@ -241,6 +320,28 @@ private static RefreshingAWSCredentials.CredentialsRefreshState FetchCredentials
if (securityCredentials == null)
throw new AmazonServiceException("Unable to get IAM security credentials from EC2 Instance Metadata Service.");

IAMSecurityCredentialMetadata metadata = GetMetadataFromSecurityCredentials(securityCredentials);

return new RefreshingAWSCredentials.CredentialsRefreshState(
new ImmutableCredentials(metadata.AccessKeyId, metadata.SecretAccessKey, metadata.Token),
metadata.Expiration);
}

private static async Task<RefreshingAWSCredentials.CredentialsRefreshState> FetchCredentialsAsync()
{
var securityCredentials = await EC2InstanceMetadata.GetIAMSecurityCredentialsAsync().ConfigureAwait(false);
if (securityCredentials == null)
throw new AmazonServiceException("Unable to get IAM security credentials from EC2 Instance Metadata Service.");

IAMSecurityCredentialMetadata metadata = GetMetadataFromSecurityCredentials(securityCredentials);

return new RefreshingAWSCredentials.CredentialsRefreshState(
new ImmutableCredentials(metadata.AccessKeyId, metadata.SecretAccessKey, metadata.Token),
metadata.Expiration);
}

private static IAMSecurityCredentialMetadata GetMetadataFromSecurityCredentials(System.Collections.Generic.IDictionary<string, IAMSecurityCredentialMetadata> securityCredentials)
{
string firstRole = null;
foreach (var role in securityCredentials.Keys)
{
@@ -255,9 +356,7 @@ private static RefreshingAWSCredentials.CredentialsRefreshState FetchCredentials
if (metadata == null)
throw new AmazonServiceException("Unable to get credentials for role \"" + firstRole + "\" from EC2 Instance Metadata Service.");

return new RefreshingAWSCredentials.CredentialsRefreshState(
new ImmutableCredentials(metadata.AccessKeyId, metadata.SecretAccessKey, metadata.Token),
metadata.Expiration);
return metadata;
}

private static void CheckIsIMDSEnabled()
Loading
Oops, something went wrong.