MinIOのユーザ作成をやってみた
MinIOのユーザを作る
MinIOはS3互換のオブジェクトストレージです。今回はそのMinIOでユーザを作ってみたり試行錯誤してみたりしたので、その記録をします。
MinIOをたてる
これは以下の場所等でも書きましたが、もう一度書いておきます。
services:
nginx:
container_name: nginx
hostname: nginx
image: nginx:stable-alpine3.19-perl
volumes:
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- ./certs/evakichi.example.com.crt:/opt/certs/evakichi.example.com.crt:ro
- ./certs/evakichi.example.com.key:/opt/certs/evakichi.example.com.key:ro
ports:
- 80:80
- 443:443
- 9000:9000
environment:
- TZ=Asia/Tokyo
networks:
- minio-network
s3:
container_name: s3
hostname: s3
image: minio/minio:RELEASE.2024-05-10T01-41-38Z
environment:
- MINIO_ROOT_USER=${MINIO_ROOT_USER}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
- TZ=Asia/Tokyo
volumes:
- minio-volume:/data/
- ./certs/evakichi.example.com.ca.crt:/root/.minio/certs/CAs/evakichi.example.com.ca.crt
- ./docker-entrypoint.sh:/usr/bin/docker-entrypoint.sh
tty: true
networks:
- minio-network
command: ["server", "/data", "--console-address", ":9001","--certs-dir","/root/.minio/certs/"]
restart: always
volumes:
minio-volume:
networks:
minio-network:
この時の環境変数は以下の通りです。
MINIO_ROOT_USER=admin
MINIO_ROOT_PASSWORD=Adminのパスワード
あとはdocker-entrypoint.sh
の初めのほうに以下の細工をします。
cat /root/.minio/certs/CAs/* >> /etc/ssl/certs/ca-certificates.crt
最後に、nginx.confを書きます。
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name evakichi.example.com;
return 301 https://$http_host$request_uri;
}
server {
listen 9000 ssl;
ssl_protocols TLSv1.3;
ssl_certificate /opt/certs/evakichi.example.com.crt;
ssl_certificate_key /opt/certs/evakichi.example.com.key;
server_name evakichi.example.com;
location /{
proxy_pass http://s3:9000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
#proxy_set_header Host $host;
#proxy_set_header X-Forwarded-for $remote_addr;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name evakichi.example.com;
ssl_protocols TLSv1.3;
ssl_certificate /opt/certs/evakichi.example.com.crt;
ssl_certificate_key /opt/certs/evakichi.example.com.key;
# Allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# Disable buffering
proxy_buffering off;
proxy_request_buffering off;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
proxy_pass http://s3:9001; # This uses the upstream directive definition to load balance
}
location /minio/ui/ {
rewrite ^/minio/ui/(.*) /$1 break;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
# This is necessary to pass the correct IP to be hashed
real_ip_header X-Real-IP;
proxy_connect_timeout 300;
# To support websockets in MinIO versions released after January 2023
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Some environments may encounter CORS errors (Kubernetes + Nginx Ingress)
# Uncomment the following line to set the Origin request to an empty string
# proxy_set_header Origin '';
chunked_transfer_encoding off;
proxy_pass http://s3:9001; # This uses the upstream directive definition to load balance
}
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
こうすれば、MinIOがオレオレ証明書でHTTPS接続できます。
これでMinIOの準備はできました。実行しておきます。
$ docker compose up -d
MinIOが立ち上がったのでMinIOへのユーザ登録をしていく方法を考えます。
MinIOへのユーザ登録をするDockerを立てる
MinIOのDockerへ接続するのは別にDockerである必要はありませんがなるべくどこでも動かせるようにDockerで作成することとしました。
Dockerfileの準備
MinIOを操作するためにはWebUIから行う方法とCUIから行う方法がありますが、今回はできるだけCUIを使うこととしました。ですのでDockerfileにmc等に必要なものを記載していきます。
FROM debian:bookworm
USER root
RUN apt-get update && apt-get install -y ca-certificates
COPY ./evakichi.example.com.ca.crt /certs/evakichi.example.com.ca.crt
RUN cd /usr/share/ca-certificates && mkdir mylocal && cp -p /etc/ca-certificates.conf /etc/ca-certificates.conf.bak
RUN cd /usr/share/ca-certificates && cp /certs/evakichi.example.com.ca.crt mylocal/
RUN echo "mylocal/evakichi.example.com.ca.crt" >> /etc/ca-certificates.conf
RUN update-ca-certificates
RUN apt-get update && apt-get -y upgrade && apt-get -y install gnupg uuid-runtime pwgen bsd-mailx ssmtp sharutils
RUN curl https://dl.min.io/client/mc/release/linux-amd64/mc --create-dirs -o /root/minio-binaries/mc && chmod +x /root/minio-binaries/mc
ENV PATH=$PATH:/root/minio-binaries/:/root/bin
WORKDIR /root/
CMD ["bash"]
services:
mc:
container_name: mc
hostname: mc
build: .
tty: true
volumes:
- ./bin:/root/bin/:ro
- ./keys:/keys
- ./.mailrc:/root/.mailrc
- ./ssmtp.conf:/etc/ssmtp/ssmtp.conf
- gnupg-volume:/root/.gnupg/
- userinfo-volume:/userinfo/:rw
environment:
- TZ=Asia/Tokyo
- MINIO_ACCESS_KEY_ID=${MINIO_ACCESS_KEY_ID}
- MINIO_SECRET_ACCESS_KEY=${MINIO_SECRET_ACCESS_KEY}
- MINIO_HOST=${MINIO_HOST}
- MINIO_PORT=${MINIO_PORT}
- GPG_SECRET_KEY_ID=${GPG_SECRET_KEY_ID}
restart: always
volumes:
gnupg-volume:
userinfo-volume:
工夫その1:メールによるユーザ情報の自動送信
私が考えているシステムではユーザがたくさんいてその管理が面倒になってくることを想定しています。なので、ユーザの作成をしたらその情報をメールで飛ばすようにしています。
工夫その2:GnuPGによるアカウント情報の暗号化
そのままユーザの情報を流してしまうとセキュアで無いかもしれないと思い、私の考えているシステムではGnuPGによる暗号化をかけることをデフォルトとしました。
工夫を実現するために行ったこと
まずは、システムからメールを送れるようにしないといけません、ですのでます思いついたのはmailx
コマンドで送りつけることでした。ですので
set smtp=smtp://example.com:465
set smtp-auth-user=testuser@example.com
set smtp-auth-password=passwd
set from=testuser@exmple.com
のように設定して送ろうとしました。しかしmailq
でみたらFrozen状態になってメールが送られなかったのです。そこでいろいろ調べたらssmtp
というパッケージにたどり着きました。
こんな感じにメールの設定をして送ることを実現しています。
# The user that gets all the mails (UID < 1000, usually the admin)
root=testuer@example.com
# The mail server (where the mail is sent to), both port 465 or 587 should be acceptable
# See also https://support.google.com/mail/answer/78799
mailhub=exmaple.com:465
# The address where the mail appears to come from for user authentication.
rewriteDomain=example.com
# The full hostname. Must be correctly formed, fully qualified domain name or GMail will reject connection.
hostname=HOSTNAME
# Use SSL/TLS before starting negotiation
UseTLS=Yes
# Username/Password
AuthUser=testuser@example.com
AuthPass=passwd
AuthMethod=LOGIN
# Email 'From header's can override the default domain?
FromLineOverride=Yes
これでメールが送られることが確認できたのであとはメールにGnuPGで作成したデータを送りつけるだけです。単に送りつけるよりも添付ファイルで送った方がより親切かと思いsharutilsをいれて実験してみたのですが、わたしのThunderbirdでは文字エンコードがおかしくなってしまっていたので結局メール本文に貼り付けることとしました。
こんな方法でDockerfileと設定ファイルを用意したので、Dockerを実行します。
$ docker compose up -d --build
これでだいたいのことはOKになりました。
ユーザ追加の方法
いよいよ私がどうやってユーザを追加したかを記載していきます。この方法が合っているかは定かではありませんが、こんな方法を採りました。
#!/bin/bash
USERNAME=u$(uuidgen|cut -c1-8,10-13,15-18,20-22)
if [ ! $? -eq 0 ]; then
exit $?;
fi
DATETIME=$(date '+%Y%m%d-%H%M%S-%N')
if [ ! -d /userinfo/ ]; then
echo 'userinfo directory do not exist.' >&2
exit -1
fi
FILENAME="/userinfo/user-${DATETIME}-${USERNAME}.info"
PASSWORD=$(pwgen -1 -c -B -y -n -r "&;:><*\"\'\` \\" 32
)
if [ ! $? -eq 0 ]; then
exit $?;
fi
if [ -z "${1}" ]; then
echo "No User ID." >&2
exit -1
fi
TEMP=$(mktemp)
export GPG_TTY=$(tty)
touch ${TEMP}; LANG=C gpg -r ${GPG_SECRET_KEY_ID} -u ${GPG_SECRET_KEY_ID} -se ${TEMP}; if [ $? -eq 0 ]; then rm ${TEMP}.gpg; else echo 'Fail'; rm ${TEMP}; exit -1; fi; rm ${TEMP}
KEY_ID=${1}
gpg --list-keys ${KEY_ID}
if [ $? -ne 0 ]; then
echo "No USER in Keyring." >&2
rm ${FILENAME}
exit -1
fi
echo "User Name: ${USERNAME}" | tee ${FILENAME}
echo "Password: ${PASSWORD}" |tee -a ${FILENAME}
mc alias set USER_CREATION ${MINIO_HOST}:${MINIO_PORT} ${MINIO_ACCESS_KEY_ID} ${MINIO_SECRET_ACCESS_KEY}
mc admin user add USER_CREATION ${USERNAME} ${PASSWORD}
mc admin user svcacct add USER_CREATION --expiry 2025-01-01T00:00:00Z ${USERNAME} |tee -a ${FILENAME}
mc alias remove USER_CREATION
gpg -sea -r ${KEY_ID} ${FILENAME}
if [ $? -ne 0 ]; then
echo "Cannot encrypt user file." >&2
rm ${FILENAME}
exit -1
fi
gpg -se -r ${GPG_SECRET_KEY_ID} ${FILENAME}
if [ $? -ne 0 ]; then
echo "Cannot encrypt own file." >&2
rm ${FILENAME}
exit -1
fi
MESSAGE="""
Hello ${KEY_ID}!!\n\n
This is Your Account.\n
Please copy following PGP message and decode it.\n\n
Best regards,\n
evakichi\n
"""
(echo -e ${MESSAGE} ; cat < ${FILENAME}.asc) | mailx -s 'Your account.' ${KEY_ID}
rm ${FILENAME}
echo "Done."
ひとつひとつ説明していきます。
USERNAME=u$(uuidgen|cut -c1-8,10-13,15-18,20-22)
これはUUIDを発行してその中からハイフンを抜いた先頭19文字を取り出してその先頭にuをつけてユーザ名としています。
DATETIME=$(date '+%Y%m%d-%H%M%S-%N')
細かい時間まで取っておいています。これはファイル名の一部になります。
FILENAME="/userinfo/user-${DATETIME}-${USERNAME}.info"
ファイルの保存場所を日時+ユーザ名としています。
PASSWORD=$(pwgen -1 -c -B -y -n -r "&;:><*\"\'\` \\" 32
パスワードを作成していますが、コマンドラインで扱いやすいように特定の記号は入らないようにしています。
TEMP=$(mktemp)
export GPG_TTY=$(tty)
touch ${TEMP}; LANG=C gpg -r ${GPG_SECRET_KEY_ID} -u ${GPG_SECRET_KEY_ID} -se ${TEMP}; if [ $? -eq 0 ]; then rm ${TEMP}.gpg; else echo 'Fail'; rm ${TEMP}; exit -1; fi; rm ${TEMP}
ここはちょっとした遊びです。Tempファイルを作成して送信元のユーザの秘密鍵がきちんと機能するようにしているものです。パスフレーズをあらかじめ入れておいてそのあとの署名や暗号化のときにパスフレーズを入れるのを失敗してユーザの情報ファイルができないなんてことを防ぐためにやっています。
KEY_ID=${1}
gpg --list-keys ${KEY_ID}
if [ $? -ne 0 ]; then
echo "No USER in Keyring." >&2
rm ${FILENAME}
exit -1
fi
echo "User Name: ${USERNAME}" | tee ${FILENAME}
echo "Password: ${PASSWORD}" |tee -a ${FILENAME}
いよいよユーザ情報ファイルを作成します。まずは、コマンドラインの第一引数に与えられたユーザ名(メールアドレス)が鍵束にあるか確認し、そのあとユーザ名とパスワードを先ほどのファイルに格納します。
mc alias set USER_CREATION ${MINIO_HOST}:${MINIO_PORT} ${MINIO_ACCESS_KEY_ID} ${MINIO_SECRET_ACCESS_KEY}
mc admin user add USER_CREATION ${USERNAME} ${PASSWORD}
mc admin user svcacct add USER_CREATION --expiry 2025-01-01T00:00:00Z ${USERNAME} |tee -a ${FILENAME}
mc alias remove USER_CREATION
ここで実際のMinIOへのユーザの登録を行っています。1行目はエイリアスを作成し、2行目で実際のユーザ登録をおこない、3行目でポリシーを与え、4行目でそのユーザの有効期限を決めつつ出力からえられるクレデンシャルをユーザ情報ファイルに記載しています。
最後にこの登録に使ったエイリアスを削除します。
gpg -sea -r ${KEY_ID} ${FILENAME}
if [ $? -ne 0 ]; then
echo "Cannot encrypt user file." >&2
rm ${FILENAME}
exit -1
fi
gpg -se -r ${GPG_SECRET_KEY_ID} ${FILENAME}
if [ $? -ne 0 ]; then
echo "Cannot encrypt own file." >&2
rm ${FILENAME}
exit -1
fi
次の場所ではできあがったユーザファイルを暗号化して署名します。一応念のために自分の手元用に自分の公開鍵で暗号化して署名したバイナリファイルも保存しておきます。
MESSAGE="""
Hello ${KEY_ID}!!\n\n
This is Your Account.\n
Please copy following PGP message and decode it.\n\n
Best regards,\n
evakichi\n
"""
(echo -e ${MESSAGE} ; cat < ${FILENAME}.asc) | mailx -s 'Your account.' ${KEY_ID}
rm ${FILENAME}
echo "Done."
さいごにファイルの中身を貼り付けてメールを送信しておわりです。
実際に実行して見ましょう。
$ docker compose exec -it mc createuser.sh testuser@example.com
-略-
User Name: uc4cad01d61154158acb
Password: Uhmb|]Unzwj$/n,+CMax9_J7d!gN-mN7
Added `USER_CREATION` successfully.
Added user `uc4cad01d61154158acb` successfully.
Access Key: 7S7P10GD71GCV60YPSI8
Secret Key: QBNjat+UXVQ6JN43cTKChxu1lukrt8SGVZjyWgF6
Expiration: 2025-01-01 00:00:00 +0000 UTC
Removed `USER_CREATION` successfully.
これで、メールが
This is Your Account.
Please copy following PGP message and decode it.
Best regards,
evakichi
-----BEGIN PGP MESSAGE-----
hQGMAxkM1tMzX9JJAQv+K2HWFPebG7n3XwFoEt5EZtFoHAlGppWyqo1CaYnW1Zcc
-略-
DSWTihR0HvCXzAO1jF51yTs4KvOMOhB2Bn971zge0OncN3UdcnmFGfmHWeeFiuaA
+Q==
=wRsH
-----END PGP MESSAGE-----
のように送られてくればOKです。
ユーザがきちんと作られているかも確認します。
OKっぽそうです。
おわりに
これでMinIOのユーザ登録までできました。あとはポリシーの設定等々をすれば良いわけです。
Discussion