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かからない程度の時間に抑えることができました。

また、競技終了後にサーバー規模の縮小作業を行ったのですが、この作業もAnsibleのyamlをわずかに修正しansibleコマンドを叩くのみで行うことができ、かなり楽に行えました。

ローカルPCからのDockerイメージの転送

Dockerイメージをそのままサーバー上へ送ることは難しいため、docker saveでtarに固めてAnsibleのcopyモジュールでサーバー上へ送りdocker loadでサーバー上に読み込むことで行いました。以下は実際にWebshellのユーザーログイン用Dockerイメージの転送に用いたAnsibleのタスクです。

# wetty_ssh.image_versionにはDockerイメージのバージョンが入ります
- name: Save docker image central 
  delegate_to: localhost
  docker_image:
    name: cpctf-ubuntu
    tag: "{{ wetty_ssh.image_version }}"
    state: present
    archive_path: /tmp/cpctf/cpctf-ubuntu.tar
    source: local

- name: Copy docker images
  become: yes
  copy:
    src: /tmp/cpctf/cpctf-ubuntu.tar
    dest: /srv/webshell/cpctf-ubuntu.tar

- name: Load images
  become: yes
  docker_image:
    source: load 
    load_path: /srv/webshell/cpctf-ubuntu.tar
    name: cpctf-ubuntu
    tag: "{{ wetty_ssh.image_version }}"

nssによるSSH権限管理

CPCTFで用いた全てのサーバーのユーザー管理はlibnss-jsonを用いて行っていました。

libnss-jsonはLinuxのName Space Switchという機能のJSONバインディングモジュールで、これを利用することでリモートから読み込んだJSONを用いてサーバーのユーザー管理を行えます。

traPのSysAd班ではこれとsshのAuthorizedKeysCommandを組み合わせてGitHubに登録された公開鍵を用いてサーバーへSSHができるようになっています。

今回のCPCTFでもこの仕組みを用いることで、ユーザー管理を行いました。ユーザー情報のJSONは、Gitea上で管理し部内PaaSのShowcaseを用いて静的配信しました。

準備期間中短期間のみサーバーへのログイン権限を与えたい場面が複数回発生したのですが、その処理がかなり楽になりました。

監視基盤

今年のCPCTFではPrometheus、Loki、Grafanaを用いて監視基盤を構築しました。

これまでのCPCTFでは監視基盤が存在しなかったため、何らかのトラブルが発生していても、報告を受けるまで気づけませんでした。また、原因の調査も直接サーバーに入って行う必要がありました。

今回のCPCTFではnode_exporterを用いての次のような情報を取得し、トラブル発生により早く気づくことのできる環境を作りました。

トラブル発生可能性が高いと判断し、カスタムエクスポーターによりメトリクスを取得したのは

です。

このほかにWebshell、スコアサーバーではgoroutine数、メモリ使用量なども取得していました。取得にはPrometheus公式のGoクライアントライブラリを用いました。

これらの監視により、大会中のトラブルにいち早く気づくことができました。実際にCTFのWeb問のアプリケーションが暴走し、CPU使用率が100%に到達する事態が発生したのですが、数分で気づき対処することができ、影響が出るのを10分に満たない期間に抑えることができました。

また、昨年度まではリクエスト数やWebsocketのコネクション数などのデータが残っていなかったため、今年のスコアサーバーの開発ではどの程度の負荷に耐えることができれば良いのかの予測が難しく、目算で負荷対策を行っていました。しかし、今年はPrometheusによりデータを取りそのデータのバックアップを残しているため、来年のスコアサーバーなどの負荷対策は今年より行いやすくなると思われます。

他にも、大会終了から数時間で下記の記事で統計情報の公開を行えたのも、Grafana、Prometheusによりデータの取得、可視化を行っていたためです。

CPCTF当日レポート
皆さん、本日は参加していただいてありがとうございます!本記事では、開催中の様子や各種統計情報を紹介します。 参加者今回の参加者は156人(内、点数を獲得したのは107名)でした。事前登録者数が少なくてドキドキしていましたが、直前に登録してくれる方が多くて安心しました。 ユーザー数の推移盛り上がり全体・新入生のどちらでも競り合いが激しく、白熱したイベントになりました。皆さん、お疲れさまです! 全体ランキングの推移新入生ランキングの推移HTTPリクエスト数PPCの提出数問題情報ジャンルごとの問題数+-----------+----------+| genre | 問題数 …

余談

Prometheusのカスタムメトリクスではトラブル発生要因の排除以外にも運営人が楽しむためのメトリクスもとっていました。具体的には、各問題の提出数や正答率で、登録ユーザー数も実は半分ぐらい参加者が増える様子を見たかったためにとっていました。

大会開始直後に一気に参加者数が増える様子や、OSINT問の「Student ID Card」の提出数が増加していく様子を見て、運営も大会中盛り上がっていました。個人的に監視基盤を整備してよかったと思った瞬間でした。

まとめ

今年のCPCTFでは全インフラのAnsible化、nssによる権限管理、監視基盤の整備などで、昨年度までと比べて大きく安定性を向上させることができました。参加者アンケートでインフラの安定性を褒めてくださる声もあり、実際に安定性を向上させることができたと考えています。

各アプリケーションのデプロイのフローなど、まだまだ改善の余地はあると考えているので、来年度以降もより良いインフラ基盤を作っていきたいと考えています。

5/19(水)にスコアサーバーについてのブログ記事も予定されています。また、日付は決まっていませんがWebshellについてのブログ記事も出る予定なのでお楽しみに!

mazrean icon
この記事を書いた人
mazrean

SysAd班で活動したり、百合漫画の布教をしたりしている人。

reyu icon
この記事を書いた人
reyu

いろいろやったりやらなかったりしてます。

hijiki51 icon
この記事を書いた人
hijiki51

20B/理学院 Go書いたり競プロしたり

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2021年3月19日
traPグラフィック班の活動紹介
NABE icon NABE
2021年7月8日
じゃぱりぱーく・おんらいん
suzushiro icon suzushiro
2019年4月22日
アセンブリを読んでみよう【新歓ブログリレー2019 45日目】
eiya icon eiya
2021年4月26日
CPCTF2021 作問者writeup by zassou
zassou icon zassou
記事一覧 タグ一覧 Google アナリティクスについて