IAM角色访问AWS RDS For MySQL

发布于:2025-05-27 ⋅ 阅读:(24) ⋅ 点赞:(0)

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目录
    • 查看configcredentials文件
    • credentials中配置IAM用户的accessIdaccessSecret; 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信任实体配置
    • 跳板机执行命令:
      aws sts assume-role --role-arn "arn:aws-cn:iam::xxxxx:role/rolename" --role-session-name 主账号 --profile profile
      
      TIPS: 这里的rolename即是需要配置的role
使用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:
      aws rds generate-db-auth-token --hostname $RDSHOST --port 3306 --region cn-northwest-1 --username dbuser --profile xxxx_profile
      
      xxxx_profile是自定义的profile名称,不加–profile 参数默认使用default
    • 将获取到的token写入变量:
      export TOKEN="...token..."
      
    • 引用变量进行连接:
      mysql --host=$RDSHOST --port=3306 --enable-cleartext-plugin --user=dbuser --password=$TOKEN
      
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

网站公告

今日签到

点亮在社区的每一天
去签到