🐢

オンプレでデータレイクハウスのユーザを作って配る

に公開

今までのあらすじ

https://zenn.dev/evakichi/books/ab58dab238083e

Apache Sparkでデータレイクハウスを作ってみました。

今回の目標

今回はオンプレデータレイクハウス(MinIO+PostgreSQL)を使ったときに、データのアクセス権を配る方法を編み出してみました。

ユーザを作る

材料

今回ユーザを作るには、以下のモジュールを使います。(Debianの場合)

  • at
  • pwgen
  • expect

つまりこうします。

$ sudo apt install at pwgen expect

そして、MinIOの操作をするのにMinIO Clientをダウンロードしておく必要があります。
ここを参考にmcをインストールしてください。

方針

方針としては以下の通りです。

  1. MinIOのユーザを作る
  2. MinIOのユーザにポリシーを与えてACCESS_KEYとSECRET_KEYを作る
  3. PostgreSQLユーザを作成する
  4. atコマンドでユーザを削除するスクリプトを登録する。

では一個ずつ解説してきます。

ユーザ作成ルーチンの作成

基本情報を作成する

基本情報は以下の通りで作っています。

# 頭文字がアルファベットでそれ以降31文字の計32文字のユーザ名を作成する。
USER_NAME=$(pwgen -1 -0 -B -A -s 1)$(pwgen -1 -B -A -n -s 31) 
# パスワードは64文字。
USER_PASSWORD=$(pwgen -1 -B -c -n -s -y -r "\"\'\`!;&|" 64)
# 日時を取っておく。
DATE=$(date '+%Y%m%d-%H%M%S-%N')
# 作成したユーザを書き留めておくテキストファイルを作成する。(以後パスポートファイルと呼びます。)
PASSPORT_FILENAME="./userinfo/UserInfo-${DATE}-${USER_NAME}.txt"
# 消滅時間(分)を生成する。第一引数がなければ16とする。
DURATION=${1:-16}

MinIOとPostgreSQLのAdminアカウント情報を得る

MinIOとPostgreSQLのアカウント情報はあらかじめ環境変数で持っています。それを読んでいます。

. ../.env

情報をパスポートファイルに書き出す。

# 自サーバ名を書き込む
echo "export STORAGE_NAME='$(hostname -f)'" >> ${PASSPORT_FILENAME}
# PostgreSQLのポート番号を書き込む
echo "export POSTGRES_PORT='${POSTGRES_PORT}'" >> ${PASSPORT_FILENAME}
# PostgreSQLのデータベース名を書き込む
echo "export POSTGRES_DBNAME='${POSTGRES_DBNAME}'" >> ${PASSPORT_FILENAME}
# PostgreSQLのユーザ名を書き込む
echo "export POSTGRES_USERNAME='${USER_NAME}'" >> ${PASSPORT_FILENAME}
# PostgreSQLのユーザパスワードを書き込む
echo "export POSTGRES_PASSWORD='${USER_PASSWORD}'" >> ${PASSPORT_FILENAME}
# MinIOのCLIのポートを書き込む
echo "export MINIO_CLI_PORT='${MINIO_CLI_PORT}'" >> ${PASSPORT_FILENAME}
# MinIOのバケット名を書き込む
echo "export MINIO_BUCKET_NAME='${MINIO_BUCKET_NAME}'" >> ${PASSPORT_FILENAME}

MinIOのアカウントを作成する。

その1、テンポラリのAliasを作る

MinIOではCLIで動かすときにAliasを使用して色々します。ですのでアカウント追加用のAliasを作成します。

mc alias set MINIO_ADDUSER https://${DOMAIN_NAME}:${MINIO_CLI_PORT} ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}
  • DOMAIN_NAME:自サーバのFQDN
  • MINIO_CLI_PORT:MinIOのCLIのポート番号
  • MINIO_ROOT_USER:MinIOのAdmin名
  • MINIO_ROOT_PASSWORD:MinIOのAdminパスワード

その2、ユーザを作成する

ユーザの作成をします。

mc admin user add MINIO_ADDUSER ${USER_NAME} ${USER_PASSWORD}

その3、MinIOのポリシーを与える

MinIOのユーザに readwriteのポリシーを与えます。

mc admin policy attach MINIO_ADDUSER readwrite --user ${USER_NAME}

その4、MinIOのACCESS_KEYとSECRET_KEYを作成する

作成はJSONで与えるポリシーを作成します。

