『[試して理解]Linuxのしくみ 増補改訂版』輪読会のための CTF 風ハンズオン環境。 参加者1人につき1台の Ubuntu 24.04(x86_64)EC2 を払い出し、章の内容に対応した 問題(フラグ回収・壊れた環境の修復)を仕込んで、手を動かしながら理解を深める。
運営 AWS
────────────────────────────────────────────────────────────
terraform apply ──────▶ S3 (章の配布物・全12章)
EC2 × 参加者数 (Ubuntu 24.04)
└ cloud-init が S3 から全章を取得し、
「今日の章」(today_chapter)を自動で有効化
Lambda (採点サーバー・任意)
└ 正解すると Slack / Google Chat にお祝いを投稿
参加者
────────────────────────────────────────────────────────────
SSM Session Manager で VM に接続(SSH 鍵・公開ポートなし)
├ /opt/handson/<章>/ の問題を解いて submit 'flag{...}' で提出
└ start-chapter chNN で章を切り替え(その章のディレクトリに移動。復習・先取りも自由)
- フラグは参加者ごとに
HMAC-SHA256(FLAG_SECRET, "参加者:章-問題")で決まる。 コピペによる使い回しはできない。 - 採点サーバーがないときは VM 内でローカル採点にフォールバックする (VM には正解の sha256 しか置かないため、覗いても答えは分からない)。
- 章の setup は遅延実行(初回の
start-chapter時)。アクティブになるのは 常に1章だけで、切り替えると前の章は自動で一時停止する(作ったファイルや 修復の成果は残り、動いている問題プロセスと全体に効く設定だけ戻る)。start-chapterで戻ればそのまま再開できる。
| パス | 内容 |
|---|---|
terraform/handson/ |
VM 払い出し一式(開催ごとに apply、指定時刻に自動 destroy) |
terraform/ops/ |
常設インフラ(state バケット・章配布バケット・自動 destroy の CodeBuild。初回セットアップ手順も terraform/ops/README.md) |
chapters/chNN/ |
各章(ch01〜ch12)の問題文(README.md)・仕込みスクリプト(setup.sh)・自動判定(check.sh)・解説(SOLUTION.md、実施後公開)。任意で常駐ユニット一覧(units)と一時停止・再開フック(pause.sh / resume.sh) |
grader/ |
採点 Lambda(VM から Invoke API で直接呼ぶ、Slack / Google Chat の Webhook に通知) |
tools/submit |
VM に配布されるフラグ提出コマンド |
tools/e2e・tools/preflight・tools/connected |
運営用(章の実 VM E2E・ローカル機械チェック・接続中の参加者一覧) |
tools/vm/ |
VM のランタイム一式(cloud-init から呼ばれる bootstrap.sh、章切り替えの start-chapter、状態表示の handson-status、ログイン案内の handson-profile.sh) |
$ cd terraform/handson
$ cp terraform.tfvars.example terraform.tfvars # 参加者リストと今日の章を編集
$ export AWS_PROFILE=handson-tf # credential_process 経由(下記)
$ terraform init -backend-config=backend.hcl
$ terraform apply初回だけ、フラグ用シークレットの登録と backend.hcl の作成が必要
(terraform/ops/README.md 参照)。
aws login方式の認証を Terraform は直接読めないため、~/.aws/configに このプロファイルを用意してある(トークンは実行のたびに自動リフレッシュされる):[profile handson-tf] credential_process = aws configure export-credentials --profile default --format process region = ap-northeast-1
apply 後、接続用ワンライナーを Slack に貼る(参加者は NAME= を自分の名前に
変えて実行する。全員共通なので毎回人別に出力する必要はない):
$ NAME=自分の名前; aws ssm start-session --region ap-northeast-1 --target $(aws ec2 describe-instances --region ap-northeast-1 --filters Name=tag:Name,Values=handson-$NAME Name=instance-state-name,Values=running --query 'Reservations[0].Instances[0].InstanceId' --output text)参加者はマネジメントコンソール(EC2 → 接続 → セッションマネージャー)からも 接続できる(ローカルに AWS CLI 不要)。
第1回(2026-09-16、ch01・ch02)の反省を反映した手順。
- 接続確認は本編と切り離す。 開始前に Slack で接続テストを促し、誰が
つながっているかは
tools/connectedで見る。ウォームアップ問題の時間が 接続トラブルに食われ、詰まっているのか見ているだけなのか分からなかった。 - 章を切り替えるときは、コマンドとリンクをチャットに貼る。 画面共有で
見せただけでは誰も追っておらず、前の章の問題を解き続けた。
start-chapter ch02をそのまま貼り、問題順のページのリンクも送る (切り替え後はその章のディレクトリへ自動で移動する)。 - 解説は参加者主導にする。 各自が好きに解く → 解けた人に解き方を 話してもらう → 解けなくても全員が一言(感想・詰まった点)。運営が一方的に 進めるとレスポンスが薄く、楽しめているか分からない。
- 時間内に解けなくても、最後に想定解のライブ実演で「解けた状態」で終える。
- セッションは
ssm-user(パスワードなし sudo 持ち)で始まり、bash で/opt/handsonに落ちる(SSM セッション設定を Terraform で管理している)。 ログイン時にhandson-statusが章の状態と提出状況を表示する。 - 実験プログラムの実行は一般ユーザーのまま行い、システムを直す操作
(
ldconfig、chNN-checkなど)だけsudoする運用。本の実験スタイルと同じ。 /opt/handson/<章>/に問題、/usr/local/bin/にsubmit・start-chapter・handson-status・chNN-check、/etc/handson/に採点用データと章の配布物 (フラグと配布物は root のみ読める)。- 章の切り替えは
start-chapter chNN(初回はその場で setup が走る。 切り替え時に前の章は自動で一時停止し、進捗は残る)。対話シェルでは 切り替えと同時にその章のディレクトリへ移動する(/etc/profile.d/handson.shの関数。前の章のディレクトリに居残って別の章の問題を解いてしまう事故を防ぐ)。 一時停止中・未開始の章のディレクトリに居ると再開方法を1行案内する (cd で勝手に切り替えはしない)。
$ terraform output flags壊れた参加者の分だけをピンポイントで再作成できる(他の VM には触れない):
$ terraform apply -replace='aws_instance.handson["shimomura"]'フラグは同じ値が再計算されるため提出済みフラグはそのまま有効。失われるのは VM ローカルの状態(修復の成果・作業ファイル)だけで、採点サーバー側の提出記録は残る。
払い出し済みの VM 1台に対して、章の判定(chNN-check)の合否ケースを一通り流す。
対象章を start-chapter で有効化し、リポジトリの最新 check.sh を VM に
配置してから、chapters/chNN/e2e.sh のシナリオ(不合格系 → 合格系 → 後始末)を
SSM 経由で実行する:
$ tools/e2e ch03 # tag:Name から VM を自動検出(running が1台のとき)
$ tools/e2e ch03 i-xxxx # インスタンス指定
$ tools/e2e ch03 -q # 合否件数だけ表示(ネタバレなし。ブラインドプレイ前の確認用)e2e.sh は想定解を含むため、SOLUTION.md と同様に参加者 VM へは配布されない。
ローカルで完結する機械チェック(構文・必須ファイル・配布物の混入)は
tools/preflight <chNN>(全章は all)。章の品質監査プロセス一式は
Claude Code の /audit-chapter chNN --pre|--full に手順化してある
(--pre はネタバレなしでプレイ前に、--full はプレイ後に全段監査)。
destroy_at(既定:当日19時 JST)に自動で destroy される。前倒しで消したい
ときは:
$ aws codebuild start-build --project-name linux-handson-destroy # または terraform destroyIAM ロール等も消えるが、翌週 apply すれば同名で再作成される。
- 新しい章なら
chapters/chNN/にREADME.md(問題文)・setup.sh(仕込み)・ 必要ならcheck.sh(自動判定)を作り、grader/lambda_function.pyのQUESTIONSに問題を追加する。章が常駐させる systemd ユニットはunitsに 列挙する(章切り替え時の一時停止・再開の対象)。システム全体に効く設定を 仕込む章はpause.sh/resume.shで退避・復元する(ch08・ch09 参照)。 terraform.tfvarsのtoday_chapterを更新する(配布は常に全章)。
ch01〜ch12 は作成済み。
| 章 | 問題1(CTF) | 問題2(修復) |
|---|---|---|
| ch01 Linuxの概要 | 捨てられたフラグ | 起動しない greeter |
| ch02 プロセス管理(基礎編) | 眠り続けるデーモン | 倒せないプロセス |
| ch03 プロセススケジューラ | 気難しい預言者 | CPU を譲らない同居人 |
| ch04 メモリ管理システム | 生きているプロセスのメモリ | OOM で死につづけるサービス |
| ch05 プロセス管理(応用編) | 無口な郵便屋 | 消える予約 |
| ch06 デバイスアクセス | 開かずの金庫 | 7年前に巻き戻った業務データ |
| ch07 ファイルシステム | 消えたファイルのフラグ | いっぱいなのに空いている cache |
| ch08 記憶階層 | 幻を映すファイル | 書き込みが遅すぎる |
| ch09 ブロック層 | ディスクに書かれなかったフラグ | 親切すぎるチューナー |
| ch10 仮想化機能 | 消された仮想ディスク | 起動しない仮想マシン |
| ch11 コンテナ | 別世界の金庫 | 1回しか動かないコンテナ |
| ch12 cgroup | 秘密は絞られたときだけ | 何度でも殺されるワーカー |
- VM の中では何をしてもよい(man・Web 検索も自由)。
- ただし
/etc/handson/を直接読むのは反則(答え合わせ用データと 章の配布物=種明かしの置き場)。 - フラグを見つけたら VM 上で
submit 'flag{...}'。 - 今日の章は接続した時点で始まっている。前の章の復習や先取りは
start-chapter chNN(進捗は章ごとに残る。実行するとその章のディレクトリに移動する)。
- フラグは Terraform 側で全章分を事前計算(
terraform/handson/scripts/flags.py、 要 python3)して VM に渡す。FLAG_SECRETを VM に渡さないのは、user-data が IMDS 経由で参加者本人に読めるため。漏れて困るのは本人のフラグだけ (自分の答えのカンニングは性善説で運用)。 - 章のファイルは S3 配布(全12章で user-data の 16KB 制限を超えるため)。
cloud-init は小さな bootstrap だけを実行し、VM が S3 から
/etc/handson/dist/(root のみ)へ全章を取得する。 - 同時にアクティブなのは常に1章。t3.micro では章同士が干渉するため
(ch04 の OOM が他章のデーモンを巻き添えにする、ch08 の sysctl 劣化が
他章の計測を狂わせる、など)。
start-chapterの切り替えで前の章の 稼働中ユニットを記録して stop し(chapters/chNN/units)、全体に効く knob はpause.sh/resume.shで退避・復元する。一時停止中の章のchNN-checkは 誤判定を避けるため案内スタブに差し替える。 - コスト目安:t3.micro × 10人 × 2時間 ≒ 30円/回。