IAM角色访问AWS RDS For MySQL
Tips: 写这篇文章,主要是用作记录;在AWS配置IAM RDS 角色权限访问,官方文档不怎么全,踩了一些坑…
AWS云上配置
开启IAM身份验证
- 登录AWS控制台
- 搜索并进入Databases管理页面
- 选择数据库实例,点击修改按钮
- 在Database authentication部分,选择Password and IAM database authentication,启用IAM数据库身份验证
创建IAM Policy
- 搜索策略,创建策略,选择JSON配置; Resource指向的是 db实例/数据库账号
- 示例策略:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Statement1", "Effect": "Allow", "Action": [ "rds-db:connect" ], "Resource": [ "arn:aws-cn:rds-db:cn-northwest-1:xxxxxxxx:dbuser:cluster-xxxxxxxx/dbuser" ] } ] }
创建IAM角色并附加策略
创建一个角色,选择RDS,并附加上述策略
编辑新创建的权限,信任关系,指向session;即AWS那个字段,取值为个人账号arn
示例信任关系:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws-cn:iam::xxxxx:user/个人acount", "Service": [ "rds.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] }
AWS Config 配置
- 新增role配置
- 登录到宿主机,进入
.aws
目录 - 查看
config
和credentials
文件 - 在
credentials
中配置IAM用户的accessId
和accessSecret
; config与 credentials 通过 profile {name} 匹配 - 在
config
中新增相关配置:[profile prodaccess] role_arn = arn:aws:iam::xxxxxxxx:role/ProductionAccessRole source_profile = default
- 如果 default的credentials配置无role权限,请切换profile或者授权
- 登录到宿主机,进入
Role授权
- AWS上role信任实体配置
- 跳板机执行命令:
TIPS: 这里的rolename即是需要配置的roleaws sts assume-role --role-arn "arn:aws-cn:iam::xxxxx:role/rolename" --role-session-name 主账号 --profile profile
- 跳板机执行命令:
使用IAM身份验证创建数据库账户
- 创建数据库用户
- 需要与策略中的用户保持一致,用户名为
dbuser
,可以换成自己的 - 命令:
CREATE USER 'dbuser' IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
- 如果要开启SSL:
ALTER USER 'dbuser'@'%' REQUIRE SSL;
- 需要与策略中的用户保持一致,用户名为
命令行连通性验证
- 写入变量
- 设置RDS主机名:
export RDSHOST="rds-aurora-xxx.cluster-xxxx.rds.cn-northwest-1.amazonaws.com.cn"
- 获取token:
xxxx_profile是自定义的profile名称,不加–profile 参数默认使用defaultaws rds generate-db-auth-token --hostname $RDSHOST --port 3306 --region cn-northwest-1 --username dbuser --profile xxxx_profile
- 将获取到的token写入变量:
export TOKEN="...token..."
- 引用变量进行连接:
mysql --host=$RDSHOST --port=3306 --enable-cleartext-plugin --user=dbuser --password=$TOKEN
- 设置RDS主机名:
SpringBoot应用接入Datasource
- 新增配置
- 在
application.properties
中添加:aws.rds.host=rds-aurora-xxxxx.cluster-xxxxx.rds.cn-northwest-1.amazonaws.com.cn aws.rds.port=3306 aws.rds.user=dbuser aws.rds.dbname=dbuser aws.region=cn-northwest-1
- 添加AWS SDK依赖:
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>rds</artifactId> <version>2.20.0</version> </dependency>
- 剔除SpringBoot数据源配置文件注入,重新注入DataSource
- 在
package com.xxl.job.admin.core.conf;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.rds.RdsUtilities;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Slf4j
@Configuration
public class DynamicDataSourceConfig {
@Value("${aws.rds.host}") private String dbHost;
@Value("${aws.rds.port}") private int dbPort;
@Value("${aws.rds.user}") private String dbUser;
@Value("${aws.rds.dbname}") private String dbName;
@Value("${aws.region}") private String region;
@Bean
@Primary
public DataSource dataSource() {
log.info("[DynamicDataSourceConfig] create datasource start");
String token = refreshIamToken();
log.info("[DynamicDataSourceConfig] get token: {}", token);
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(buildJdbcUrl());
dataSource.setUsername(dbUser);
dataSource.setPassword(token);
//TODO 验证可用性,可删除
validateDataSource(dataSource);
// 每10分钟刷新令牌
// 临时凭证,默认有效期为15分钟
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() ->
dataSource.setPassword(refreshIamToken()),
10, 10, TimeUnit.MINUTES
);
return dataSource;
}
private void validateDataSource(HikariDataSource dataSource) {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery("SELECT 1");
log.info("Database connection test succeeded");
} catch (SQLException e) {
log.error("Database connection test FAILED", e);
throw new RuntimeException("Datasource validation failed", e);
}
}
private String refreshIamToken() {
return generateAuthToken(
dbHost,
dbPort,
dbUser,
Region.of(region)
);
}
private String buildJdbcUrl() {
return String.format("jdbc:mysql://%s:%d/%s?" +
"allowCleartextPasswords=true&useUnicode=true&characterEncoding=UTF-8"
+ "&autoReconnect=true&serverTimezone=Asia/Shanghai"
+ "&useSSL=false&requireSSL=false",
dbHost, dbPort, dbName);
}
private String generateAuthToken(String dbHost, int dbPort, String dbUser, Region region) {
//profile需做成环境变量
//dev_mysql_auth_profile, 这里换成对应的profile名称,若没有自定义配置则设置为default
AwsCredentialsProvider credentialsProvider =
ProfileCredentialsProvider.create("dev_mysql_auth_profile");
return RdsUtilities.builder()
.credentialsProvider(credentialsProvider)
.region(region)
.build()
.generateAuthenticationToken(b -> b
.hostname(dbHost)
.port(dbPort)
.username(dbUser)
.region(region)
);
}
}
- 使用默认凭证链,自动获取Pod角色权限:
AwsCredentialsProvider
K8s secret 挂载映射
- 代码中使用:AwsCredentialsProvider
- 使用默认凭证链,自动获取Pod角色权限:AwsCredentialsProvider
官方文档
- 参考AWS官方文档以获取更多详细信息。
- 通用文档:https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.Java.html
- 切换角色:https://docs.amazonaws.cn/IAM/latest/UserGuide/id_roles_use_switch-role-cli.html
- 信任授权:https://repost.aws/zh-Hans/knowledge-center/iam-assume-role-error