【ITニュース解説】Dev Log #1 — Mirage / Apate: Field Notes from the First Brick
2025年09月28日に「Dev.to」が公開したITニュース「Dev Log #1 — Mirage / Apate: Field Notes from the First Brick」について初心者にもわかりやすく解説しています。
ITニュース概要
コンテナアプリ開発初期、DB接続問題でサービスが停止。「プロセスが動いていてもシステムは機能しない」と学ぶ。遅延DB初期化などで起動は改善したが、永続化や本番環境へのDB再現性、監視、セキュリティなど多くの課題に取り組んでいる。
ITニュース解説
この記事は、Mirage / Apateというプロジェクトの最初の開発段階で直面した課題と、それらをどのように解決していったか、そして今後どのような改善を目指しているかを記した開発日誌である。このプロジェクトの最終的な目標は、「ハニーポット」と呼ばれる、攻撃者を誘い込みその行動を監視するためのシステムを構築することであり、そのためには、あたかも本物のシステムであるかのように見せかける「信憑性」が非常に重要となる。
開発の最初のステップとして、Mirageコンテナを立ち上げたところ、Dockerのコマンドで確認するとコンテナ自体は「稼働中」と表示され、ウェブサイトへのアクセスも成功を示す「200」というHTTPステータスコードを返した。開発チームはこれで本番環境に近いものができたと一度は安堵した。しかし、誰かがデータベースにアクセスを試みると、そこで問題が発生した。コンテナは動いているのに、サービス自体はまるで動かない「蝋人形」のようになってしまったのである。ここから、「プロセスが生きていること」と「システムが全体として機能していること」は異なる、という重要な教訓を得た。
開発の初日、「素朴な起動」と題された段階では、シンプルな非同期ウェブアプリケーションと軽量なSQLiteデータベース、そしてデータベースの構造変更を管理するAlembicツールを使う計画だった。これらをDocker Composeで簡単に連携させるつもりであった。しかし、実際にはデータベースの初期化がアプリケーションの起動を妨げた。アプリケーションが読み込まれる際に、データベースのスキーマ(構造)を作成しようとしたため、データベースとの接続がうまくいかないと起動全体が停止してしまったのだ。結果として、ログにはデータベース操作エラーが大量に記録され、コンテナは生きているにもかかわらず、ユーザーがアクセスするウェブサイトの各機能は全く動作しなかった。
この問題を解決するため、データベースの初期化を必要になるまで遅らせる「遅延初期化」という手法を採用し、非同期で起動するように変更した。また、接続失敗時には短い時間でタイムアウトするように設定した。これにより、たとえデータベースが利用できない状態であっても、サービス自体はすぐに起動できるようになった。しかし、この解決策には注意点がある。開発環境で使っているインメモリ(メモリ上)のSQLiteデータベースは、本番環境で使う予定のPostgreSQLデータベースとは挙動が大きく異なるため、攻撃者がシステムの同時実行性やデータ型、分離性などを詳しく調べると、おもちゃのようなデータベースだと見破られてしまう可能性がある。当面は開発時の試行錯誤には十分だが、将来的にはより堅牢なWAL(Write-Ahead Logging)方式のSQLite、または軽量なPostgreSQLを補助的なコンテナとして導入し、「安価なおとり」だと露呈しないようにする必要がある。
次に、「永続化の規律」の問題があった。開発ではSQLiteファイルを使い、本番ではPostgreSQLを使う計画だったが、コンテナを再起動するたびにデータベースファイルが衝突したり、データベースの構造変更を行うマイグレーション処理が互いに干渉し、データの状態が壊れるという問題が発生した。
この問題を解決するため、非同期処理に対応したPythonのデータベースライブラリであるAsync SQLAlchemyを導入し、適切な非同期フローを構築した。また、Alembicによるマイグレーション処理は、特定の環境変数が設定されている場合にのみ実行されるように制御した。さらに、一時的な対策として、マイグレーション処理が同時に実行されて競合しないよう、簡易的なファイルロックの仕組みを導入した。しかし、ファイルロックは一時しのぎの手段であり、将来的にはPostgreSQLの勧告的ロックや、Docker ComposeやKubernetesのようなコンテナ管理ツールが提供する、一度だけ実行される初期化ジョブを活用して、より確実な方法に置き換える必要がある。開発者がフラグを設定するのを「忘れずに行う」と信用してはいけない、という教訓も得た。
システムの「表面の強化」も行った。例えば、ウェブサイトのアイコンであるファビコンへの不必要なアクセスが頻繁に発生し、ログに大量の「404 Not Found」エラーが記録されていた。これを解決するため、コンテンツがないことを示す「204 No Content」というHTTPステータスコードを返すように変更した。また、環境変数を使って異なるドメインからのアクセスを制御するCORS(Cross-Origin Resource Sharing)設定を追加し、機密性の高いウェブページへのアクセスを制限した。今後の課題として、HTTPのHEADやOPTIONSメソッドへの一貫した対応、検索エンジンのクローラーへの指示ファイルであるrobots.txtの追加、そして「あまりに完璧に見えすぎないよう」に特定のページへのアクセス頻度制限や、意図的にわずかなランダムな遅延を加えるといった対策も検討している。
「意図的に騒がしい可観測性」も重要な要素である。以前は、システムが故障しても何も情報が得られず、問題の特定が困難だった。そこで、監視システムを導入した。具体的には、Prometheusという監視ツールを使って、リクエスト数、処理の遅延、データベースの代替利用回数などのメトリクス(測定値)を収集する「/metrics」というエンドポイントを追加した。また、問題発生時にはWebhookを通じてアラート通知を送信し、重複するアラートを排除したり、再送までの間隔を空けたりする仕組みも導入した。さらに、アラート自体の送信が失敗した場合も監視するようにしている。ただし、現在のところ、システムが劣化した状態だと、公開されているヘルスチェックのページに「代替モードが作動中」といった情報がそのまま表示されてしまう。これはハニーポットの「指紋」となり、攻撃者にシステムの状態を教えてしまうことになるため、外部向けのヘルスチェックは常に「OK」とだけ返し、内部のメトリクスで真実を伝えるように修正する予定である。
「テストサイクル」も改善された。以前は、外部のシステムに依存する遅くて不安定なテストが課題だった。現在では、非同期テストを53個も用意し、これらをアプリケーション内部で約0.5秒という短時間で実行できるようになった。しかし、まだ必要なテストもある。例えば、データベースがリクエストの途中で一時的に不安定になった場合や、マイグレーションのロック処理で競合が発生した場合など、システムが劣化した状態での挙動を検証する「プロパティベーステスト」の導入が必要だと考えている。
これまでの取り組みで得られた「初期の成功」として、以下の点が挙げられる。一つは、常に同じ結果で安定して起動できるよう「決定的な起動」が可能になったこと。二つ目は、「1秒未満で完了する高速なテスト」が実現したこと。そして三つ目は、漠然とした「感覚」ではなく、実際の数値に基づいた「リアルなメトリクス」でシステムの状態を把握できるようになったことである。
今後の「課題とリスク」も整理されている。データベースについては、より現実的なWAL方式のSQLiteとランダムな遅延の導入、またはPostgreSQLの補助コンテナの導入。ヘルスチェックについては、システムの準備完了状態と生存状態を分離し、外部には情報を隠し、内部には正直に状態を伝えること。マイグレーションについては、ファイルロックを廃止し、勧告的ロックや初期化ジョブに置き換えること。表面的な洗練として、HEAD/OPTIONSメソッドへの対応、robots.txt、レート制限、偽の遅延の追加。可観測性の洗練として、リクエストIDを含む構造化されたログ、Prometheusのヒストグラムとサンプルデータ、そして分散トレーシングの導入。CI(継続的インテグレーション)の強化として、コード品質、セキュリティ、依存関係のチェックツール(ruff, mypy, bandit, pip-audit, safety)の導入、依存ライブラリのバージョン固定、複数のCPUアーキテクチャに対応したビルド、必要最小限のOS要素しか含まないコンテナイメージの利用。そして最後に、データベースURL、代替機能の有効/無効、Alembicフラグ、アクセス許可オリジン、アラート設定など、すべての環境変数を文書化することが挙げられている。
結びとして、ハニーポットは完璧である必要はないが、信じてもらえるだけの「信憑性」は必要であると述べている。現状のMirageは、起動し、劣化した状態になり、カジュアルなスキャンを欺く程度には情報を提供できる。しかし、本格的な分析を行う攻撃者であれば、手抜きがされている部分を嗅ぎつけてしまうだろう。今後の計画は、システムを故障しにくくし、故障した際にはより明確に情報を発信し、そして「おとり」としての見せかけをより精巧にすることである。これが本番システムなのか、それともおとりシステムなのか、攻撃者が立ち止まって考えざるを得ない状況を作り出せれば、Mirageはその役目を果たしたと言えるだろう。