🏊

MinIOのユーザ作成をやってみた

2024/09/14に公開

MinIOのユーザを作る

MinIOはS3互換のオブジェクトストレージです。今回はそのMinIOでユーザを作ってみたり試行錯誤してみたりしたので、その記録をします。

MinIOをたてる

これは以下の場所等でも書きましたが、もう一度書いておきます。

https://zenn.dev/evakichi/articles/7c1beafb5bdb96

https://zenn.dev/evakichi/articles/cfe0dea7029514

compose.yml
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:

この時の環境変数は以下の通りです。

.env
MINIO_ROOT_USER=admin
MINIO_ROOT_PASSWORD=Adminのパスワード

あとはdocker-entrypoint.shの初めのほうに以下の細工をします。

docker-entrypoint.sh
cat  /root/.minio/certs/CAs/* >> /etc/ssl/certs/ca-certificates.crt

最後に、nginx.confを書きます。

./conf/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等に必要なものを記載していきます。

Dockerfile
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"]
compose.yml
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コマンドで送りつけることでした。ですので

.mailrc
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になりました。

ユーザ追加の方法

いよいよ私がどうやってユーザを追加したかを記載していきます。この方法が合っているかは定かではありませんが、こんな方法を採りました。

createuser.sh
#!/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