Управляйте доступом к RDS с помощью AWS Secrets Manager

В настоящее время я работаю с Eclipse и AWS Toolkit для Eclipse. Мой проект уже работает, и он выполняет свою работу, которая заключается в подключении к экземпляру RDS и возврате объектов JSON для вызовов API-шлюза.

У меня только что появилось новое требование, мы должны использовать службу SecretsManager для автоматического изменения конфигурации RDS, такой как пользователи, пароли и так далее.

Проблема в том, что когда я пытаюсь импортировать классы, такие как GetSecretValueResponse, я получаю The import com.amazonaws.services.secretsmanager cannot be resolved. Когда я изучаю документацию и SDK, существует GetSecretValueRequest, но не GetSecretValueResponse, поэтому я не могу понять, что мне делать, и я не нашел ничего похожего на пример, который я мог бы изучить.

Следующий код - это то, что я пытаюсь реализовать, и он предоставляется самой Amazon (на странице Secrets Manager есть кнопка, которую вы можете щелкнуть, чтобы увидеть, как это будет работать с Java, в этом случае), и он представлен без каких-либо изменений. пока что, как я уже сказал, я не знаю, как импортировать несколько классов:

// Use this code snippet in your app.
public static void getSecret() {
String secretName = "secretName";
String endpoint = "secretEndpoint";
String region = "region";

AwsClientBuilder.EndpointConfiguration config = new AwsClientBuilder.EndpointConfiguration(endpoint, region);
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
clientBuilder.setEndpointConfiguration(config);
AWSSecretsManager client = clientBuilder.build();

String secret;
ByteBuffer binarySecretData;
GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
        .withSecretId(secretName)
        .build();
GetSecretValueResponse getSecretValueResponse = null;
try {
    getSecretValueResponse = client.getSecretValue(getSecretValueRequest);

} catch(ResourceNotFoundException e) {
    System.out.println("The requested secret " + secretName + " was not found");
} catch (InvalidRequestException e) {
    System.out.println("The request was invalid due to: " + e.getMessage());
} catch (InvalidParameterException e) {
    System.out.println("The request had invalid params: " + e.getMessage());
}

if(getSecretValueResponse == null) {
    return;
}

// Decrypted secret using the associated KMS CMK
// Depending on whether the secret was a string or binary, one of these fields will be populated
if(getSecretValueResponse.getSecretString() != null) {
    secret = getSecretValueResponse.getSecretString();
}
else {
    binarySecretData = getSecretValueResponse.getSecretBinary();
}

// Your code goes here. 
}

person monkey intern    schedule 11.06.2018    source источник


Ответы (6)


У меня была такая же проблема, код, который присутствует на странице AWS, не работает из коробки. Класс, который вы ищете: GetSecretValueResult Вот последние версии java-документации.

https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/secretsmanager/model/GetSecretValueResult.html

Вот часть, которая будет работать:

public void printRdsSecret() throws IOException {
    String secretName = "mySecretName";

    System.out.println("Requesting secret...");
    AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard().build();

    GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest().withSecretId(secretName);

    GetSecretValueResult getSecretValueResult = client.getSecretValue(getSecretValueRequest);

    System.out.println("secret retrieved ");
    final String secretBinaryString = getSecretValueResult.getSecretString();
    final ObjectMapper objectMapper = new ObjectMapper();

    final HashMap<String, String> secretMap = objectMapper.readValue(secretBinaryString, HashMap.class);

    String url = String.format("jdbc:postgresql://%s:%s/dbName", secretMap.get("host"), secretMap.get("port"));
    System.out.println("Secret url = "+url);
    System.out.println("Secret username = "+secretMap.get("username"));
    System.out.println("Secret password = "+secretMap.get("password"));
 }

Это было протестировано с aws-java-sdk-secretsmanager версии 1.11.337

