web-dev-qa-db-ja.com

IAM認証とSpringJDBC(DataSourceおよびJdbcTemplace)を使用したAWSRDSへのアクセス

これを実装する方法がわかりません。ヘルプやポインタは大歓迎です。

現在、私のJava/Springアプリケーションバックエンドは EC2 にデプロイされており、通常のSpring JDBCセットアップを使用して、 RDS 上のMySQLに正常にアクセスしています。つまり、データベース情報をapplication.propertiesに格納し、DataSourceJdbcTemplate @ Configuration クラスに構成します。すべてが正常に動作します。

ここで、RDS上のMySQLに安全にアクセスする必要があります。 RDSインスタンスでIAM認証が有効になっています。また、 IAMロールを正常に作成し、インラインポリシーを適用しました。次に、AWSRDSドキュメントと このリンク のJavaの例に従って、スタンドアロンJavaからデータベースにアクセスできます。 class Authentication Token と、通常のdbユーザー名とパスワードの代わりに作成したユーザーを正常に使用しました。このスタンドアロンのJavaクラスは、「 Connection 」オブジェクトを直接処理しています。

私が立ち往生している場所は、これをSpring JDBC構成に変換する方法です。つまり、@ ConfigurationクラスでこのためにDataSourceおよびJdbcTemplateBeanを設定します。

これを実装するための正しい/正しいアプローチは何でしょうか?

-----編集-開始-----

これを複数のプロジェクトで使用できるライブラリとして実装しようとしています。つまり、JARとして使用され、プロジェクトのPOMファイルで依存関係として宣言されます。このライブラリには、一般的なDBユーザー名とパスワードを使用したこのRDSアクセス、IAM認証を使用したRDSアクセス、データ暗号化用のKMS(CMK /データキー)などの構成可能なAWSサービスが含まれます。

プロジェクトに応じて、任意のWeb /アプリサーバーでこのライブラリを使用することをお勧めします。

これが私の必要性をさらに明らかにすることを願っています。

-----編集-終了-----

DataSourceには内部にgetConnection()があるので、基本的に独自のDataSource実装を作成して、必要なものを実現できます。しかし、これは良いアプローチですか?

何かのようなもの:

public class MyDataSource implements DataSource {
    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = null;
        // get a connection using IAM Authentication Token for accessing AWS RDS, etc. as in the AWS docs
        return conn;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return getConnection();
    }

    //other methods
} 
9
Gauzy

次のスニペットは、SpringBoot/Tomcatによって提供されるデフォルトの接続プールの代わりに使用できます。トークンは15分間有効であるため、トークンのパスワードは10分ごとに更新されます。また、DNSホスト名からリージョンを抽出できることを前提としています。そうでない場合は、使用する地域を指定する必要があります。

public class RdsIamAuthDataSource extends org.Apache.Tomcat.jdbc.pool.DataSource {

private static final Logger LOG = LoggerFactory.getLogger(RdsIamAuthDataSource.class);

/**
 * The Java KeyStore (JKS) file that contains the Amazon root CAs
 */
public static final String RDS_CACERTS = "/rds-cacerts";
/**
 * Password for the ca-certs file.
 */
public static final String PASSWORD = "changeit";
public static final int DEFAULT_PORT = 3306;

@Override
public ConnectionPool createPool() throws SQLException {
    return pool != null ? pool : createPoolImpl();
}

protected synchronized ConnectionPool createPoolImpl() throws SQLException {
    return pool = new RdsIamAuthConnectionPool(poolProperties);
}

public static class RdsIamAuthConnectionPool extends ConnectionPool implements Runnable {

    private RdsIamAuthTokenGenerator rdsIamAuthTokenGenerator;
    private String Host;
    private String region;
    private int port;
    private String username;
    private Thread tokenThread;

    public RdsIamAuthConnectionPool(PoolConfiguration prop) throws SQLException {
        super(prop);
    }

    @Override
    protected void init(PoolConfiguration prop) throws SQLException {
        try {
            URI uri = new URI(prop.getUrl().substring(5));
            this.Host = uri.getHost();
            this.port = uri.getPort();
            if (this.port < 0) {
                this.port = DEFAULT_PORT;
            }
            this.region = StringUtils.split(this.Host,'.')[2]; // extract region from rds hostname
            this.username = prop.getUsername();
            this.rdsIamAuthTokenGenerator = RdsIamAuthTokenGenerator.builder().credentials(new DefaultAWSCredentialsProviderChain()).region(this.region).build();
            updatePassword(prop);
            final Properties props = prop.getDbProperties();
            props.setProperty("useSSL","true");
            props.setProperty("requireSSL","true");
            props.setProperty("trustCertificateKeyStoreUrl",getClass().getResource(RDS_CACERTS).toString());
            props.setProperty("trustCertificateKeyStorePassword", PASSWORD);
            super.init(prop);
            this.tokenThread = new Thread(this, "RdsIamAuthDataSourceTokenThread");
            this.tokenThread.setDaemon(true);
            this.tokenThread.start();
        } catch (URISyntaxException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    @Override
    public void run() {
        try {
            while (this.tokenThread != null) {
                Thread.sleep(10 * 60 * 1000); // wait for 10 minutes, then recreate the token
                updatePassword(getPoolProperties());
            }
        } catch (InterruptedException e) {
            LOG.debug("Background token thread interrupted");
        }
    }

    @Override
    protected void close(boolean force) {
        super.close(force);
        Thread t = tokenThread;
        tokenThread = null;
        if (t != null) {
            t.interrupt();
        }
    }

    private void updatePassword(PoolConfiguration props) {
        String token = rdsIamAuthTokenGenerator.getAuthToken(GetIamAuthTokenRequest.builder().hostname(Host).port(port).userName(this.username).build());
        LOG.debug("Updated IAM token for connection pool");
        props.setPassword(token);
    }
}
}

信頼できる接続を確立するには、Amazonのルート/中間証明書をインポートする必要があることに注意してください。上記のサンプルコードは、証明書が「rds-cacert」というファイルにインポートされ、クラスパスで使用できることを前提としています。または、JVMの「cacerts」ファイルにインポートすることもできます。

このデータソースを使用するには、Springの次のプロパティを使用できます。

datasource:
  url: jdbc:mysql://dbhost.xyz123abc.us-east-1.rds.amazonaws.com/dbname
  username: iam_app_user
  driver-class-name: com.mysql.cj.jdbc.Driver
  type: com.mydomain.jdbc.RdsIamAuthDataSource

Springの使用Java config:

@Bean public DataSource dataSource() { 
    PoolConfiguration props = new PoolProperties(); 
    props.setUrl("jdbc:mysql://dbname.abc123xyz.us-east-1.rds.amazonaws.com/dbschema"); 
    props.setUsername("iam_dbuser_app"); 
    props.setDriverClassName("com.mysql.jdbc.Driver"); 
    return new RdsIamAuthDataSource(props); 
}
6
blagerweij