スマートフォン・タブレットからインターネットサーバーオペレーション

APPW.jp
 
appw.jp // incident report

移設後に踏んだ
権限トラブル二題

HOST
sasagawa.tokyo(新ConoHa VPS)
AFFECTED
RAGハイブリッド検索 / RabbitMQ管理プラグイン
CAUSE
ファイル権限・プラグイン有効化漏れ
STATUS: RESOLVED

ConoHa VPSの新環境へ一式を移した後、静かに動いていたはずのRAGハイブリッド検索が沈黙した。 ディレクトリごとコピーしたのに、なぜ。今回はその原因を追いかけて見つかった、 見た目は別々でも根っこが似ている2つのトラブルの記録。

01kb.json が403を返す

まず気づいたのは、ブラウザのコンソールに出た一行だった。

[search] kb.json 失敗: Unexpected token '<', "<script sr"... is not valid JSON [rag] ハイブリッド検索 無効 → サーバー検索(query)にフォールバック

JSONを期待したところにHTMLが返ってきている。まずは配信されているファイル自体を疑い、 静的スクリプト側から一つずつ確かめていった。

curl -I https://sasagawa.tokyo/cge-search.js → 200 curl -I https://sasagawa.tokyo/kb.json → 403

片方は通り、片方だけ403。Apacheのエラーログを見ると、原因は一目瞭然だった。

[core:error] (13)Permission denied: AH00132: file permissions deny server access: /home/login-user/www/sasagawa.tokyo/kb.json

kb.json の権限は所有者(u)のみrw、グループ・その他は権限なし——いわゆる600相当。 旧サーバーではApacheの実行ユーザーとファイルの所有者が同じだったため問題なく読めていたが、 新サーバーではApacheの実行ユーザーをデフォルトの www-data に変更したため、 同じ600のファイルが突然読めなくなっていた。

応急処置 chmod 644 kb.json で即座に復旧。curlも200へ。

本当の原因はもう一段深いところにあった

ここで一度は「umaskの設定差だろう」と考えたが、実際にkb.jsonを生成しているコードを見直すと、 そもそもumaskとは無関係に600が生まれる作りになっていた。

tmp_fd, tmp_path = tempfile.mkstemp(dir=os.path.dirname(kb_path), suffix=".tmp") ... os.replace(tmp_path, kb_path) # アトミック置換

tempfile.mkstemp() はセキュリティ仕様として、umaskの設定に関わらず常に0600でファイルを作る。 そのtmpファイルをそのままos.replace()で本番パスへ差し替えているだけなので、 サーバーのumaskをどう設定しても、実行ユーザーを誰にしても、生成されるkb.jsonは必ず600になる運命だった。 旧サーバーで動いていたのは「umaskが正しかったから」ではなく、 「たまたまApacheの実行ユーザーとファイル所有者が同じで600でも読めていたから」に過ぎなかった。

恒久対応 os.replace() の直後に os.chmod(kb_path, 0o644) を1行追加。 再生成のたびに600へ戻ってしまう問題を止めた。

02RabbitMQ管理プラグインが応答しない

kb.jsonの復旧作業の途中、別のログにも目が止まった。

キュー長取得失敗: Cannot connect to host localhost:15672 ssl:default [Connect call failed ('127.0.1.1', 15672)]

localhost が 127.0.0.1 ではなく 127.0.1.1 に解決されている。 /etc/hosts を確認すると、たしかに妙な状態だった。

127.0.0.1 localhost 127.0.1.1 localhost

本来は2行目が 127.0.1.1 <hostname> であるべきところ、両方とも localhost を指している。一見これが犯人に見えたが、結論を急がず先にプラグイン自体の状態を確認した。

sudo rabbitmq-plugins list | grep management [ ] rabbitmq_management 4.0.5 sudo ss -tlnp | grep 15672 (何も出ない)

プラグインは未有効化、ポート自体がLISTENしていなかった。 /etc/hosts の重複行は事実として気になる状態ではあるものの、今回の直接原因ではない。 設定ファイルはコピーで引き継げても、プラグインの有効化状態はRabbitMQ自身が持つデータディレクトリ側の状態で、 新規インストールでは引き継がれていなかった。

対応 sudo rabbitmq-plugins enable rabbitmq_management で解決。再確認すると 0.0.0.0:15672 でLISTEN——ついでにufwで外部からブロックされていることも確認し、 将来の保険として rabbitmq.conf に management.tcp.ip = 127.0.0.1 を足す方針にした。

03「移設のせい」ではなかったもの

kb.jsonの修正後も、埋め込みモデルのロードだけは120秒のタイムアウトで失敗を繰り返した。

[search] kb.json OK: 2110件 12.4s [search] model 失敗: model timeout 120000ms [rag] ハイブリッド検索 無効 → サーバー検索(query)にフォールバック

CDN経由でモデル本体をダウンロードする処理なので、新サーバーのCSPヘッダーなどを疑ったが、 実機(Android Chrome / iPad Safari)でモバイル回線条件を揃えて確認したところ、 旧サーバーでも同じ条件では成功率が半々程度だったことがわかった。数十MBのモデルDLがLTE回線で 120秒に間に合わないのは移設以前からの既知の挙動であり、失敗時はサーバー検索へフォールバックする 設計が、まさに想定どおりに機能していただけだった。

04次の移設への教訓

  1. Apacheの実行ユーザーを変えるときは、「サイトが表示されるか」だけでは足りない。 tempfile.mkstemp() のようにumaskを無視して権限を決め打ちする処理がコード内に潜んでいないか、 書き込み系の処理を洗い出して確認する。
  2. サービスやプラグインの有効化状態は、設定ファイルのコピーだけでは引き継がれないことがある。 「起動しているか」ではなく「ポートが実際にLISTENしているか」まで見て、機能そのものを検証する。
  3. 動かない原因を全部「移設のせい」だと決めつけない。モバイル回線のような外部要因は、 移設前の同条件と比較して初めて切り分けられる。
appw.jp — サーバー運用記録
ConoHa VPS移設シリーズ・関連記事

『移設後に踏んだ権限トラブル二題』を公開しました。