Blawxの認証認可強化PoC
実稼働までに検討したいこと
セキュリティや可用性等考慮すべきことがある
これまでのヤドカリくんの技術スタックと異なり「データを変更可能/保存が必要」
仕様が固まったら
セキュリティ
特定の(管理者が許可した)人のみアカウントを作成できる
現状は誰でも作れるため、悪意ある人がリソースを占有することも可能
クラウド代高額請求リスクも
最初はproj-inclusiveとの関連団体にアカウントを払い出す形式にしたい
↑誰でも作れる方がシビックテック的だが、リソースや安定稼働のためにまずは制限したい
...と思ったが、他人のパスワードを管理するのはセキュリティリスクなので避けたい
アカウント作成画面は制限するが、普通のAPIは誰でも叩けるようにする
でないとヤドカリくんで使えないため
そもそもAPIは明示的に公開しないと叩けないので、アカウント作成を制限する必要はないかも
管理者権限を安全に管理できる
ブルートフォースアタック等にさらされないか要確認
The Blawx Administration Interface is available by navigating to /admin on your Blawx Server.
The admin interface can be used to view and make changes to the projects owned by anyone on the server, and to add, change, and delete user accounts, and change what permissions are granted to what user accounts.
Dockerfileでパスワードを変えられる
ビルド時に作成されるため、ビルド→再デプロイの流れになりそう
ビルドジョブのクレデンシャルとして扱えばよい
緊急時であれば先に手動でadminを手でパスワード変更して、あとで設定値を変えることになる
変更できた
$ docker build . -t blawx --build-arg SU_PASSWORD=secretpass --build-arg ADMIN_USERNAME=admin2
実際にはCI等でマスクされたクレデンシャルとして渡す
セキュリティ対策の別案
RaC作成者が作ったものが即時反映されなくてもよい
公開されているものだけが更新されればよい
RaC作成者が作れる環境は特定の人だけに公開
dev環境的な扱い
本番環境はOSS公開されたものをimportして公開
readonlyのDBを作成
中を見られたとしてもDBは物理的に書き換え不可
(もちろんセキュリティ対策は最低限行うが)
RaCの性質上即時性は不要だし、利用者もAPIでGETしかしない
※リードレプリカは使えない
レプリカだとパスワード情報も含むため
✅アカウントからログアウトできるようにする
ログアウトできない不具合?
コンテナ側ログ
code:_
Method Not Allowed (GET): /accounts/logout
Django4.1でGETメソッドによるログアウトが不可となったため
フォームでPOSTメソッドを送るようにすれば解決した
ユーザー情報を盗み見られない
どこに保存されるのか確認
DBを使う場合は、暗号化されているかどうか確認
平文にはなっていなかった(下記)
Djangoの機能でAPI等からクエリが発行できてしまうかどうかも見る
blawxの性質上、アカウントさえ守れればひとまずはok
ルールのデータはむしろOSS/オープンデータなので
✅可用性
✅データを永続化でき、作ったルールが消えないようにできる
現状だとコンテナが時間経過で消える→DBごと揮発する
DBは別建てにして永続化できるようにする
どれだけの領域を使用するか、GCPだと価格がどれくらいになるか要確認
最安値
Iowa, Enterprise, 1インスタンス, 1vCPU, メモリ3.75GiB, ストレージ10GiB, HDD db-lightweight-1 (本番利用の推奨の最小)
$50.21/ month (無理)
Iowa, Enterprise, 1インスタンス, 1vCPU, メモリ3.75GiB, ストレージ10GiB, HDD db-f1-micro (本番非推奨の最小構成)
$8.57/ month
15,000円/年 それでも辛い...
まともに動くのか要確認
ユースケース上、中身を定期的に吸い出しリポジトリにバックアップという手も取れそう
管理者API等で取得できるか確認
編集はできてはマズいが、参照は誰でもできるとよい
OSSコミュニティ的な意味でも、できたruleのyamlは公開する方針にしたい
ユーザー名が必要なので一括で吸い出すのは難しい
ユーザーをすべて管理者が払い出す形式なら扱えるが...
↑アカウントのバックアップはとれないため、DBはやはり必要?
DBから吸い出す経路も用意しておく
デフォルトで使用可能なsqliteの中身を見る
code:bash
$ sqlite3 db.sqlite3
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .table
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups django_site
auth_user_user_permissions guardian_groupobjectpermission
blawx_blawxtest guardian_userobjectpermission
blawx_docpage preferences_blawxpreference
blawx_query preferences_preferences
blawx_ruledoc preferences_preferences_sites
blawx_workspace
# ユーザー名は平文
sqlite> select * from auth_user;
1|{暗号化されたパスワード?}||0|AnonymousUser|||0|1|2025-04-07 14:08:01.367914|
2|{...}||1|admin||admin@admin.com|1|1|2025-04-07 14:08:02.864226|
3|{...}|2025-05-31 04:44:00.624629|0|syuparn|||0|1|2025-05-31 04:44:00.347142|
# ルールとブロックはここに格納されている
sqlite> select * from blawx_ruledoc;
sqlite> select * from blawx_workspace;
SQLiteのファイルをCloud Storageに入れればもっと安くなる?
すでに実践している例があった
1月3円(!)
(SQLiteはデフォルトだとスケールアウトしづらい)
sqliteファイルを定期的にコピーすることでバックアップも取れる?
指定するには、settings.py の以下を環境変数指定 or cloud storageの内容をボリュームマウント?(できるか不明)
cloud storage、Django, Dockerの組み合わせについてもう少し知見が必要そう
code:py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
初期化したsqlite3ファイルをマウントしcloud storageへ永続化
そのままディレクトリをマウントすると上書きされて消えてしまうため、以下の手順で保持
ビルド時)sqlite3ファイル作成、初期化
ビルド時)別ディレクトリに退避
実行時)ディレクトリをマウント
実行時)sqlite3ファイルをマウントしたディレクトリに移動後サーバー起動
code:dockerfile
# HACK: move db temporalily and then move it back in execution time so that the initialized db can be mounted
RUN mv /app/blawx/sqlite/db.sqlite3 /tmp
code:bash
$ docker run -it -p 8000:8000 -v .:/app/blawx/sqlite blawx &
# DBファイルがマウントされている
$ ls
db.sqlite3
✅無限ループをタイムアウトできる
サーバー側でs(CASP)を実行しているため、無限ループが発生するとサーバーに負荷が発生
負荷が溜まり続けると最悪コンテナがクラッシュしてサービスダウンする
→処理をどこかで打ち切りたい
無限ループの例
Prologは以下のような形式のルールだと無限ループしてしまい答えが出せない
子孫(A,B):- 子孫(A,C),子孫(C,B).
https://scrapbox.io/files/68443444b9c266134e1f3c2b.png
テスト
https://scrapbox.io/files/68443452f7226bcde8d20e60.png
結果
1~2分で以下のエラーが出た
There was an error while running the code.
https://scrapbox.io/files/684434276a639c6efdf13f60.png
投げられているのは POST http://localhost:8000/syuparn/loop/test/test1/run/
サーバー側にリクエストごとにプロセス(swipl)が作られ負荷が上がっている
code:_
top - 21:51:07 up 9 days, 18:24, 0 users, load average: 3.77, 1.30, 0.56
Tasks: 117 total, 1 running, 115 sleeping, 1 stopped, 0 zombie
%Cpu(s): 36.6 us, 1.1 sy, 0.0 ni, 60.9 id, 0.0 wa, 0.0 hi, 1.4 si, 0.0 st
MiB Mem : 15700.2 total, 9683.3 free, 5078.7 used, 938.2 buff/cache
MiB Swap: 4096.0 total, 4096.0 free, 0.0 used. 10385.0 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15489 root 20 0 642188 477264 6144 S 100.7 3.0 1:23.24 swipl
15526 root 20 0 642184 467000 6144 S 100.3 2.9 1:21.37 swipl
15987 root 20 0 217216 109644 6144 S 100.3 0.7 0:22.12 swipl
15387 root 20 0 658568 599132 6144 S 100.0 3.7 1:38.03 swipl
15958 root 20 0 217220 113036 6016 S 100.0 0.7 0:23.41 swipl
15476 root 20 0 642188 492604 6144 S 99.7 3.1 1:25.22 swipl
ブラウザ側でタイムアウトして上記のエラーが出ていそう
サーバー側のプロセスも時間経過で消える?
実行箇所
タイムアウト15sを入れた結果
top上でswiplのプロセスが15sで終了
同時に、フロントエンド側でも以下のレスポンスを受け取る
code:json
{
"error": "There was an error while running the code.",
"transcript": "time_limit_exceeded"
}
✅APIリクエストでユーザー指定の値を使用できる
よく考えたらこれがないと実用化できない
webAPIとして使用する必須条件
金額等をユーザーがAPIリクエストで指定
認証無しで参照可能
認証必須でもサーバーサイドでBFFを使えばエンドユーザーは認証不要になるが、構成が複雑化
認証は必須だった
code:bash
$ curl -i -d @body.json -H "Content-Type: application/json" localhost:8000/syuparn/rock-paper-scissors-act/test/bobjane/run/
HTTP/1.1 100 Continue
HTTP/1.1 403 Forbidden
...
Rule Editorで Publish を選択すれば、公開されてログインせずともリクエスト可能になった
https://scrapbox.io/files/6885b64808cfd7ca5e6e9c62.png
code:bash
$ curl -i -d @body.json -H "Content-Type: application/json" localhost:8000/syuparn/rock-paper-scissors-act/test/bobjane/run/
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
...
code:bash
// 結果を取り出す
$ curl -d @body.json -H "Content-Type: application/json" localhost:8000/syuparn/rock-paper-scissors-act/test/bobjane/run/ | jq '.Answers0.Variables.Winner' "jane"
サンプルのじゃんけんでも計算に2秒くらいかかる
たくさんリクエストが来るとさばけない?
サイズも大きいので、その対策のためにBFFにしてもよいかも
JSONで45KBある
リクエストのfacts で指定できるのでパラメータ変更可能
code:json
{
"from_ontology": true,
"type": "true",
"relationship": "throw",
"parameter1": "jane",
"parameter2": "rock",
"parameter3": "testgame"
},
書き換え("from_ontology": false にすること)
A 'from_ontology' key, the value of which is a true or false value. This is used to note which facts were already present in the code and were not provided by the user. Data provided by a user application should always set the 'from_ontology' value to false.
上書きすると紛らわしいので、ブロックでは未定義の変数を使ったほうが良さそう
APIエンドポイント用の空のテストを作る?
code:json
{
"from_ontology": false,
"type": "true",
"relationship": "throw",
"parameter1": "jane",
"parameter2": "paper",
"parameter3": "testgame"
},
code:bash
$ curl -d @body.json -H "Content-Type: application/json" localhost:8000/syuparn/rock-paper-scissors-act/test/bobjane/run/ | jq '.Answers0.Variables.Winner' "bob"
from_ontology: trueの内容は省略可能
code:bash
$ curl -d '{"facts":[]}' -H "Content-Type: application/json" localhost:8000/syuparn/rock-paper-scissors-act/test/bobjane/run/ | jq '.Answers0.Variables.Winner' "jane"
APIの高速化
(※難しいので優先度低)
レスポンスが返るまでに2~3秒かかる
(ローカルPCのdockerコンテナ(メモリ、CPU制限なし)でこの速度なのでデプロイしたらもっと遅くなる?)
利用者が増えたときにボトルネックになりそう
高速化の余地はある?
どこがボトルネックか要確認
scasp
prologインタプリタ
blawxのterm
pythonインタプリタ
✅Cloud Runにデプロイする
code:bash
# ローカル
$ docker build . -t blawx
$ docker tag blawx us-west1-docker.pkg.dev/${projectname}/docker-repo/blawx:latest
$ docker push us-west1-docker.pkg.dev/${projectname}/docker-repo/blawx:latest
https://scrapbox.io/files/68a46a573e48cda797d2eafa.png
https://scrapbox.io/files/68a46aa8660fc69f10e9773f.png
https://scrapbox.io/files/68a46ab6964ccb79641fb1c4.png
Cloud StorageのバケットはCloud Storage FUSEのファイルシステムとしてマウントされる
ログにも出ている 2025-08-19 21:14:55.977 JST GCSFuse is mounted with bucket blawx_db. Enable GCSFuse logging with mount options for more logs
中のデータを確認
code:bash
# ローカル
$ gcloud storage ls gs://blawx_db
gs://blawx_db/db.sqlite3
$ gcloud storage cp gs://blawx_db/* .
Copying gs://blawx_db/db.sqlite3 to file://./db.sqlite3
Completed files 1/1 | 624.0kiB/624.0kiB
Average throughput: 9.9MiB/s
# リモートのDBがダウンロードできている
$ sqlite3 db.sqlite3
sqlite> select username from auth_user;
AnonymousUser
admin
syuparn
ルール作成時のロードは数十秒かかる
cloud storageは本来DBを動かす永続領域ではないのでやむなし...
レスポンスも少し重い
code:bash
$ curl -d @body.json -H "Content-Type: application/json" ${blawx_url}/syuparn/rock-paper-scissors-act/test/bobjane/run/ | jq '.Answers0.Variables.Winner' % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 46395 100 45272 100 1123 10162 252 0:00:04 0:00:04 --:--:-- 10414
"jane"