本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用 IAM 进行身份验证
概述
使用 IAM 身份验证,您可以在缓存配置为使用 Redis 版本 7 或更高版本时,使用 Amazon IAM 身份对与 ElastiCache for Redis 的连接进行身份验证。这使您可以增强安全模型并简化许多管理安全任务。您还可以使用 IAM 身份验证,遵循最低权限原则,为每个单独的 ElastiCache 缓存和 ElastiCache 用户配置精细的访问控制。ElastiCache for Redis 的 IAM 身份验证的工作原理是在 Redis AUTH
或 HELLO
命令中提供有效期很短的 IAM 身份验证令牌,而不是有效期很长的 ElastiCache 用户密码。有关 IAM 身份验证令牌的更多信息,请参阅《Amazon 一般参考指南》中的 Signature Version 4 签名流程和下面的代码示例。
您可以使用 IAM 身份及其关联策略进一步限制 Redis 访问权限。您还可以直接从联合身份提供商向用户授予对 Redis 缓存的访问权限。
要将 Amazon IAM 与 ElastiCache for Redis 一起使用,您首先需要创建身份验证模式设置为 IAM 的 ElastiCache 用户,然后创建或重用 IAM 身份。IAM 身份需要关联策略来向 ElastiCache 缓存和 ElastiCache 用户授予 elasticache:Connect
操作权限。配置完成后,您可以使用 IAM 用户或角色的 Amazon 凭证创建 IAM 身份验证令牌。最后,在连接到 Redis 缓存时,您需要在 Redis 客户端中提供有效期较短的 IAM 身份验证令牌作为密码。支持凭证提供程序的 Redis 客户端可以为每个新连接自动生成临时凭证。ElastiCache for Redis 将对启用 IAM 的 ElastiCache 用户的连接请求执行 IAM 身份验证,并将通过 IAM 验证连接请求。
限制
使用 IAM 身份验证时,以下限制适用:
使用 ElastiCache for Redis 版本 7.0 或更高版本时,IAM 身份验证可用。
对于启用了 IAM 的 ElastiCache 用户,用户名和用户 ID 属性必须相同。
IAM 身份验证令牌的有效期为 15 分钟。对于长时间的连接,建议使用支持凭证提供程序接口的 Redis 客户端。
经过 IAM 身份验证的 ElastiCache for Redis 连接将在 12 小时后自动断开。通过使用新 IAM 身份验证令牌发送
AUTH
或HELLO
命令,可以将连接延长 12 小时。MULTI EXEC
命令不支持 IAM 身份验证。目前,IAM 身份验证支持以下全局条件上下文键:
对无服务器缓存使用 IAM 身份验证时,支持
aws:VpcSourceIp
、aws:SourceVpc
、aws:SourceVpce
、aws:CurrentTime
、aws:EpochTime
和aws:ResourceTag/%s
(从关联的无服务器缓存和用户)。对复制组使用 IAM 身份验证时,支持
aws:SourceIp
和aws:ResourceTag/%s
(从关联的复制组和用户)。
有关全局条件上下文键的更多信息,请参阅《IAM 用户指南》中的 Amazon 全局条件上下文键。
设置
要设置 IAM 身份验证,请执行以下操作:
创建缓存
aws elasticache create-serverless-cache \ --serverless-cache-name cache-01 \ --description "ElastiCache IAM auth application" \ --engine redis
为您的角色创建 IAM 信任政策文档,如下所示,允许您的账户承担新角色。将策略保存到名为 trust-policy.json 的文件中。
{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:root" }, "Action": "sts:AssumeRole" } }
创建 IAM policy 文档,如下所示。将策略保存到名为 policy.json 的文件中。
{ "Version": "2012-10-17", "Statement": [ { "Effect" : "Allow", "Action" : [ "elasticache:Connect" ], "Resource" : [ "arn:aws:elasticache:us-east-1:123456789012:serverlesscache:cache-01", "arn:aws:elasticache:us-east-1:123456789012:user:iam-user-01" ] } ] }
创建一个 IAM 角色。
aws iam create-role \ --role-name "elasticache-iam-auth-app" \ --assume-role-policy-document file://trust-policy.json
创建 IAM policy。
aws iam create-policy \ --policy-name "elasticache-allow-all" \ --policy-document file://policy.json
向角色附加 IAM policy。
aws iam attach-role-policy \ --role-name "elasticache-iam-auth-app" \ --policy-arn "arn:aws:iam::123456789012:policy/elasticache-allow-all"
创建启用 IAM 的新用户。
aws elasticache create-user \ --user-name iam-user-01 \ --user-id iam-user-01 \ --authentication-mode Type=iam \ --engine redis \ --access-string "on ~* +@all"
创建用户组并附加用户。
aws elasticache create-user-group \ --user-group-id iam-user-group-01 \ --engine redis \ --user-ids default iam-user-01 aws elasticache modify-serverless-cache \ --serverless-cache-name cache-01 \ --user-group-id iam-user-group-01
连接
使用令牌作为密码进行连接
您首先需要使用 Amazon SigV4 预签名请求生成有效期较短的 IAM 身份验证令牌。之后,您需要在连接到 Redis 缓存时提供 IAM 身份验证令牌作为密码,如下例所示。
String userId = "
insert user id
"; String cacheName = "insert cache name
"; boolean isServerless =true
; String region = "insert region
"; // Create a default AWS Credentials provider. // This will look for AWS credentials defined in environment variables or system properties. AWSCredentialsProvider awsCredentialsProvider = new DefaultAWSCredentialsProviderChain(); // Create an IAM authentication token request and signed it using the AWS credentials. // The pre-signed request URL is used as an IAM authentication token for ElastiCache Redis. IAMAuthTokenRequest iamAuthTokenRequest = new IAMAuthTokenRequest(userId, cacheName, region, isServerless); String iamAuthToken = iamAuthTokenRequest.toSignedRequestUri(awsCredentialsProvider.getCredentials()); // Construct Redis URL with IAM Auth credentials provider RedisURI redisURI = RedisURI.builder() .withHost(host) .withPort(port) .withSsl(ssl) .withAuthentication(userId, iamAuthToken) .build(); // Create a new Lettuce Redis client RedisClient client = RedisClient.create(redisURI); client.connect();
以下为 IAMAuthTokenRequest
的定义。
public class IAMAuthTokenRequest { private static final HttpMethodName REQUEST_METHOD = HttpMethodName.GET; private static final String REQUEST_PROTOCOL = "http://"; private static final String PARAM_ACTION = "Action"; private static final String PARAM_USER = "User"; private static final String PARAM_RESOURCE_TYPE = "ResourceType"; private static final String RESOURCE_TYPE_SERVERLESS_CACHE = "ServerlessCache"; private static final String ACTION_NAME = "connect"; private static final String SERVICE_NAME = "elasticache"; private static final long TOKEN_EXPIRY_SECONDS = 900; private final String userId; private final String cacheName; private final String region; private final boolean isServerless; public IAMAuthTokenRequest(String userId, String cacheName, String region, boolean isServerless) { this.userId = userId; this.cacheName = cacheName; this.region = region; this.isServerless = isServerless; } public String toSignedRequestUri(AWSCredentials credentials) throws URISyntaxException { Request<Void> request = getSignableRequest(); sign(request, credentials); return new URIBuilder(request.getEndpoint()) .addParameters(toNamedValuePair(request.getParameters())) .build() .toString() .replace(REQUEST_PROTOCOL, ""); } private <T> Request<T> getSignableRequest() { Request<T> request = new DefaultRequest<>(SERVICE_NAME); request.setHttpMethod(REQUEST_METHOD); request.setEndpoint(getRequestUri()); request.addParameters(PARAM_ACTION, Collections.singletonList(ACTION_NAME)); request.addParameters(PARAM_USER, Collections.singletonList(userId)); if (isServerless) { request.addParameters(PARAM_RESOURCE_TYPE, Collections.singletonList(RESOURCE_TYPE_SERVERLESS_CACHE)); } return request; } private URI getRequestUri() { return URI.create(String.format("%s%s/", REQUEST_PROTOCOL, cacheName)); } private <T> void sign(SignableRequest<T> request, AWSCredentials credentials) { AWS4Signer signer = new AWS4Signer(); signer.setRegionName(region); signer.setServiceName(SERVICE_NAME); DateTime dateTime = DateTime.now(); dateTime = dateTime.plus(Duration.standardSeconds(TOKEN_EXPIRY_SECONDS)); signer.presignRequest(request, credentials, dateTime.toDate()); } private static List<NameValuePair> toNamedValuePair(Map<String, List<String>> in) { return in.entrySet().stream() .map(e -> new BasicNameValuePair(e.getKey(), e.getValue().get(0))) .collect(Collectors.toList()); } }
使用凭证提供程序进行连接
以下代码显示了如何使用 IAM 身份验证凭证提供程序通过 ElastiCache for Redis 进行身份验证。
String userId = "
insert user id
"; String cacheName = "insert cache name
"; boolean isServerless =true
; String region = "insert region
"; // Create a default AWS Credentials provider. // This will look for AWS credentials defined in environment variables or system properties. AWSCredentialsProvider awsCredentialsProvider = new DefaultAWSCredentialsProviderChain(); // Create an IAM authentication token request. Once this request is signed it can be used as an // IAM authentication token for ElastiCache Redis. IAMAuthTokenRequest iamAuthTokenRequest = new IAMAuthTokenRequest(userId, cacheName, region, isServerless); // Create a Redis credentials provider using IAM credentials. RedisCredentialsProvider redisCredentialsProvider = new RedisIAMAuthCredentialsProvider( userId, iamAuthTokenRequest, awsCredentialsProvider); // Construct Redis URL with IAM Auth credentials provider RedisURI redisURI = RedisURI.builder() .withHost(host) .withPort(port) .withSsl(ssl) .withAuthentication(redisCredentialsProvider) .build(); // Create a new Lettuce Redis client RedisClient client = RedisClient.create(redisURI); client.connect();
以下是 Lettuce Redis 客户端的示例,该客户端将 IAMAuthTokenRequest 封装在凭证提供程序中,以便在需要时自动生成临时凭证。
public class RedisIAMAuthCredentialsProvider implements RedisCredentialsProvider { private static final long TOKEN_EXPIRY_SECONDS = 900; private final AWSCredentialsProvider awsCredentialsProvider; private final String userId; private final IAMAuthTokenRequest iamAuthTokenRequest; private final Supplier<String> iamAuthTokenSupplier; public RedisIAMAuthCredentialsProvider(String userId, IAMAuthTokenRequest iamAuthTokenRequest, AWSCredentialsProvider awsCredentialsProvider) { this.userName = userName; this.awsCredentialsProvider = awsCredentialsProvider; this.iamAuthTokenRequest = iamAuthTokenRequest; this.iamAuthTokenSupplier = Suppliers.memoizeWithExpiration(this::getIamAuthToken, TOKEN_EXPIRY_SECONDS, TimeUnit.SECONDS); } @Override public Mono<RedisCredentials> resolveCredentials() { return Mono.just(RedisCredentials.just(userId, iamAuthTokenSupplier.get())); } private String getIamAuthToken() { return iamAuthTokenRequest.toSignedRequestUri(awsCredentialsProvider.getCredentials()); } }