All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.jdbc.plugin.iam.LightRdsUtility Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package software.amazon.jdbc.plugin.iam;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.CredentialUtils;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.auth.signer.params.Aws4PresignerParams;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.CompletableFutureUtils;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.jdbc.util.Messages;

public class LightRdsUtility implements IamTokenUtility {

  private static final Logger LOGGER = Logger.getLogger(LightRdsUtility.class.getName());

  // The time the IAM token is good for. https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html
  private static final Duration EXPIRATION_DURATION = Duration.ofMinutes(15);

  @Override
  public String generateAuthenticationToken(
      final @NonNull AwsCredentialsProvider credentialsProvider,
      final @NonNull Region region,
      final @NonNull String hostname,
      final int port,
      final @NonNull String username) {

    // The following code is inspired by software.amazon.awssdk.services.rds.DefaultRdsUtilities,
    // method generateAuthenticationToken(GenerateAuthenticationTokenRequest request).
    // Update this code when original method changes.

    final Clock clock = Clock.systemUTC();
    final Aws4Signer signer = Aws4Signer.create();

    SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder()
        .method(SdkHttpMethod.GET)
        .protocol("https")
        .host(hostname)
        .port(port)
        .encodedPath("/")
        .putRawQueryParameter("DBUser", username)
        .putRawQueryParameter("Action", "connect")
        .build();

    Instant expirationTime = Instant.now(clock).plus(EXPIRATION_DURATION);

    final AwsCredentials credentials = CredentialUtils.toCredentials(
        CompletableFutureUtils.joinLikeSync(credentialsProvider.resolveIdentity()));

    Aws4PresignerParams presignRequest = Aws4PresignerParams.builder()
        .signingClockOverride(clock)
        .expirationTime(expirationTime)
        .awsCredentials(credentials)
        .signingName("rds-db")
        .signingRegion(region)
        .build();

    SdkHttpFullRequest fullRequest = signer.presign(httpRequest, presignRequest);
    String signedUrl = fullRequest.getUri().toString();

    // Format should be:
    // >:>/?Action=connect&DBUser=>&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expi...
    // Note: This must be the real RDS hostname, not proxy or tunnels
    String result = StringUtils.replacePrefixIgnoreCase(signedUrl, "https://", "");
    LOGGER.finest(() -> "Generated RDS authentication token with expiration of " + expirationTime);
    return result;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy