ICTSC2025 本戦 問題解説: [4402] 「なんかたまにエラー画面が出るんですけど」

問題文

概要

社内向けに新規構築したWeb APIのテスト運用を開始したところ、利用者から「たまにエラー画面が表示される」という報告が寄せられている。

詳しく検証を行ったところ、以下のような不安定な挙動が確認された。

  • 単発でアクセスした場合、エラー(HTTP 500)になることがある。

  • 負荷テストツール等で連続アクセスを行うと、数秒〜10秒程度待たされた後に別のエラー(HTTP 504)が入り混じって発生する。

各サーバーのプロセス自体は起動しており、一見すると正常に稼働しているように見える。
この事象の原因を究明し、システム本来の仕様通りにDBからデータを取得し、すべてのリクエストが正常に処理されるように修正せよ。

制約

  • アプリケーションのコードは、不具合の調査目的以外で直接編集・変更してはならない。(DBの接続先やパスワード等の仕様は既存のままにすること)

  • Nginxの設定を変更し、静的ファイルを直接返却させたり、別のバックエンドにルーティングさせたりしてはならない。必ず既存のアプリケーションプロセスを経由してDBからデータを取得する構成を維持すること。

  • 各サーバに設定されているパケットフィルタリング機能のサービス自体を完全に無効化してはならない。不要な制限がある場合は、必要な通信のみを許可するよう個別のルールを修正すること。

初期状態

  • web-server 上から $ curl http://10.128.10.10/ を実行すると、本来のデータではなく 500 番台のエラー画面が出力される。

  • web-server 上から $ ab -n 50 -c 10 http://10.128.10.10/ を実行して負荷をかけると、結果に Non-2xx responses: 50 が記録され、かつ Connection Times の最大値が 10000ms付近に達するリクエストが混在する。

  • アクセス後、NginxのアクセスログにHTTPステータスコード 500 と 504 が混在して記録される。

終了状態

  • web-server 上から $ curl http://10.128.10.10/ を実行すると、Success: Hello ICTSC2025 という文字列が含まれる正常な応答が返却される。

  • web-server 上から $ ab -n 50 -c 10 http://10.128.10.10/ を実行すると、Failed requests: 0 かつ Non-2xx responses が出力されず、Connection Times の最大値が1秒未満で安定して完了する。

  • アクセス後、NginxのアクセスログにHTTPステータスコード 200 のみが記録される。


解説

  1. db-serverで sudo ufw delete limit 5432/tcp (または sudo ufw allow 5432/tcp による上書き)を実行し、通信制限を解除する。

  2. db-serverでPostgreSQLに管理者としてログインし、予約語をエスケープした上で正しい権限付与コマンド (GRANT SELECT ON target_table TO "user";) を実行する。

想定解法

1. ログの確認と事象の切り分け

解答者はまず app-server にログインし、アプリケーションプロセスのログを確認する。

sudo journalctl -u app.service --no-pager | tail -n 50

ログから、以下の2種類の異なるエラーが併発していることが確認できる。

  1. [CRITICAL] WORKER TIMEOUT
  2. psycopg2.errors.InsufficientPrivilege: permission denied for table target_table

別々のレイヤーで問題が起きているため、ネットワーク層とデータベース層で切り分けて対処を行う。

2. 504 Gateway Time-out の解消

ログの WORKER TIMEOUT は、DBサーバへ接続を試みたものの応答がなく、アプリケーションがハングアップしてNginxが504を返している状態を示す。db-server にログインし、ファイアウォール設定を確認する。

sudo ufw status

出力結果から、TCP 5432ポート宛の通信に LIMIT ルールが適用されていることがわかる。UFWの limit は、同一IPから30秒間に6回以上のアクセス試行を検知するとパケットをサイレントドロップする。
この制限を解除し、通常の許可ルールに変更する。

sudo ufw delete limit 5432/tcp
sudo ufw allow 5432/tcp
sudo ufw reload

これによりタイムアウトは解消され、以降のアクセスはすべて権限エラーへと変わる。

3. 500 Internal Server Error の解消

UFWの制限を通過したパケットは、データベース側で弾かれている。
ログの permission denied の通り、app-server が接続に使用しているロールに対して、対象テーブルの SELECT 権限が付与されていない。

db-server でPostgreSQLに管理者ログインし、probdb データベースの権限を修正する。

sudo -u postgres psql -d probdb

ロール名である user はPostgreSQLの予約語であるため、ダブルクォーテーションで囲んでエスケープした上で権限付与コマンドを実行する。

GRANT SELECT ON target_table TO "user";
\q

4. 動作確認

対象のWebサーバに対して連続でリクエストを送り、エラー画面が出ることなく、データベースから取得した値を含んだ正常なレスポンスが返ってくることを確認する。

curl http://10.128.10.10/

ab -n 50 -c 10 http://10.128.10.10/

採点基準

満点 (100%)

以下の2つの条件を両方満たし、Webサーバへの連続アクセス時にエラーが一切発生しなくなっていること(Success: Hello ICTSC2025 が安定して出力される状態)。

  • db-server のUFWによるTCP 5432ポートの接続制限が解除されている。
  • db-server のPostgreSQLにて、probdb データベース内のロール "user" に対する target_table のSELECT権限が正しく付与されている。

部分点

  • ネットワーク制限の解除 (40%): UFWのレートリミット設定が削除され、パケットのサイレントドロップによる504エラーが発生しなくなっていること。(sudo ufw status5432/tcpALLOW であることを確認)

  • データベース権限の修正 (40%): アプリケーションが使用するDBロールに対し、対象テーブルの読み取り権限が付与され、権限不足による500エラーが発生しなくなっていること。(\dp target_table 等で権限を確認)

  • 修正後の動作確認 (20%):

    curl http://10.128.10.10/
    
    ab -n 50 -c 10 http://10.128.10.10/
    
    

    の結果を記載している。

注意事項

  • limit ルールの修正ではなく、sudo ufw disable でファイアウォール自体を完全に停止させた場合は、制約違反として0点とする。
  • トラブルの本質(テーブル権限不足)とは無関係に、pg_hba.conftrust に書き換える等のセキュリティを低下させる場当たり的な対応を行っていた場合は0点とする。
  • 制約に反して app-server 上のNginxの設定やFlaskのコードを書き換え、静的な文字列として Success: Hello ICTSC2025 を返却するなどの迂回行為を行っていた場合は、0点とする。

講評

コマンドを実行しました、という記載だけでは報告書として成り立っていません。

ちゃんと、hogehogeというコマンドを実行して、hugahugaという出力が返ってきました、と書きましょう。