-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Fix RDS IAM Cross Account Auth and Clarify Dev Container Docs #27632
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
base: main
Are you sure you want to change the base?
Changes from all commits
4e57fbc
7c2b9ad
a0297a1
e5bf90e
0738b31
86c550c
7409964
7f25bc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,58 +3,135 @@ | |
| import java.net.MalformedURLException; | ||
| import java.net.URI; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import org.openmetadata.common.utils.CommonUtil; | ||
| import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; | ||
| import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; | ||
| import software.amazon.awssdk.regions.Region; | ||
| import software.amazon.awssdk.services.rds.RdsUtilities; | ||
| import software.amazon.awssdk.services.rds.model.GenerateAuthenticationTokenRequest; | ||
| import software.amazon.awssdk.services.sts.StsClient; | ||
| import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; | ||
| import software.amazon.awssdk.services.sts.model.AssumeRoleRequest; | ||
|
|
||
| /** | ||
| * {@link DatabaseAuthenticationProvider} implementation for AWS RDS IAM Auth. | ||
| * | ||
| * @see <a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Enabling.html"></a> | ||
| */ | ||
| public class AwsRdsDatabaseAuthenticationProvider implements DatabaseAuthenticationProvider { | ||
| public class AwsRdsDatabaseAuthenticationProvider | ||
| implements DatabaseAuthenticationProvider, AutoCloseable { | ||
|
|
||
| public static final String AWS_REGION = "awsRegion"; | ||
| public static final String ALLOW_PUBLIC_KEY_RETRIEVAL = "allowPublicKeyRetrieval"; | ||
| public static final String ASSUME_ROLE_ARN = "assumeRoleArn"; | ||
| public static final String PROTOCOL = "https://"; | ||
|
|
||
| private final Map<String, AwsCredentialsProvider> credentialsProviderCache = | ||
| new ConcurrentHashMap<>(); | ||
| private final Map<String, StsClient> stsClientCache = new ConcurrentHashMap<>(); | ||
| private final Map<String, RdsUtilities> rdsUtilitiesCache = new ConcurrentHashMap<>(); | ||
| private static final AwsCredentialsProvider DEFAULT_CREDENTIALS_PROVIDER = | ||
| DefaultCredentialsProvider.create(); | ||
|
|
||
| @Override | ||
| public String authenticate(String jdbcUrl, String username, String password) { | ||
| public String authenticate(final String jdbcUrl, final String username, final String password) { | ||
| try { | ||
|
|
||
| URI uri = URI.create(PROTOCOL + removeProtocolFrom(jdbcUrl)); | ||
| Map<String, String> queryParams = parseQueryParams(uri.toURL()); | ||
| final URI uri = URI.create(PROTOCOL + removeProtocolFrom(jdbcUrl)); | ||
| final Map<String, String> queryParams = parseQueryParams(uri.toURL()); | ||
|
|
||
| // Set | ||
| String awsRegion = queryParams.get(AWS_REGION); | ||
| String allowPublicKeyRetrieval = queryParams.get(ALLOW_PUBLIC_KEY_RETRIEVAL); | ||
| final String awsRegion = queryParams.get(AWS_REGION); | ||
| final String allowPublicKeyRetrieval = queryParams.get(ALLOW_PUBLIC_KEY_RETRIEVAL); | ||
| final String assumeRoleArn = queryParams.get(ASSUME_ROLE_ARN); | ||
|
|
||
| // Validate | ||
| Objects.requireNonNull(awsRegion, "Parameter `awsRegion` shall be provided in the jdbc url."); | ||
| Objects.requireNonNull( | ||
| allowPublicKeyRetrieval, | ||
| "Parameter `allowPublicKeyRetrieval` shall be provided in the jdbc url."); | ||
| if (CommonUtil.nullOrEmpty(awsRegion)) { | ||
| throw new DatabaseAuthenticationProviderException( | ||
| "Parameter `awsRegion` shall be provided in the jdbc url."); | ||
| } | ||
| if (CommonUtil.nullOrEmpty(allowPublicKeyRetrieval)) { | ||
| throw new DatabaseAuthenticationProviderException( | ||
| "Parameter `allowPublicKeyRetrieval` shall be provided in the jdbc url."); | ||
| } | ||
|
Comment on lines
+49
to
+56
|
||
|
|
||
| final AwsCredentialsProvider credentialsProvider = | ||
| getCredentialsProvider(awsRegion, assumeRoleArn); | ||
|
|
||
| // Prepare request | ||
| GenerateAuthenticationTokenRequest request = | ||
| final GenerateAuthenticationTokenRequest request = | ||
| GenerateAuthenticationTokenRequest.builder() | ||
| .credentialsProvider(DefaultCredentialsProvider.create()) | ||
| .credentialsProvider(credentialsProvider) | ||
| .hostname(uri.getHost()) | ||
| .port(uri.getPort()) | ||
| .username(username) | ||
| .build(); | ||
|
|
||
| // Return token | ||
| return RdsUtilities.builder() | ||
| .region(Region.of(awsRegion)) | ||
| .build() | ||
| .generateAuthenticationToken(request); | ||
| return getRdsUtilities(awsRegion).generateAuthenticationToken(request); | ||
|
|
||
| } catch (MalformedURLException e) { | ||
| // Throw | ||
| throw new DatabaseAuthenticationProviderException(e); | ||
| } catch (Exception e) { | ||
| throw new DatabaseAuthenticationProviderException("Failed to generate AWS RDS IAM token", e); | ||
| } | ||
| } | ||
|
|
||
| private RdsUtilities getRdsUtilities(final String awsRegion) { | ||
| return rdsUtilitiesCache.computeIfAbsent( | ||
| awsRegion, region -> RdsUtilities.builder().region(Region.of(region)).build()); | ||
| } | ||
|
|
||
| private AwsCredentialsProvider getCredentialsProvider( | ||
| final String awsRegion, final String assumeRoleArn) { | ||
| if (CommonUtil.nullOrEmpty(assumeRoleArn)) { | ||
| return DEFAULT_CREDENTIALS_PROVIDER; | ||
| } | ||
|
|
||
| final String cacheKey = awsRegion + ":" + assumeRoleArn; | ||
| return credentialsProviderCache.computeIfAbsent( | ||
| cacheKey, | ||
| k -> { | ||
| final StsClient stsClient = | ||
| stsClientCache.computeIfAbsent( | ||
| awsRegion, | ||
| region -> | ||
| StsClient.builder() | ||
| .region(Region.of(region)) | ||
| .credentialsProvider(DEFAULT_CREDENTIALS_PROVIDER) | ||
| .build()); | ||
|
|
||
| final AssumeRoleRequest assumeRoleRequest = | ||
| AssumeRoleRequest.builder() | ||
| .roleArn(assumeRoleArn) | ||
| .roleSessionName("OpenMetadata-RDS-IAM-Auth") | ||
| .build(); | ||
|
|
||
| return StsAssumeRoleCredentialsProvider.builder() | ||
| .stsClient(stsClient) | ||
| .refreshRequest(assumeRoleRequest) | ||
| .build(); | ||
| }); | ||
| } | ||
|
|
||
| @Override | ||
| public void close() { | ||
| credentialsProviderCache | ||
| .values() | ||
| .forEach( | ||
| p -> { | ||
| if (p instanceof AutoCloseable closeable) { | ||
| try { | ||
| closeable.close(); | ||
| } catch (Exception ignored) { | ||
| // Ignored | ||
| } | ||
| } | ||
| }); | ||
| stsClientCache.values().forEach(StsClient::close); | ||
| credentialsProviderCache.clear(); | ||
| stsClientCache.clear(); | ||
| rdsUtilitiesCache.clear(); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.