本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用 IAM 进行身份验证
概述
使用 IAM 身份验证,当您的缓存配置为使用 Redis OSS 版本 7 或更高版本时,您可以使用 Amazon IAM 身份验证与 ElastiCache (Redis OSS) 的连接。这使您可以增强安全模型并简化许多管理安全任务。您还可以使用 IAM 身份验证按照最低权限原则为每个 ElastiCache 缓存和 ElastiCache 用户配置精细的访问控制。适用于 ElastiCache (Redis OSS) 的 IAM 身份验证的工作原理是,在 Redis OSS 或命令中提供短期的 IAM 身份验证令牌,而不是长期存在的 ElastiCache 用户密码。AUTH
HELLO
有关 IAM 身份验证令牌的更多信息,请参阅《 Amazon 通用参考指南》中的签名版本 4 签名流程和下面的代码示例。
您可以使用 IAM 身份及其相关策略来进一步限制 Redis OSS 的访问权限。您也可以直接向来自联合身份提供商的用户授予对 Redis OSS 缓存的访问权限。
要 Amazon 将 IAM 与 ElastiCache (Redis OSS) 配合使用,您首先需要创建一个身份验证模式设置为 IAM 的 ElastiCache 用户。然后,您可以创建或重复使用 IAM 身份。IAM 身份需要关联策略才能将elasticache:Connect
操作授予 ElastiCache 缓存和 ElastiCache 用户。配置完成后,您可以使用 IAM 用户或角色的 Amazon 证书创建 IAM 身份验证令牌。最后,在连接到 Redis OSS 缓存时,您需要在您的 Redis OSS 客户端中提供短暂的 IAM 身份验证令牌作为密码。支持凭证提供程序的 Redis OSS 客户端可以自动为每个新连接生成临时证书。 ElastiCache (Redis OSS) 将对支持 IAM 的 ElastiCache 用户的连接请求执行 IAM 身份验证,并将通过 IAM 验证连接请求。
限制
使用 IAM 身份验证时,以下限制适用:
使用 ElastiCache (Redis OSS) 7.0 或更高版本时,可以使用 IAM 身份验证。
对于启用 IAM 的 ElastiCache 用户,用户名和用户 ID 属性必须相同。
IAM 身份验证令牌的有效期为 15 分钟。对于长期连接,我们建议使用支持凭证提供程序接口的 Redis OSS 客户端。
通过 IAM 身份验证的 ElastiCache (Redis OSS) 连接将在 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 策略。
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 OSS 缓存时,您需要提供 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 OSS). IAMAuthTokenRequest iamAuthTokenRequest = new IAMAuthTokenRequest(userId, cacheName, region, isServerless); String iamAuthToken = iamAuthTokenRequest.toSignedRequestUri(awsCredentialsProvider.getCredentials()); // Construct Redis OSS 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 OSS 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 (Redis OSS) 进行身份验证。
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 OSS). IAMAuthTokenRequest iamAuthTokenRequest = new IAMAuthTokenRequest(userId, cacheName, region, isServerless); // Create a Redis OSS credentials provider using IAM credentials. RedisCredentialsProvider redisCredentialsProvider = new RedisIAMAuthCredentialsProvider( userId, iamAuthTokenRequest, awsCredentialsProvider); // Construct Redis OSS URL with IAM Auth credentials provider RedisURI redisURI = RedisURI.builder() .withHost(host) .withPort(port) .withSsl(ssl) .withAuthentication(redisCredentialsProvider) .build(); // Create a new Lettuce Redis OSS client RedisClient client = RedisClient.create(redisURI); client.connect();
以下是 Lettuce Redis OSS 客户端的示例,该客户端将 IAM 封装在证书提供程序AuthTokenRequest 中,以便在需要时自动生成临时证书。
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()); } }