ICTトラブルシューティングコンテスト2022(ICTSC2022)に参加しました

3/4 から 3/5 にかけて開催された ICTトラブルシューティングコンテストに参加しました。

最終的に2785点を獲得し、3位でした。残り1問解ければ全完でした。

3位!

チームメンバー

この5人でチーム「結束baud」(ケッソク バウド*1)として参加しました。

作戦

2日間共に出ることができる人が私を含めて2人であったこともあり、なにも考えずに各自が解けそうなものを好きに解いていました。 とはいえ私は皆が選択して残った問題をやろうと思っていました。

各日は次のメンバーたちが来てくれました。

  • 3/4: gotti rand0m
  • 3/5: gotti k1h sho

また、すべてオンラインで行いました。そのための工夫としては

  • Discord 上で通話を常に繋いでおく
  • 問題を解き始める・手放すときは Discord のテキストチャンネルで宣言をする
  • 引き継ぎができるように Scrapbox に情報を書いておく

といった感じでした。

解いた問題

私は以下の問題を解きました。

どのように進めたか、簡単に説明します。

1日目

私は前日に早めに寝た結果2時頃に起きてしまって、そのままぼんやり時間を過ごしていたら開会式の時間になっていました。
開会式の間に気合を入れるためにシャワーを浴びており、開会式に出ているのは gotti だけという状態でした。

10:30 からはしばらく問題を喋りながら眺めて、触れそうかつ点の高いものから適当にやることにしました。

ajl protocが... 見つからない!?

11:10 頃に取りかかりました。

~builder 内の Dockerfile を見たら Alpine Linux を使っており、経験から即座に共有ライブラリのせいだろうと思いました。

とはいえ根拠なしに変更するのもよくないので、PATH が通っていることや、ファイルがあることを確認して(lddld と混同してコマンドがないと言われたりもしてから)シュッとベースイメージを変更しました。 そのあと ~builder/deploy を叩いたら protoc-gen-go がないよ、と言われたのでそういえばそうだったなと思いながら足して、動きました。

9c9
< FROM golang:1.20.1-alpine AS builder
---
> FROM golang:1.20.1-bullseye AS builder
10a11,12
> RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 && \
>   go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

当初はイメージの容量が制約条件になく満たしていたのですが、11:52 頃に運営から builder:v1 イメージが 300MB を越える事態はゆるしまへんとの通告がなされたため、Alpine Linux で動くように直しました。*2

ldd を見ると libstdc++libgcc を入れることでとりあえず動きそうなので入れて試したものの解決しなかったので、さらに gcompat を追加することで動かしました。

参考文献を探したところ Alpine LinuxwikiRunning glibc programs というページがあり、面白がっていました。

ところで、docker image ls したら alpine ベースなのに size が 280MB くらいあったのですが、なぜなのかわからずじまいでした。そういう罠?

解答: https://gist.github.com/otofune/236952a4dc0001e8254b6fa94dc220a9#file-ajl-md

ing データベースに入れない!

12:35 頃に取りかかりました。

nc を使って TCP パケットの疎通を確認しながらファイアウォールの設定を調整しました。人生初 VyOS でしたが show firewall した内容を真似するだけですんなり使えました。 その後、MySQL の bind-address を修正し、その上で user アカウントに接続できないのは host が限定されているためだということもすぐにわかりました。

制約からユーザーを変更することは許されていると考え CHANGE USER を使おうとしたものの user ユーザーでは当然叩けず、root@localhost に入りたかったのですが方法がわからず詰まりました。

このときは MySQL は host の名前解決を行うことを利用し、嘘解答を出しました。 具体的にはどういう副作用があるかはわからないものの /etc/hosts に次の行を追加し、動作させました。

172.16.1.128 localhost

そもそも採点されないかと思いきや、165点と高得点を頂けたためしばらく放置することになります。

rct 盲点の窓

14:48 頃に取りかかりました。

systemd status をしたところ次のように open が失敗して、nginx が stop していました。

Mar 04 04:25:00 blog-server nginx[292]: nginx: [emerg] open() "/etc/nginx/sites-enabled/blog.example.jp" failed (40: Too many levels of symbolic links) in /etc/nginx/nginx.conf:62

ファイルを確認したところ、相対パスシンボリックリンクを貼っており「やるよね〜」と思いながら絶対パスで貼り直し、nginx -t が通ることを確認してから再起動しました。

そのあと 502 エラーが起こったので proxy_passjournalctl -u blog のログと見比べて、ホストが違っていたのでシュッと直して提出しました。

解答: https://gist.github.com/otofune/236952a4dc0001e8254b6fa94dc220a9#file-rct-md

cya オレオレS3

15:15 頃に取りかかりました。

問題文を見ただけで path-style のことを思い出していたので、もう一日目は終わり際だったこともあり、のんびりドキュメントを調べてから提出しました。

詰まった点としては fluentd が td-agent という形で配布されていることを覚えていなかったこと、adm グループがなにに使われているか調べようとしたもののよくわからず無駄に時間を食ったことくらいでした。

解答: https://gist.github.com/otofune/236952a4dc0001e8254b6fa94dc220a9#file-cya-md

1日目の終わり

