1.はじめに
ID、パスワードだけでの認証では心許ないので、わんこ監視システム(nginx+Mjpg-streamer)からのストリーミング参照できるデバイスを限定する目的でJWT認証も採用したのですが、各デバイス上でトークンを定期的に更新するのが少々面倒になりました。
それで、安直に多要素認証にすれば、堅牢性と運用上の利便性が両立可能かなと考えました。
今にして思えば、JWT認証は実装で結構苦戦したので、最初からMFAにしておけばよかっただけかと思います。
2.認証用に何を使用する?
この辺の知見ないので、Google AIモード(以降、AIと略す)で今のシステムに合うものを聞いてみたら、リスト上の一番上に(イチオシとして)autheliaを紹介されたので、比較検討することもなくauthelia導入に決定しました。
3.導入方法は?
これまたAIに聞いてみたら、以下を答えてくれたので、AI畜よろしくただ従うのみと。
なお、以降の記載は、まだ家内LANで使用可能な範囲の実装です。
屋外からインターネットを介して利用するにはnginxの設定が不足しています。
また、誤りがあったら申し訳ありません。自己責任でお願いします。
(1)Docker環境の準備: ラズパイにDockerとDocker Composeをインストールします。
(2)Autheliaの配置: Docker ComposeでAutheliaコンテナを立ち上げ、ユーザー情報(ID/パスワード)をYAMLやデータベース(SQLite/MariaDB等)で管理するよう設定します。
(3)リバースプロキシの設定: Nginx等の設定ファイルで、特定のエンドポイントへのアクセスをAutheliaの認証ポータルへリダイレクトするように書き換えます。
(a)Nginxの各locationブロックにauth_request /authelia;を追加します。
(b)未認証の場合はAutheliaのログインページ(MFAを含む)へリダイレクトさせます。
(c)認証の委譲: 認証自体はNginx/Autheliaで完了しているため、Mjpg-streamer等でJWTを検証する必要はなくなります。
(b)ユーザー情報の取得: Autheliaが認証成功後に、Remote-UserやRemote-GroupsといったHTTPヘッダーにユーザー情報を載せてMjpg-streamer等に転送するようにNginxを設定します。
(4)MFAの有効化: Autheliaのポータル上でTOTP(QRコードスキャン)を設定し、ログイン時に2要素目を要求するようにポリシーを設定します。
3.1 DockerとDocker-Composeの導入
# Dockerのインストールスクリプトを実行
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 現在のユーザーをdockerグループに追加(次回ログインからsudo不要に)
sudo usermod -aG docker $USER
# Docker Compose プラグインのインストール
sudo apt-get update
sudo apt-get install -y docker-compose-plugin
3.2 Autheliaの導入
Autheliaの動作には主に3つのファイルが必要となる。
最初に設定ファイルを管理するためのフォルダを作成する。
mkdir -p ~/authelia/config
cd ~/authelia
/home/(ユーザー名)/
└── authelia/
├── docker-compose.yml
└── config/
├── configuration.yml
└── users_database.yml
① docker-compose.yml
services:
authelia:
image: authelia/authelia:latest
container_name: authelia
volumes:
- ./config:/config
networks:
- auth-net
# expose:
# - 9091
ports:
- "9091:9091"
restart: unless-stopped
environment:
- TZ=Asia/Tokyo
networks:
auth-net:
external: true # 事前に docker network create auth-net が必要
② config/configuration.yml (基本設定)
# ランダム文字列の生成
LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 64 ; echo
上記コマンドを3回実行し、各々の出力内容を、以下の"適当な長い文字列#1〜#3"に設定する。
server:
address: 'tcp://:9091/'
jwt_secret: '適当な長い文字列#1'
default_2fa_method: 'totp'
authentication_backend:
password_reset:
disable: true # ラズパイ環境なら最初は無効が楽です
file:
path: /config/users_database.yml
session:
name: 'authelia_session'
secret: '適当な長い文字列#2'
# default_cookie_attributes:
# insecure: true # HTTPでのCookie送信をブラウザに許可させる
# same_site: 'lax' # IPアドレス直叩き時のCookie動作を安定させる
cookies:
- domain: '192.168.x.xx'
authelia_url: 'https://192.168.x.xx'
storage:
local:
path: /config/db.sqlite3
encryption_key: '適当な長い文字列#3'
notifier:
filesystem:
filename: /config/notification.txt # 2FA登録用URLがここに書き込まれます
access_control:
default_policy: 'two_factor' # 全てにMFAを強制
③ config/users_database.yml (ユーザー定義)
# パスワードハッシュ生成コマンド
docker run --rm authelia/authelia:latest authelia hash-password "your_password"
上記コマンドで生成したパスワードを下記password:欄に貼り付ける。
users:
your_username:
displayname: "User Name"
password: "$argon2id$v=19$m=65536,t=3,p=4..."
email: user@example.com
groups:
- admins
4.Autheliaの起動
# ネットワークを作成して起動
docker network create auth-net #①
docker compose down #②
docker compose up -d
①を実行すると、長い16進文字列が出力されるが、Dockerが作成した「ネットワークID」 で、特に記録などは不要である(出力目的は各人で調べてほしい、もしかすると使用機会があるかも)。
②が必要かどうかは、よくわからない(なくてもよいかも)。
5.Nginxの導入
いろいろな方が導入記事を書かれているので、そちらを参照ください。
6.Nginxの設定
/etc/nginx/sites-enabled/xxx を修正するが、これはシンボリックリンクファイルで、実体は/etc/nginx/sites-available/xxx。
新規インストール直後でsites-enabled下に何もない状態の場合、先にsites-available下に実体ファイルを作成してから、sites-enabled下にシンボリックリンクを作成することになる。
なお、xxxに付与する名称は各人の目的に合わせて決めれば良い。
server {
listen 443 ssl;
server_name 192.168.x.xx
ssl_certificate /etc/nginx/ssl/lan.crt;
ssl_certificate_key /etc/nginx/ssl/lan.key;
# --- 0. Authelia 自体の設定 ---
# 混同を避けるため、/authelia/ というパスでログイン画面を動かします
# "location /"より上に置く必要あり
location /authelia/ {
auth_request off; # 明示的に認証チェックoff
proxy_pass http://127.0.0.1:9091/; # 末尾の / が重要
proxy_set_header Host $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;
}
# autheliaが内部で使う/api、/staticも認証除外にする
location /api/ {
auth_request off;
proxy_pass http://127.0.0.1:9091/api/;
proxy_set_header Host $host;
}
location /static/ {
auth_request off;
proxy_pass http://127.0.0.1:9091/static/;
proxy_set_header Host $host;
}
# --- 1. メインのアプリ(Mjpg-streamer)の設定 ---
location / {
# Autheliaに認証を確認しに行く
auth_request /internal/authelia/authz;
# 認証成功時、Autheliaからユーザー情報を受け取る
auth_request_set $user $upstream_http_remote_user;
proxy_set_header Remote-User $user;
# エラー(未認証)なら、Autheliaのログイン画面へ飛ばす
error_page 401 = @error401;
# 本来のmjpg-streamerへ転送
proxy_pass http://192.168.x.xx:80xx/;
# ストリーミングをスムーズにするための設定
proxy_buffering off;
proxy_request_buffering off;
# 接続を維持(Keep-alive)しやすく
proxy_http_version 1.1;
}
# --- 2. 認証チェック用(内部処理用) ---
location = /internal/authelia/authz {
internal;
proxy_pass http://127.0.0.1:9091/api/verify;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
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 Content-Length "";
proxy_pass_request_body off;
}
# --- 3. 未認証時にログイン画面へ飛ばす設定 ---
location @error401 {
# ログイン後、元のURLに戻れるように引数 (?rd=...) を付ける
return 302 https://192.168.x.xx/authelia/?rd=$scheme://$http_host$request_uri;
}
}
設定変更後、以下を実行して設定反映を。
sudo nginx -t
sudo systemctl restart nginx
7.デバイス登録を完了させる手順
7.1 nginxサーバーに接続
https://192.168.x.xx (httpsであることを確認)にアクセス。
警告画面が出たら「詳細設定」からアクセスを許可。
ID/パスワード入力しログインを試みると画面が遷移する。
7.2 登録用QRコードのスキャン
ラズパイのターミナルで cat ~/authelia/config/notification.txt を実行。
登録用URLが表示されるので、PCのブラウザのアドレスバーに貼り付けて開く。
ブラウザにQRコードの画面が表示される。
スマホの認証アプリ(Google Authenticatorなど)でQRコードをスキャン。
(このスキャンでサーバーで生成されたsecretがスマホに保存される)
スマホに表示された6桁の数字(TOTP)をブラウザに入力すると認証される。