getObject.json
{
  "Id": "Policy1747103409271",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1747103407593",
      "Action": [
        "s3:*"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::real-data-lakehouse/*",
      "Principal": "*"
    }
  ]
}

${DURATION}の時間(分)でユーザに与えてJSONで作成したポリシーを与え、ACCESS_KEYSECRET_KEYを取得します。その時の情報をsedで切り取り、パスポートファイルに書きます。

mc admin accesskey create MINIO_ADDUSER/ ${USER_NAME} --expiry-duration ${DURATION}m --policy ./json/getObject.json --description "" --name "${USER_NAME}" 2>&1 |sed -e "s/^Access Key: \(.*\)$/export MINIO_ACCESS_KEY='\1'/g" -e "s/^Secret Key: \(.*\)$/export MINIO_SECRET_KEY='\1'/g" -e '/^Expiration:/d' -e '/^Name:/d' -e '/^Description:/d'>> ${PASSPORT_FILENAME} 

ここまでで情報は一通りできます。以下は情報の一例です。

$ cat UserInfo-20250802-183111-909782781-fq9d7ra733tjtt4chznr3vcanqpymyss.txt
export STORAGE_NAME='misumi.lan'
export POSTGRES_PORT='5432'
export POSTGRES_DBNAME='real-data-lakehouse-20250802'
export POSTGRES_USERNAME='fq9d7ra733tjtt4chznr3vcanqpymyss'
export POSTGRES_PASSWORD='<A:v,m(,H4x<@K3HPvRFgzL79]Pi3{*dAjg7t*z{b$M*pv<{-Xaw(Cj=MA~W3,}J'
export MINIO_CLI_PORT='9000'
export MINIO_BUCKET_NAME='real-data-lakehouse'
export MINIO_ACCESS_KEY='3VLUL0YO4L2S8MAGZOV4'
export MINIO_SECRET_KEY='B9ZJluEmj7FwkTPobK8eEu7wT4DGgRv6DQbIGvUl'

MinIOのテンポラリのAliasを削除する

mc alias remove MINIO_ADDUSER

PostgreSQLのユーザを作成する

PostgreSQLではexpectを使ってユーザを作ります。

./adduser.exp ${POSTGRES_USERNAME} ${POSTGRES_PASSWORD} ${USER_NAME} ${USER_PASSWORD}

このexpectファイルであるadduser.expを使ってユーザを作ります。

adduser.exp
#!/usr/bin/expect

set POSTGRES_USERNAME [lindex $argv 0]
set PW [lindex $argv 1]
set USERNAME [lindex $argv 2]
set USERPWD [lindex $argv 3]

set timeout -1

spawn docker compose exec -u postgres -it rdlh-pgsql psql -h rdlh-pgsql -U ${POSTGRES_USERNAME} -W -c "CREATE USER ${USERNAME} WITH ENCRYPTED PASSWORD \'${USERPWD}\'" -c "GRANT SELECT,UPDATE,INSERT ON TABLE iceberg_tables,iceberg_namespace_properties TO ${USERNAME}"

expect {
    -glob "Password:" {
        send "${PW}\n"
	exp_continue
    }
}

ユーザの削除を予約する

さいごにMinIOとPostgreSQLユーザを削除することを予約しておきます。

# atコマンドで実行するためのスクリプトを作ります。
AT_COMMAND_FILENAME="/tmp/${DATE}-${USER_NAME}.sh"
cat < ./removeuser_header.sh > ${AT_COMMAND_FILENAME}
cat << EOF >> ${AT_COMMAND_FILENAME}
USER_NAME=${USER_NAME}
SCRIPT_DIR=${SCRIPT_DIR}
EOF
cat < ./removeuser_footer.sh >> ${AT_COMMAND_FILENAME}

# 実行権限をつけます。
chmod 755 ${AT_COMMAND_FILENAME}
# 最後ににatコマンドで${DURATION}分後に発行されるようにします。
at now+${DURATION}minutes -f ${AT_COMMAND_FILENAME}

このときのremoveuser_header.shremoveuser_footer.shは以下の通りです。

removeuser_header.sh
#!/bin/bash
removeuser_footer.sh
cd ${SCRIPT_DIR}

. ../.env
# MinIOで削除するユーザ用のテンポラリのAliasを作成します。
if ! mc alias set MINIO_REMOVEUSER https://${DOMAIN_NAME}:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};then
	echo "Alias set error!"
	exit 254;
fi

# MinIOユーザを削除します
if ! mc admin user rm MINIO_REMOVEUSER ${USER_NAME}; then
	echo "User remove error!"
	exit 240;
fi
# MinIOで削除するユーザ用のテンポラリのAliasを削除します。
if ! mc alias remove MINIO_REMOVEUSER;then
	echo "Alias remove error!"
	exit 253;
fi

cd ${SCRIPT_DIR}/../PostgreSQL/bin/;
# PostgreSQLユーザを削除するExpectファイルを呼びます。
./dropuser.exp ${POSTGRES_USERNAME} ${POSTGRES_PASSWORD} ${USER_NAME}

このときのdropuser.expは以下の通りです。

dropuser.exp
#!/usr/bin/expect

set U [lindex $argv 0]
set PW [lindex $argv 1]
set USERNAME [lindex $argv 2]

set timeout -1

spawn docker compose exec -u postgres -it rdlh-pgsql psql -h rdlh-pgsql -U ${U} -W -c "REVOKE SELECT,UPDATE,INSERT ON iceberg_tables, iceberg_namespace_properties FROM ${USERNAME}" -c "DROP USER ${USERNAME}" 

expect {
    -glob "Password:" {
        send "${PW}\n"
	exp_continue
    }
}

これで

$ ./adduser.sh 16

のようにするとユーザを作成し、16分後に削除される様になります。

おわりに

これの何がうれしいと言いますと、以下の事が言えます。

  • お試しアカウントが配布しやすい。
  • ユーザを時限的に作成できる。

これで自動的にユーザの作成と削除を実現できます。

Discussion