本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
以 IAM 進行身分驗證
概觀
當您的快取設定為使用 Redis OSS 第 7 版或更新版本時,您可以使用 AWS IAM 身分驗證與 ElastiCache (Redis OSS) 的連線進行身分驗證。這可讓您強化安全模型,並簡化許多管理安全任務。您也可以使用 IAM 身份驗證,為每個單獨的 ElastiCache 快取和使用 ElastiCache 者設定精細的存取控制,遵循最低權限許可原則。適用於 ElastiCache (Redis OSS) 的 IAM 身份驗證的運作方式是在 Redis OSS 或命令中提供短期的 IAM 身份驗證令牌,而不是長期使用壽命的 ElastiCache 使用者密碼。AUTH
HELLO
有關 IAM 身份驗證令牌的更多信息,請參閱《 AWS 一般參考指南》中的簽名版本 4 簽名過程和下面的代碼示例。
您可以使用 IAM 身分及其相關政策來進一步限制 Redis OSS 存取。您也可以將存取權授與使用者從其聯合身分提供者直接存取 Redis OSS 快取。
若要搭配 ElastiCache (Redis OSS) 使用 AWS IAM,您首先需要建立將身份驗證模式設定為 IAM 的使用 ElastiCache 者。然後,您可以建立或重複使用 IAM 身分。IAM 身分需要關聯的政策,才能將elasticache:Connect
動作授與 ElastiCache 快取和 ElastiCache 使用者。設定完成後,您可以使用 IAM 使用者或角色的 AWS 登入資料建立 IAM 身份驗證權杖。最後,當您連線至 Redis OSS 快取時,您需要在 Redis OSS 用戶端中提供短期的 IAM 身份驗證權杖做為密碼。支援憑證提供者的 Redis OSS 用戶端可以為每個新連線自動產生臨時登入資料。 ElastiCache (Redis OSS) 會針對啟 ElastiCache 用 IAM 的使用者的連線要求執行 IAM 身分驗證,並驗證與 IAM 的連線要求。
限制
使用 IAM 身分驗證,會套用以下限制:
使用 ElastiCache (Redis OSS) 7.0 版或以上版本時,可以使用 IAM 身份驗證。
對於已啟 ElastiCache 用 IAM 的使用者,使用者名稱和使用者 ID 屬性必須相同。
IAM 身分驗證字符的有效期限為 15 分鐘。對於長期連線,我們建議使用支援憑證提供者介面的 Redis OSS 用戶端。
與 ElastiCache (Redis OSS) 的 IAM 驗證連線會在 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 使用者指南》中的 AWS 全域條件內容索引鍵。
設定
設定 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.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 政策連接至角色。
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
連接
以字符做為密碼進行連線
首先,您需要使用 AWS 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();
以下是萵苣 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()); } }