SECCON Beginners CTF 2020のインフラ構築自動化を支える技術
LifeMemoryTeamのak1ra24です。今回のSECCON Beginners CTF 2020はお楽しみいただけましたでしょうか。
先日、@atpons先生によるブログが公開され、書いてよと言われたのでたまには書こうと思います。
今回は、自分が担当していたサーバーやディスク、パケットフィルタ等さくらのクラウド側での操作をinfrastructure as codeによる構築自動化について書いていきたいと思います。
以下のGitHub Actionsでさくらのクラウドプロバイダーを利用したTerraformについては、以下の記事のようにやるとより効率よくバージョンを変えたりできるので、ぜひ読んでください。 id:febc_yamamoto さん、ありがとうございます!!
TerraformとCI
SECCON Beiginnersではインフラスポンサーであるさくらインターネット株式会社様の「さくらのクラウド」を使用させてもらっております。
インフラの構築を自動化するために、GitHubを利用し、PRからmasterにマージされるとインフラの構築がTerraformとansibleによって実行される、CI/CDを試してきました。
Drone.io
はじめは、OSSのDrone.ioを使用して行いました。
terraform plan
の結果に関しては、tfnotifyを使用し、planの結果などをインフラチームで確認してから、masterにマージを行ってきました。
いろいろなチャットツールやCIに対応していて、大変便利でした。
これは、Terraformを使用するのみなら使い心地はよかったのですが、SECCON Beginnersでは、問題サーバーが稼働しているか、ソルバーはちゃんと動き解けるようになっているのかなども同様にTerraformで使うものと同じCIツールを使ってきました。
しかし、Drone.ioでは@atpons先生のブログの通り
また、Drone.ioはCIツールですので、VCSのリポジトリと強く紐付いています。そのため、問題リポジトリとソルバーの共存が難しかったりしたので、もっと汎用的なCI/CDツール(ConcourseやJenkins)を使おうかと思っていました
Concourse CI
上記のため、次はConcourse CIをすることに決まりました。 Concourse CIを使っていましたが、微妙に使いづらい点があったのですが、忘れてしまったのでたぶん自分たちの使い方が悪かったのだと思います。 Concourse CIでも同様にソルバーチェックを行う予定でしたが、いろいろありできませんでした。
このあたりのソルバーチェックの話は、atpons先生のブログで書かれています。
GitHub Actions
SECCON Beginners CTF 2020では、GitHub Actionsを使用するようになりました。
Terraform用のGitHub Actionsがありますが、terraform-provider-sakuracloudはCommunityプロバイダーのため、その場合terraform-github-actionsではそのプロバイダーを入れるオプションがないので自作しないといけません。
そのため、以下のterraform-provider-sakuracloud用のGitHub Actionsを作成しました。
基本は以下のブログ記事とterraform-github-actionsを読むといろいろカスタマイズできます。
terraform-sacloud-github-actionsは、ベースとなるDocker Imageを用意して、そこにENTRYPOINTでshell scriptを書いていろいろ設定するだけです。
しかしここで問題だったのが、terraform-provider-sakuracloudではversion2から書き方がversion1のときとは変わってしまった問題があり、Beginners CTF 2020ではversion2を使用し、 Beginners NOCが管理するインスタンスのTerraformではversion1で管理していたため、GitHub ActionsでCIを回すときに、terraform-provider-sakuracloudのバージョンを指定して実行したいというのがあったので、できるようにしてあります。
上記のブログのとおりのシェルスクリプトに、今回はさくらのクラウドのTerraformプロバイダーのバージョンを環境変数で渡すことで、バージョンを指定してTerraformのプロバイダーをダウンロードし、特定のプロバイダーバージョンを使用できるようにしました。
ENTRYPOINTで実行するシェルスクリプトに以下のように追加することで解決しました。
if [ -z "$TF_SACLOUD_PROVIDER_VERSION" ]; then TF_SACLOUD_PROVIDER_VERSION=$(curl -s https://api.github.com/repos/sacloud/terraform-provider-sakuracloud/releases | jq -r '.[0] | .tag_name' | sed -e 's/[^0-9\.]//g') fi echo "$TF_SACLOUD_PROVIDER_VERSION download..." wget https://github.com/sacloud/terraform-provider-sakuracloud/releases/download/v${TF_SACLOUD_PROVIDER_VERSION}/terraform-provider-sakuracloud_${TF_SACLOUD_PROVIDER_VERSION}_linux-amd64.zip unzip terraform-provider-sakuracloud_${TF_SACLOUD_PROVIDER_VERSION}_linux-amd64.zip rm terraform-provider-sakuracloud_${TF_SACLOUD_PROVIDER_VERSION}_linux-amd64.zip rm -rf /terraform-provider-sakuracloud mv terraform-provider-sakuracloud* /terraform-provider-sakuracloud
実際に、 terraform-sacloud-github-actions
を使った GitHub Actionsの設定で以下のように設定します。
masterにPRの設定
name: pr_master on: pull_request: branches: - master type: - opened - synchronize - rerequested env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SAKURACLOUD_ACCESS_TOKEN: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN }} SAKURACLOUD_ACCESS_TOKEN_SECRET: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN_SECRET }} SAKURACLOUD_ZONE: ${{ secrets.SAKURACLOUD_ZONE }} TF_ACTION_WORKING_DIR: "terraform/production" TZ: Asia/Tokyo TF_SACLOUD_PROVIDER_VERSION: "2.3.1" jobs: terraform: name: 'Terraform' runs-on: ubuntu-latest steps: - name: 'Checkout' uses: actions/checkout@master - name: 'Terraform Format' uses: "lifememoryteam/terraform-sacloud-github-actions/fmt@master" - name: 'Terraform Init' uses: "lifememoryteam/terraform-sacloud-github-actions/init@master" - name: "Terraform Plan" uses: "lifememoryteam/terraform-sacloud-github-actions/plan@master"
PRが作られると、GitHub Actionsでterraform fmt, planが実行され、PRにコメントが以下のように来る
masterにpush時に, terraform apply
にとってインスタンス構築を行う場合の設定
name: push_master on: push: branches: - master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SAKURACLOUD_ACCESS_TOKEN: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN }} SAKURACLOUD_ACCESS_TOKEN_SECRET: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN_SECRET }} SAKURACLOUD_ZONE: ${{ secrets.SAKURACLOUD_ZONE }} TZ: Asia/Tokyo TF_SACLOUD_PROVIDER_VERSION: "2.3.1" jobs: terraform: name: 'Terraform' runs-on: ubuntu-latest steps: - name: 'Checkout' uses: actions/checkout@master - name: 'Terraform Format' uses: "lifememoryteam/terraform-sacloud-github-actions/fmt@master" - name: 'Terraform Init' uses: "lifememoryteam/terraform-sacloud-github-actions/init@master" - name: "Terraform Apply" uses: "lifememoryteam/terraform-sacloud-github-actions/apply@master"
試行錯誤してきましたが、これらによって問題作成者、インフラチームになるべく負荷がかからないようにインフラ構築の自動化を行ってきました。
試行錯誤したこと
tfstatediff
TerraformとCIを使った自動化できましたが、インフラチームがいちいち作られたインスタンスのIPアドレスはこれだよって問題作成者にお知らせするのは、時間がかかりインフラチームの負担も増えるため面倒です。
そこで、問題作成者に必要な情報としては以下が必要と考えました。
- 作成されたサーバーのIPアドレス
- サーバーの名前
それだけをお知らせしてくれるやつ作ればいいか~ということで、 tfstatediff
を作りました。
tfstatediffは、terraform apply
前と後の二つのtfstateファイルを取得し、Drone.ioの環境変数を参照しDrone.ioで回した対象のPRを取得します。
そのPRに対して、二つのtfstateを比較して増えたサーバー情報からサーバー名とIPアドレスをPRにコメントするというのを行いました。
以下のような画像でPRにコメントされます。
tfstatediffの問題点、それは閉じたPRは誰も見ないため、「どこにインスタンスの情報あるの?」というメッセージが大量発生しました。。。
tfstatediffからの改善
確かに閉じたPRは、見ない人が多いなってことになり、GitHub Wikiにすべてのサーバー情報を書き込むことで、問題作成者がそこを参照すればいいのかっていうのは無事解決しました。
alertmanager-webhook
LifeMemoryTeamでは、監視は基本Prometheus + alertmanager + Grafanaで可視化をやっています。
Alertも全部GitHubにまとめれば、見るところ少なくて済むのではというアイデアの元、alertmanager-webhookを作りました。
仕組みは、WebHookのサーバーがalermanagerからのアラートのjsonを受け取り、WebHookサーバーはGitHubのAPIを叩いて、GitHub Issueにそのアラート情報とそこに書かれているラベルを書き込むという流れです。
alertmanager-webhookの問題点、それはGitHub Issue誰も見ない
そもそも開催時は、運営陣が集まり、インフラチームがGrafanaで監視していたため、GitHub Issueを見る前に「やばい」っていうのは問題作成者に伝えていたのもあって、これ必要ないやんってことになりました。
alertmanager-webhookからの改善
ここはatpons先生のブログにも書かれていましたが、今回はDiscordに問題のソルバーの状態についてはお知らせがBotによってお知らせしてくれていたので、改善をしてくれました。
おしまい
今回もまた試行錯誤しながら新しい試みもでき、そして概ね上手くいって良かったです。
特に監視周りは、atpons先生がいろいろやっておかげで、運営中も寝れました。ありがとうございました。
今回さらっとですが、インフラの足回り的なことをかけたので、スコアサーバーについても他のメンバーが頑張ってくれたので書いてくれるはず!!(たぶん)
インフラチームの皆さんには知識・技術力不足の自分にいろいろ教えていただき、協力してもらっているのでいつも感謝しております。
また問題作成者の方々にも、毎回インフラ作成の方法が変わってしまい申し訳ないのですが、すぐに対応してもらくれて、いつも感謝しております。
本当は上記以外にも、 tfgenerator
とか他にも作ったりしたのですが、これは書いても面白くないので書くのはやめます。
最初はSECCON Beginners CTF 2020のことだけ書こうと思ったら、経緯も書かないとわからないかなって思い、気づいたらSECCONでやったインフラの振り返り記事になりました。
よりきれいにまとまったSECCONインフラの全体的な記事は、以下の記事を読むと全体的になんとなくわかるかなと思います。(会員登録しないと見れないのは申し訳ないです。。)
拙い記事でしたが、ここまでお読みいただきありがとうございました。
補足
GitHub - hashicorp/terraform-github-actions: Terraform GitHub Actions はアーカイブされたので、今後は hashicorp/setup-terraform - Terraform by HashiCorpを参考にしていこうかなと思っています。
terraform versionは指定できていないので、そちらにも対応できるようにしていきたいというお気持ちはあるので、どこかで頑張ろうかと思います