Snowflakeキーペア認証を利用したMuleアプリケーションの構築手順

Stay hungry. Stay foolish. Your time is limited, so don't waste it living someone else's life.

Snowflakeキーペア認証を利用したMuleアプリケーションの構築手順

はじめに

 Snowflakeにキーペア認証を利用して、データを照会するMuleアプリケーションを構築機会があったので、構築する手順について説明する。
  Snowflake Connectorではなく、Database Connectorを利用した理由は、キーペア認証はSnowflake Connectorでサポートされていないためである。
 Database Connectorの接続設定で、Snowflakeのキーペア認証をする場合は、JDBCのURLパラメータで必要な情報を渡す必要があり、証明書の場所を指定する場合は注意が必要である。絶対パスで指定する必要があり、エスケープやスペースが入るフォルダを指定することができないため、暗号化ファイルの配置処理を実装した。
 配置処理は、標準コンポーネントを利用して配置することも可能だが今回は、Javaプログラムを利用して配置している。
 同じ要件が発生した場合、是非、活用してみてください。では、

構築手順

以下の手順で構築を行う。

  1. 暗号キーと公開キーを作成する。
  2. Snowflakeのユーザに公開キーを設定する。
  3. Muleアプリケーションを構築する。
  4. POSTMANから動作を確認する。
  5. 関連エラーと解決方法を紹介する。

手順詳細

1.暗号キーと公開キーを作成する。

 今回は、秘密鍵をパスワードで暗号化せずに作成する。

$ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
$ openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub

 もし、秘密鍵をパスワードで暗号化して作成したい場合は下記のコマンドを実行する。
パスワードで暗号化する場合、Opensslの暗号化ライブラリに問題があり互換性がある暗号を指定する必要がある。

https://community.snowflake.com/s/article/Private-key-provided-is-invalid-or-not-supported-rsa-key-p8–data-isn-t-an-object-ID

$ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -v1 PBE-SHA1-RC4-128 -out rsa_key.p8
$ openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub

2.Snowflakeのユーザに公開キーを設定する。

 コンソールを使って作成した公開鍵をSnowflakeの連携ユーザに設定する。
 設定するユーザは、Security Adminロールが必要となる。

 追加するコマンドは以下の通り。

$ alter user {UserName} set rsa_public_key='{公開鍵の内容}';

3.Muleアプリケーションを構築する。

 Snowflake Connectorは、キーペア認証をサポートしていないため、Database Connectorを利用して接続する。
 暗号キーの配置は、JDBCのURLに指定して認証する必要があるため、URLに設定できるフォルダに暗号鍵を移動する必要がある。
 サンプルアプリケーションを用意したので、実際の実装をみたい場合は活用する。

Github:サンプルMuleアプリケーション

接続設定は以下の通り。
[Generic Connection]を利用して、必要な情報をJDBCに渡す。

SnowflakeJDBCドライバーのMavenリポジトリ情報。

<dependency>
  <groupId>net.snowflake</groupId>
  <artifactId>snowflake-jdbc</artifactId>
  <version>3.13.3</version>
</dependency>

設定ファイルの情報は以下の通り。

snowflake:
  url: jdbc:snowflake://ku55653.ap-northeast-1.aws.snowflakecomputing.com/?warehouse=${snowflake.warehouse}&db=${snowflake.db}&private_key_file=
  user: XXXXXX
  warehouse: COMPUTE_WH
  db: SNOWFLAKE_SAMPLE_DATA  
  password: XXXXXX
  keyfilename: rsa_key.p8

配置を変更する場合は、標準のコンポーネントでできないために、Javaを利用して変更する。
暗号化ファイルの配置処理は以下の通り。

<java:invoke-static doc:name="暗号ファイルコピー処理" doc:id="285bc1d5-9a5d-4b4f-9b16-035ad190c6b9" class="FileUtil" method="copyPrivateKeyFile(java.lang.String,java.lang.String)">
  <java:args ><![CDATA[#[{
 "copypath": vars.privateKeyPath,
 "content": readUrl("classpath://rsa_key.p8","text/plain") as String
}]]]></java:args>
</java:invoke-static>

