feature image

2021年5月16日 | 活動紹介

CPCTFを支えたインフラ

こんにちは。19Bの@mazreanです。普段はSysAd班でanke-toというアプリケーションの開発・運用を行なっています。

CPCTFでは@hijiki51@reyuとインフラを担当していました。

この記事ではCPCTFでのインフラ環境について書いていきます。

構成

サーバーは全てConoHaのVPSを利用しており、

の4台構成でした。

リバースプロキシにはCaddyを利用していました。CaddyはACMEプロトコルに則ってSSL証明書を自動取得してくれるので、今回のように複数の証明書が必要な場面で、かなり楽ができます。

簡単に各アプリケーションの説明を書いていきます。

スコアサーバー

ユーザー管理やflagの提出、点数計算、ランキング表示などを行う、CPCTFの中核となるアプリケーションです。本イベントのために去年のものを書き換える形で作成されました。Vue.jsを利用したクライアントと、MySQLをデータベースとして使うGo言語で書かれたサーバーで構成されます。

5月中はhttps://cpctf.space で公開しています。

ビジュアライザー

スコアサーバーからWebsocketでイベントを受け取り、得点や順位の変動を可視化するアプリケーションです。UnityのWebGLビルドを静的配信していました。

現在は停止していますが、大会中は以下のように動いていました。

PPC Judge

今年3月に卒業した@nariさんが開発された、PPC問(競技プログラミング系の問題)用のアプリケーションです。このアプリケーションは提出されたプログラムの実行を行い、AC(Accepted)であればflagを表示するアプリケーションです。提出のキューの管理やクライアント向けのAPIを提供するcentralサーバーと、提出をcentralサーバーから取得し実際に実行して結果を返すjudgeサーバー、Reactを利用したクライアントで構成されています。分離した環境でのジャッジの実行にはyosupo judgeを用いました。

Webshell

ユーザーごとに別々の環境を持つWebshellを提供するアプリケーションです。SSH接続をしてブラウザで動くUIを提供するWeTTYに、@mazreanが開発したユーザーごとのDockerコンテナを作りSSH接続を各コンテナに振り分けるssh-separatorを組み合わせて実現しています。WeTTY自体には暗号化の仕組みはなく、HTTPのままインターネットを通ると中間者攻撃により実行したコマンドなどの情報が漏れてしまうので、HTTPS化が必須です。

各問題環境

CTFのWeb問などのサーバーが必要な問題の環境です。HTTPで通信する問題はCaddyを利用することでHTTPSでアクセスできるようにしました。SSHなどのHTTP以外の方法で通信する問題ではリバースプロキシは利用しませんでした。

監視基盤

Prometheus、Loki、Grafanaを組み合わせて実現した、いわゆるPLGスタックの監視基盤です。blackbox_exporterssl_exporterdomain_exporterなどを用いて死活監視やssl証明書の有効期限の監視をできるようにしているほか、WebshellやPPC Judge、スコアサーバーからのメトリクスの取得を行なっています。

構成図

IaC

CPCTFのサーバーは全てAnsibleにより管理していました。

Ansibleで自動化した背景

複数サーバーを利用した構成になっていることや、動作検証のために各サービスを複数回デプロイすることから、手作業でのデプロイ作業では対応が難しく、より簡単で安全な方法を取る必要がありました。

また、スコアサーバーの開発はセルフホストのGitサーバーであるGiteaを利用して行われていたわけですが、デフォルトではCIの機能が存在せず、CIを利用したDockerイメージのビルドなどは難しい状況でした。

これらの状況を踏まえたうえで、CIを利用できるようにする手間を考えると、手元でのDockerイメージのビルドをIaCにより自動化するほうが簡単だと考え、Ansibleを採用しました。

採用した結果

想定通りデプロイが複数回発生し、かなりの作業効率の向上につながりました。実はビジュアライザーは本番中にもバグの解消のために複数回デプロイを行っていました。しかしデプロイをansibleコマンドを叩くのみで行うことが出来たため、一回のデプロイにかかる時間を数分程度に抑えることができました。さらに、デプロイ起因のダウンタイムはAnsibleによりファイルの移動が行われる1sかからない程度の時間に抑えることができました。

また、競技終了