1日目と2日目でどうも問題が同じそうだぞと思いながら集まったのですが、gotti 共々昼飯を抜いてぶっ続けで解いていたせいで疲れきっており、準備をする気力はありませんでした。

代わりにひたすら雑談をして、Splatoon 3 をやって寝ました。

2日目

前日に寝不足でありながら起きていた影響か 10:00 ぴったりくらいに起床して遅刻。 たまごかけご飯とインスタント味噌汁を食べてから開始しました。

dkv 答えてくれPingサーバー、ここにはuserモードとsystemdと、俺がいる!

10:27 頃に取りかかりました。 rand0m が提出して0点だった問題でした。

どうも問題の解明はできており、解決方法が前提条件と合致せず採点されなかったようでした。 具体的には systemd コマンドを直接叩いており、解答の中で許可されるアクションに合致していなかっただけでした。

いい感じに修正して提出。

解答: https://gist.github.com/otofune/236952a4dc0001e8254b6fa94dc220a9#file-dkv-md

cfb ストレージ消し飛んだ...

11:30 頃に取りかかりました。

ぱっと見るとフルバックアップを戻してから、増分バックアップを順に戻せばよさそう。 zfs send の逆操作を調べて出てきた Oracle のページを見たら、フル・増分バックアップどちらであっても受信する時に使うコマンドは同じということを感じとりました。 また、丁度同じページに zfs recv -F (force) を使うといい感じに rollback もしてくれるらしいと知りました。

ただし、新しい増分スナップショットを受信するには、まず受信側のファイルシステムロールバックする必要があります。または、-F オプションを使用すれば、ロールバック手順を実行する必要がなくなります。 https://docs.oracle.com/cd/E24845_01/html/819-6260/gbchx.html

どうせ zpool は壊れているので適当に復元してもいいだろうと思い、あんまり調べずこのコマンドをそのままつっこんで試すことにしました。 ls backups を bash の for で回してやれば順番通りになりそうだったので、シュッと recv する bash スクリプトを書いて通ったので提出。

解答: https://gist.github.com/otofune/236952a4dc0001e8254b6fa94dc220a9#file-cfb-md

ing データベースに入れない!(2)

12:40 頃に取りかかりました。 私が提出して165点だった問題でした。

わからんなあと思いながら触っていたところ、なにかの拍子に sudo mysql -u root が通ることに気付きました。 気付いた時点ですべての問題を誰かが解いている状態だったので、なぜ認証が通るのかのんびりと調べて、始めて auth_socket 認証を知りました。

The socket plugin checks whether the socket user name (the operating system user name) matches the MySQL user name specified by the client program to the server. https://dev.mysql.com/doc/refman/8.0/en/socket-pluggable-authentication.html

また、Ubuntu のデフォルト設定では root パスワードを設定しないと root は auth_socket のみになるらしく、デフォルトだったのかとひとり納得していました。

解答: https://gist.github.com/otofune/236952a4dc0001e8254b6fa94dc220a9#file-ing-md

omu トラブルシューターの初仕事

13:45 頃に取りかかりました。 gotti が提出して175点だった問題でした。

再展開をし、まっさらな状態で設定を眺めていたところ、報告書から漏れている static route の設定を見つけました。 聞いたところ、認識していたにも関わらず手順書に入れ忘れていたことが判明し、気付けてよかった〜 となりました。

ついでに ufw まわりの設定をちゃんとやるぞと思って man を調べてどうも protocol は次のようにすれば通せるらしい、と思ってやったところコケて泣いていました。

user@rt03:~$ sudo ufw allow proto 89 to any from any
ERROR: Unsupported protocol '89'

iptables ルールを ufw と共存させる方法もあったなあと調べて https://wiki.ubuntu.com/UncomplicatedFirewall#Advanced_Functionality を読んだりもしていました。 が、作業中に間違えて iptables -vF を叩いて再展開の刑を受けてしまったこともあり、ルーター本体に関するセキュリティ要件はないことはわかっていたため行いませんでした。

解答: https://gist.github.com/otofune/236952a4dc0001e8254b6fa94dc220a9#file-omu-md

おわりに

チーム word-unknown-tsukuba-otaku の tosuke に影響されてチームを作っており、倒してやろうと思っていたら優勝されて悔しいです。

とはいえ、3位という大きな結果を残すことができました! 出てみたいと思いながら学内で人を集めなければならないことにハードルを感じていたのですが、呼びかけをしたら5人と上限人数まで集まり、各人しっかり問題を解いてくれました。感謝しかありません。

私としては小さな得点の問題や部分点となった問題を拾ってチーム全体に貢献できたのではないかと満足している部分はありながら、ネットワークまわりがあまりわからず任せっきりで、助けになれなかったことが悔しいです。

初参加だったのですが、とても楽しく、休止を乗り越えてくれてよかった*3と心から思います。 運営の皆さん、本当にありがとうございました。

VyOS にも入門できたことですし、もっとネットワークまわりを触って、来年は全員を薙ぎ倒すぞ!

*1:ボー あるいは ボドー と読むのが正しいのでしょうが、発音しにくいためこうなりました

*2:なお alpine でなくても満たせそうでしたが、運営の意図から外れるだろうと思ってやりませんでした

*3:https://icttoracon.net/archives/8799