Javaコードは以下の通り。
今回は、生成の成否をアプリケーションログに出力したかったため、ロガーを設定した。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class FileUtil {

	private static Logger logger = LoggerFactory.getLogger(FileUtil.class);
	  
	public static Boolean copyPrivateKeyFile(String copypath, String content) {

		File file = new File(copypath);
		logger.trace(content);
		logger.info(copypath);

		if (file.exists()) {
			logger.info(">>> Exist Key File.");
			return false;
		}

		logger.info(">>> Create Key File.");
		try {
			FileWriter filewriter = new FileWriter(file);
			filewriter.write(content);
			filewriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return true;
	}
}

Javaプログラムでアプリケーションにログ出力したい場合は以下のライブラリーを追加する。

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.25</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.25</version>
</dependency>

4.POSTMANから動作を確認する。

正常にSnowflakeのサンプルデータを取得することができた。

Workerの再起動時は、一時フォルダに暗号鍵を配置する処理が実行される。

2回目以降から、実行されないことが分かる。

5.関連エラーと解決方法を紹介する。

 暗号鍵が取得できない場合は以下のエラーが発生する。

org.mule.runtime.api.connection.ConnectionException: Could not obtain connection from data source	
Caused by: org.mule.extension.db.api.exception.connection.ConnectionCreationException: Could not obtain connection from data source	
Caused by: org.mule.runtime.extension.api.exception.ModuleException: java.sql.SQLException: Cannot get connection for URL jdbc:snowflake://XXXXX.ap-northeast-1.aws.snowflakecomputing.com/?warehouse=COMPUTE_WH&db=SNOWFLAKE_SAMPLE_DATA&private_key_file=rsa_key.p8 : Private key provided is invalid or not supported: rsa_key.p8: rsa_key.p8	
Caused by: java.sql.SQLException: Cannot get connection for URL jdbc:snowflake://XXXXX.ap-northeast-1.aws.snowflakecomputing.com/?warehouse=COMPUTE_WH&db=SNOWFLAKE_SAMPLE_DATA&private_key_file=rsa_key.p8 : Private key provided is invalid or not supported: rsa_key.p8: rsa_key.p8	
	at org.mule.extension.db.internal.domain.connection.JdbcConnectionFactory.createConnection(JdbcConnectionFactory.java:57) ~[mule-db-connector-1.8.1-mule-plugin.jar:?]

vCoreが0.2以下の場合、初期化できないエラーが発生する。

********************************************************************************
Message               : JDBC driver internal error: exception creating result java.lang.NoClassDefFoundError: Could not initialize class net.snowflake.client.jdbc.internal.apache.arrow.memory.rounding.DefaultRoundingPolicy at net.snowflake.client.jdbc.internal.apache.arrow.memory.RootAllocator.<init>(RootAllocator.java:40).
Element               : snowflake-keypair-appFlow/processors/2 @ snowflake-keypair-app:snowflake-keypair-app.xml:28 (検索処理)
Element DSL           : <db:select doc:name="検索処理" doc:id="5a8fd90c-5fbe-4028-8881-2cc7e4a80512" config-ref="Database_Config">
<db:sql><![CDATA[
Select * From "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF1000"."CUSTOMER" limit 1
]]></db:sql>
</db:select>
Error type            : DB:QUERY_EXECUTION
FlowStack             : at snowflake-keypair-appFlow(snowflake-keypair-appFlow/processors/2 @ snowflake-keypair-app:snowflake-keypair-app.xml:28 (検索処理))
Payload Type          : java.lang.Boolean
--------------------------------------------------------------------------------
Root Exception stack trace:
net.snowflake.client.jdbc.SnowflakeSQLException: JDBC driver internal error: exception creating result java.lang.NoClassDefFoundError: Could not initialize class net.snowflake.client.jdbc.internal.apache.arrow.memory.rounding.DefaultRoundingPolicy at net.snowflake.client.jdbc.internal.apache.arrow.memory.RootAllocator.<init>(RootAllocator.java:40).

一時フォルダ以外に、ファイルを配置しようとするとパーミッションエラーが発生する。

さいごに

 いかがだったでしょうか?
 構築手順やエラーの解決方法を理解していれば、難しくない手順です。
 是非、同じような要件があった場合は活用してみてください。では、