SECCON Beginners CTF 2020の監視・オペレーションを支える技術
LifeMemoryTeamの@atponsです。今回のSECCON Beginners CTF 2020はお楽しみいただけたでしょうか。自分は運営やインフラ整備をしておりました。 今回は、自分が担当していた監視、オペレーション部分の構築回りについて書いておきます。
死活監視
バッジ
今回参加者のみなさんには、このようなバッジを問題に付与して提供しておりました。これによって、問い合わせのオペレーションを削減することを目標に、昨年も取り組んでおりました。バッジでは、そのタスク(問題からフラグを取れたかどうか、サービスに疎通できるかどうか)の成功可否を表示していました。
また、最後にタスクを実行した時間を表示できるようにしていました。このバッジ生成は完全に自作したものです。詳細は死活監視の部分で書きます。
タスクランナー
定期的に実行してその状態を知りたい、という要望にはCI/CDツールを応用することが考えられます。ということでSECCON Beginners CTF 2019から、CIツールを用いてバッジを提供することを試みました。
昨年のSECCON Beginners CTF 2019では、このバッジを提供するためにDrone.ioを利用していました。Drone.ioはOSSで提供されるCIツールです。CIを定期実行してそのバッジを返却する、というシンプルなものでした。CIツールであるDrone.ioですので、標準でバッジを生成する機能はありましたが、ラベルが限られていたりしたので、バッジ生成については生成するサーバを書いていました。
また、Drone.ioはCIツールですので、VCSのリポジトリと強く紐付いています。そのため、問題リポジトリとソルバーの共存が難しかったりしたので、もっと汎用的なCI/CDツール(ConcourseやJenkins)を使おうかと思っていました。しかし、我々にとってCI/CDツールを応用するのは面倒くさいと思っていました。ところで、我々が欲しいのは、このような機能です。
- 定期的にソルバー(を叩くコマンド)を実行
- 良い感じにバッジを生成
- できれば通知欲しい
意外とシンプルですが、既存のモノに載っかろうとすると不足が生じる・・・。なるほど、では作ってしまえばいいなということでLifeMemoryTeamで作成しました。いろいろ機能つけていたら便利&結構大きくなってしまったので、気分がノったら公開するかもしれません。
これはタスクランナーのトップ画面です。定期的にタスクを実行したその結果を表示しています。この中から、バッジを参加者のみなさんに見せておりました。タスクランナーの主要なコンポーネントはこんな感じです。
主な機能はこんな感じになります。
- 指定されたタスクを実行
- バッジの生成(好きな文字で)
- Slack通知(失敗したときに通知がくる)
- Discord通知(複数回連続して失敗してしまった場合、競技者へ周知する)
タスクの実行詳細は他のCIツールのように閲覧できます。
運営ではSlackを用いているので、タスクの実行に失敗したら即座に作問者へMentionを飛ばし、確認してもらうようにしました。
そして、複数回連続して失敗するなど、明らかにダウンしていると言うときには自動でアナウンスしてくれます。
なお、このタスクランナーにはPrometheus ExporterがInstrumentされており、運営ではGrafanaを使ってアラートと可視化を行っていました。
この中の一部は、今回はパブリックなGrafanaでも公開していました。これについては後述したいと思います。
これによって、みなさまにバッジやステータスを提供することができました。
メトリクス
SECCON Beginners CTF 2020で使われる競技インスタンスは全て監視をしておりました。簡単な構成をこちらでご紹介します。
監視にはThanosを使いました。Prometheusでも良かったんですが、お試しということでThanosにしてみました。特に変わりもなくきちんと動いてくれて良かったです。運営用にGrafanaを使ってThanosを可視化していました。タスクランナーを含めた運営用のリソースへのアクセスにはゼロトラストプロキシ(リバースプロキシ)を使っており、Basic認証などを使うことなく各自のアカウント(今回はGoogleアカウント)で入ってもらって見ることができました。Grafanaなどはそういうプロキシのヘッダも見てくれるので便利ですね。
また、今回は外部公開用にGrafanaとPrometheusのペアを一組用意しました。これは、みなさんに裏側を見てもらいたいというのと、タスクランナーのダッシュボードをGrafanaに用意したらいいな(タスクランナーのページのデザインセンスがない)ということで設置しました。Grafanaを外部公開するために、GrafanaのAuth Proxy機能を使い、グローバルに公開したリバースプロキシ(Caddy)に手を加えて閲覧のみのアカウントが常にGrafanaに刺さるようにしています。運営は別途用意されたゼロトラストプロキシからアクセスすると、自分のアカウントと権限で編集できるようになるという感じにしています。
ゼロトラストプロキシ
LifeMemoryTeamではゼロトラストプロキシを導入してBasic認証をできるだけ減らすように努力しています。詳しくは CTFもDevOpsで――2020年代に入ったSECCON CTFの新しい挑戦 (1/2):セキュリティ・アディッショナルタイム(39) - @IT を見ていただきたいと思います。
GrafanaやPrometheus、運営用のポータルページなどはすべてゼロトラストプロキシを経由します。タスクランナーにもゼロトラストプロキシのインテグレーションが入っており、誰がタスクを実行したのか、などの情報も記録できるようにしています。GrafanaもAuth Proxyという名前でそのようなプロキシに対応しているので、是非使っていきましょう。
また、作問者向けに以下のポータルページを用意したりもしました。
ログ
問題サーバのデプロイには色々な方法が使われるのですが、Docker、Docker Composeを使ってデプロイするのが傾向として多いです。そこで生じるのがログ問題です。何か問題が起きたときにそのインスタンスに入って docker-compose logs
して、というのは結構大変というのと作問者じゃないとハンドリングできない、など色々問題が生じます。そこでログの集約を今回は考えていました。FluentdやElasticsearchを持ってくるのが一番早いのですが、スポットで開催するので管理面倒くさいんだよなぁ、と思ってたところにGrafana Lokiが使えるのではないか?という話がチーム内で出たので、今回はGrafana Lokiを使ったログ収集を行いました。
こちらも簡単な構成になりますが、今回はGrafana LokiとPromtailを使ってログ収集を行いました。実際には、journalのログをPromtail経由でLokiに収集しました。また、DockerのログドライバをLokiにして、Dockerのログの収集も行いました。
しかし、ここでDockerのログドライバをLokiにすると、手元で docker logs
で確認できないという問題が生じてしまいました。これは結構不便なので、journal ログドライバにしてjournalにまとめて一気にLokiに送信してしまいました。その他にもnginxやCaddyのログもPromtailで収集してみました。
最終的には↑のような形に落ち着きました。これでとりあえずGrafanaからログの閲覧ができるようになったので良かったです。また、Grafanaの機能をガンガン(収集したメトリクスと重ねたり)使えます。もちろん、自作タスクランナーもstdoutやstderrにエラーをはいていますので、GrafanaのAnnotation機能などを上手く使って失敗したときのログを一緒に表示できるようにしたりしています。
↑Lokiでログを眺める様子
・・・と、書こうと思ったのですが競技開始あたりでLokiのチューニングが足りなくて500をめちゃめちゃ返してきたりしてつらい、ってなりました。この辺はまだGAしたばっかりなので色々設定を検討する必要がありそうだなと思いました。がんばります。
おしまい
今回は色んな新しい試みをしましたが、概ね上手くいって良かったです。インフラの足回りやスコアサーバでは他のメンバーが頑張ってくれたのでそのあたりの話はその人達に任せますが、協力してもらってこそ上の取り組みができました。というか運営の皆さんにはいろいろとお願いしたりしたので、ありがとうございましたという感じです。参加していただいた皆さんにも、感謝いたします。
普段このようなCTFで監視がどう、みたいなことってあんまり話題にならないので、今回は思い切って書いてみました。
また、Grafana Loki、Promtailを使った新しいログ収集では、まだノウハウが共有されていないこともあるので時間ができたらそのあたりも共有できたらと思います。
この回りの話に興味がある人はSECCON Beginners CTFのDiscordで @atpons を呼んだら話してくれるかも・・・しれません。よろしくお願いします。