person Nune Isabekyan    schedule 13.06.2018
comment
Большое спасибо за помощь @ Нуне Исабекян, я только что обнаружила это вчера, я не знаю, почему я так споткнулась об этом, но я не могла решить эту проблему! Ваше решение точно такое же, я рад, что я не один, и что вы поделились! - person monkey intern; 14.06.2018
comment
Я нахожу очень интересным ваше использование ObjectMapper, не знавшего о классе. Я использую JSONObject для его анализа, но ваш кажется чище ... - person monkey intern; 14.06.2018
comment
рад что помог! В идеале у вас должен быть собственный тип - объект Java, содержащий вашу конфигурацию, а затем вы читали бы свои конфигурации / секреты в объект вместо общего хэш-карты ... SecretConfig user = mapper.readValue (jsonInString, SecretConfig.class); - person Nune Isabekyan; 15.06.2018
comment
они обновили консоль, чтобы показать GetSecretValueResult также в SampleCode - person committedandroider; 02.07.2018
comment
@committedandroider - но он все равно дает результат как полный json. Вам все равно нужно проанализировать json, чтобы получить значение securestring. Не существует метода, который дал бы секретное значение непосредственно вам. - person sumit sachdeva; 15.03.2019

Думаю, основная проблема заключалась в отсутствии зависимостей от AWS SDK v2.

Добавьте сюда фрагмент кода, который использует AWS SDK v2. На всякий случай кто-нибудь это ищет.

package com.may.util;

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.secretsmanager.model.DecryptionFailureException;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
import software.amazon.awssdk.services.secretsmanager.model.InternalServiceErrorException;
import software.amazon.awssdk.services.secretsmanager.model.InvalidParameterException;
import software.amazon.awssdk.services.secretsmanager.model.InvalidRequestException;
import software.amazon.awssdk.services.secretsmanager.model.ResourceNotFoundException;

public class SecretsManagerUtil {

    public static String obtainSecret() {
        String secretName = "db_secret_name";
        String region = "us-east-1";

        SecretsManagerClient client = SecretsManagerClient.builder().region(Region.of(region)).build();
        GetSecretValueResponse response = null;

        try {
            response = client.getSecretValue(GetSecretValueRequest.builder().secretId(secretName).build());
        } catch (DecryptionFailureException e) {
            // Secrets Manager can't decrypt the protected secret text using the provided KMS key.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw e;
        } catch (InternalServiceErrorException e) {
            // An error occurred on the server side.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw e;
        } catch (InvalidParameterException e) {
            // You provided an invalid value for a parameter.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw e;
        } catch (InvalidRequestException e) {
            // You provided a parameter value that is not valid for the current state of the resource.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw e;
        } catch (ResourceNotFoundException e) {
            // We can't find the resource that you asked for.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw e;
        }

        return response.secretString();
    }
}

Десериализовать и распечатать секрет:

public class SecretPrinter {

private static final Logger logger = LoggerFactory.getLogger(SecretPrinter.class);

public void printSecret() {
    String json = SecretsManagerUtil.obtainSecret(); // secret in json format

    RdsSecret secret;
    try {
        secret = new ObjectMapper().disable(FAIL_ON_UNKNOWN_PROPERTIES).readValue(json, RdsSecret.class);
    } catch (IOException e) {
        logger.error("Couldn't parse secret obtained from AWS Secrets Manager!");
        throw new RuntimeException(e);
    }

    System.out.println("username: " + secret.getUsername());
    System.out.println("password: " + secret.getPassword());
}

static class RdsSecret {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

}

Maven:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>bom</artifactId>
      <version>2.6.3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>secretsmanager</artifactId>
</dependency>

Gradle:

implementation platform('software.amazon.awssdk:bom:2.6.3')
implementation 'software.amazon.awssdk:secretsmanager'
person Eugene Maysyuk    schedule 06.07.2019
comment
Когда разработчик запускает это локально, это нормально, что код не работает, потому что у машины нет необходимых разрешений для доступа к секрету, верно? Это будет готовый к производству код для развернутого экземпляра в облаке, который сам будет иметь доступ к секретам, потому что CodeCommit, связанный с экземпляром, имеет необходимые разрешения? (Моя текущая проблема заключается в том, что я пытаюсь запретить разработчикам доступ к секретам локально, но все же могу запускать код локально ... каков предлагаемый способ сделать это?) - person payne; 14.01.2020
comment
Локально GetSecretValueRequest.builder().secretId(secretName).build() вылетает с software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException: The security token included in the request is invalid.., Но я использую значение, предоставленное фрагментом кода, предоставленным мне при создании секрета в AWS Secrets Manager. - person payne; 14.01.2020
comment
@payne Этот код готов к производству. Насколько я понимаю (я, вероятно, где-то об этом читал), AWS SDK под капотом использует метаданные, прикрепленные к каждому физическому экземпляру (машине), чтобы проверить, имеет ли конкретная машина доступ к SecretsManager (и конкретному секрету). На локальном компьютере нет этих метаданных, поэтому локальный код не сможет получить доступ к AWS SecretsManager. Это можно настроить, но, честно говоря, я не знаю, как. Пожалуйста, оставьте здесь комментарий, если найдете полезную информацию по этому поводу. - person Eugene Maysyuk; 14.01.2020
comment
@payne, в ответ на комментарий один. Если разработчики смогут запускать код локально, они, скорее всего, смогут отлаживать код локально и оценивать простой пароль после получения из SecretsManager. - person Eugene Maysyuk; 14.01.2020
comment
Только что протестировали в развернутой среде: как и ожидалось, исключение, о котором я упоминал ранее, прошло, и теперь мы достигаем not authorized to perform: secretsmanager:GetSecretValue. Спасибо за вашу помощь! Мы решим эту проблему, добавив соответствующие разрешения. - person payne; 15.01.2020

aws-secretsmanager-jdbc можно использовать для доступа к AWS RDS через диспетчер секретов AWS. https://github.com/aws/aws-secretsmanager-jdbc

Ниже приводится содержание моего application.properties

spring.datasource.url=jdbc-secretsmanager:mysql://dev-xxxx-database.cluster-xxxxxxxxx.ap-southeast-1.rds.amazonaws.com:3306/dev_xxxxxx

spring.datasource.username=/secret/application
spring.datasource.driver-class-name=com.amazonaws.secretsmanager.sql.AWSSecretsManagerMySQLDriver

spring.jpa.database-platform = org.hibernate.dialect.MySQL5Dialect

Ниже приводится секрет секретного менеджера AWS.

введите здесь описание изображения

Используя этот метод, вам не нужно вручную получать имя пользователя и пароль и создавать источники данных.

person Harsha Jayamanna    schedule 05.11.2019
comment
А как насчет баз данных documentdb без sql? Есть ли в aws что-то подобное и для documentdb? - person Guchelkaben; 20.03.2020

просто добавьте библиотеку в свой pom: https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-secretsmanager

person ido flax    schedule 20.03.2019

Я рекомендую использовать оболочку aws secret manger jdbc для доступа к RDS (https://github.com/aws/aws-secretsmanager-jdbc). Вам не нужно иметь дело с получением секрета, декодированием и перемещением пароля в тексте перед передачей клиенту RDS.

Просто передайте секретный идентификатор клиенту RDS, а оболочка jdbc сделает все остальное.

person Prakash Tanaji    schedule 14.08.2019

Вам не хватает зависимости aws-java-sdk-secretsmanager, вам нужно добавить ее в свой pom.xml, а затем импортировать в свой класс java.

Из Maven:

    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-secretsmanager</artifactId>
        <version>1.11.355 </version>
    </dependency>

Справочник по AWS

Если вы не используете maven, у вас есть добавьте AWS SDK в существующий проект.

person Rafael Paredes    schedule 23.08.2020