/

皆さんはLDAPを使ったことがありますか?
LDAPとは Lightweight Directory Access Protocol の略で、ディレクトリサービスのためのプロトコルです。ディレクトリサービスというのはその名前の通りディレクトリのようなツリー状の情報を管理するサービスで、その特徴からリソースの場所・設定などの情報を一元管理するのに用いられています。
とりわけ大きな組織においてはこのような情報を管理することにおいて大きなメリットがあります。管理すべき機器やユーザの数が膨大になってきたとき、必要に応じて素早く情報にアクセスできることが要求されるためです。
多くの企業ではディレクトリサービスを用いて情報を管理しており、ディレクトリサービスを提供するためのソフトウェアとしてはActive Directoryが代表的です。優れたGUIと長年Microsoftがメンテナンスしてきたことによる実績があり、何よりWindowsマシンを管理するのに長けているからです。
一方で、昔ながらのOpenLDAPを用いる場合もあります。OpenLDAPはLDAPのオープンソース実装の一つであり、高機能なActive Directoryなどに比べて軽量・高速に動作します。先に挙げたActive DirectoryもLDAPをベースに実装されており、LDAPへの理解を深めることはディレクトリサービスを知る上で欠かせないと言えます。
今回出題したトラブルは、LDAPに関する基本的な操作・知識を問う問題でした。LDAPを運用した経験がないと時間内に解くのはなかなか難しかったのではないかと思います。

問題文

最近LDAPを導入して、GitLabの認証とサーバへのSSHをLDAP経由で行えるように設定したのだがどうもうまく動いていないようだ。 原因を突き止めて、GitLabにログインできるようにしてほしい。必要に応じてサーバの設定を変えても構わないが、なるべくセキュリティレベルが下がらないようにしてほしい。また、admin (cn=admin,dc=finals,dc=ictsc) とoperator (cn=operator,ou=users,dc=finals,dc=ictsc) のパスワードが脆弱なので、可能であれば指定したものに変更してほしい。
なお、すべてのサーバはLDAPに登録してあるSSH鍵で入れるようになっているはずだが、こちらも同様に動いてないのでLDAPサーバのみにログインできるアカウントとLDAPに登録したSSH秘密鍵を用意した。必要に応じて使ってほしい。

問題のゴール状態

GitLabにLDAPユーザのoperator (パスワード変更済み) でログインに成功する

解説

今回の問題に登場するサーバは全部で2つです。

  • LDAPサーバ (dc.finals.ictsc)
  • GitLabサーバ (gitlab.finals.ictsc)

それぞれにSSSDがインストールされており、sss_ssh_authorizedkeys コマンドを用いてLDAPに登録されている公開鍵でログインできるようになっているはずが、SSHログインできなくなっている状態がスタートです。
また、GitLabのLDAPログインも同様に動かない (SSL errorが発生する) ので、これらを直してほしいという内容です。

初期状態をまとめると以下の図のようになります。

図で×印がついている部分がLDAPで認証を行う部分であり、これらをすべて復旧するのがゴールです。

問題のゴール自体はGitLabでLDAPログインできるようになることとなっていますが、実際には認証を直してGitLabサーバへのSSHログインができるようにならないとトラブルシューティングが行えないため、まずはSSHログインを復活させるのが1つ目のゴールになります。

SSL証明書の有効化

GitLabでLDAPログインを試みると以下のようなエラーメッセージが出ます。

Could not authenticate you from Ldapmain because “Ssl connect syscall returned=5 errno=0 state=sslv2/v3 read server hello a”.

これはそもそもSSL接続が確立できていないということなので、opensslで確認してみます。

~$ openssl s_client -connect 192.168.2.10:636
CONNECTED(00000003)
140350580467344:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 289 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol  : TLSv1.2
Cipher    : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg   : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1552100789
Timeout   : 300 (sec)
Verify return code: 0 (ok)
---

これを見ればわかるとおり、そもそも証明書が設定されていないことがわかります。
しかし /etc/ldap/slapd.conf を見ると TLSCACertificateFile=/etc/ldap/ssl/ca.crt などの設定がされているため本来は有効化されているはずですが、これらのパラメータは少なくともdebian系のslapdでは正しく動きません。よって適切なLDIFを書いて、手動で有効化してやる必要があります。

dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ldap/ssl/ca.crt
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/ssl/dc.finals.ictsc.key
-
add: olcTLSCertficateFile
olcTLSCertficateFile: /etc/ldap/ssl/dc.finals.ictsc.crt

SSL証明書のパーミッションも正しくしておきましょう。

~$ ls -lhat /etc/ldap/ssl/
total 24K
drwxr-xr-x 2 root root 4.0K Feb 17 02:07 .
-rw-r--r-- 1 root root 2.0K Feb 17 02:07 ca.crt
-rw-r--r-- 1 root root 5.9K Feb 17 02:07 dc.finals.ictsc.crt
-rw------- 1 root root 1.7K Feb 17 02:07 dc.finals.ictsc.key
drwxr-xr-x 7 root root 4.0K Feb 17 02:07 ..

...

~$ ls -lhat /etc/ldap/ssl/
total 24K
drwxr-xr-x 2 root root     4.0K Feb 17 02:07 .
-rw-r--r-- 1 root openldap 2.0K Feb 17 02:07 ca.crt
-rw-r--r-- 1 root openldap 5.9K Feb 17 02:07 dc.finals.ictsc.crt
-rw-r----- 1 root openldap 1.7K Feb 17 02:07 dc.finals.ictsc.key
drwxr-xr-x 7 root root     4.0K Feb 17 02:07 ..

先ほどのLDIFを olcSSL.ldif という名前で保存し、以下のコマンドで適用します。
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f olcSSL.ldif

SSSDで公開鍵が取得できない

サーバの/etc/ssh/sshd_configを見ると、AuthorizedKeysCommandディレクティブにsss_ssh_authorizedkeysが設定されていることがわかります。しかし試しにこれを実行してみても公開鍵が出力される様子がありません。

~$ /usr/bin/sss_ssh_authorizedkeys operator
~$

これが原因でSSHログインができないことがわかりますが、なぜ公開鍵が取得できないのでしょうか。LDAPの設定を見直しても公開鍵はちゃんと登録されています。

sss_ssh_authorizedkeysコマンドには実はデバッグオプションが存在します(これはhelpでも出てきません)。--debug 10という引数をつけてコマンドを呼び出すと、詳しいエラーメッセージを見ることができます。

~$ ~$ sss_ssh_authorizedkeys operator --debug 10
(Wed Mar 27 20:25:46:414921 2019) [sss_ssh_authorizedkeys] [main] (0x0040): sss_ssh_format_pubkey() failed (22): Invalid argument

このメッセージから、登録されている公開鍵のフォーマットが正しくないと予想できます。もう一度LDAPに登録されている鍵をみてみましょう。

~$ ldapvi -D 'cn=admin,dc=finals,dc=ictsc' -b 'dc=finals,dc=ictsc'
...
loginShell: /bin/bash
sshPublicKey:; ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJodlzzbHLCCfldHfG7xKlA4tl6t118hAdjbzuZIYCJELLFTwctlFVOBgZHs4JkT5Cgm7eK1VXL99w7SapNzhMs= operator@dc^M\

よく注意して見ると、公開鍵の末尾に改行が含まれていることがわかります。試しに改行を消してみるとどうなるでしょうか。

~$ sss_ssh_authorizedkeys operator
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJodlzzbHLCCfldHfG7xKlA4tl6t118hAdjbzuZIYCJELLFTwctlFVOBgZHs4JkT5Cgm7eK1VXL99w7SapNzhMs= operator@dc

ちゃんと取得することができました。秘密鍵でSSHログインが可能なことも確認できます。

~$ ssh -i files/id_ecdsa operator@192.168.2.10
Last login: Sun Feb 17 03:38:42 2019 from 192.168.2.1
Could not chdir to home directory /home/operator: No such file or directory
operator@dc:/$

これで登録してある公開鍵でログインができるようになりました。

パスワードの変更

GitLabのトラブルシューティングに移る前にパスワードの変更をしておきます。

ldapviを用いてパスワードを変更しようとするとエラーが発生し変更できなかったり、phpLDAPadminにおいてはエラーすら出なかったりします。(この場合も依然として変更はできていません)

このような場合はアクセスコントロールを疑います。/etc/ldap/ldif/olcAccess.ldifが置いてあるのでこの中身をみてみましょう。

dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
# password should not be readable and only user can update
olcAccess: to attrs=userPassword
by self =sw
# uid, uidNumber, gidNumber should not be updated
olcAccess: to attrs=uid,uidNumber,gidNumber
by self read
by users read
# for ssh login
olcAccess: to attrs=sshPublicKey
by self write
by group.exact="cn=servers,ou=sgroups,dc=finals,dc=ictsc" read
# for sudo
olcAccess: to dn.children="ou=sudoers,dc=finals,dc=ictsc"
by group.exact="cn=servers,ou=sgroups,dc=finals,dc=ictsc" read
# not matched
olcAccess: to *
by self read
by users read
by anonymous auth
by * none

注目すべきはuserPasswordに対して設定してある項目です。by self =swはsearchとwriteをユーザー自身に許可する設定なので大丈夫なはずですが、by anonymous authがついていないのでbindができないようになっています。

よってolcAccess.ldifuserPasswordby anonymous authを追加してあげるとパスワードの変更が可能になります。

GitLabの証明書

SSSDのエラーを修正したことでGitLabにログインが可能になりますが、GitLabのログインページは依然としてエラーを出します。エラーメッセージを読むと証明書周りのエラーということがわかるので、/etc/gitlab/gitlab.rbの中身を確認します。

LDAPの設定部分にverify_certificates: trueとあるので、証明書がチェックに引っかかっていることが原因のようです。これを回避するためには証明書のチェックを無効にするのが一つの手ですが、セキュリティの観点からはあまり好ましい対策とは言えません。証明書 (dc.finals.ictsc) をシステムのチェーンに登録して有効な証明書とするのがベターです。

~$ sudo -s
~# cd /usr/share/ca-certificates/
~# openssl s_client -showcerts -connect dc.finals.ictsc:636 2>/dev/null | openssl x509 > rootCA.crt
~# echo "rootCA.crt" >> /etc/ca-certificates.conf
~# update-ca-certificates

GitLab上のユーザブロック解除

証明書のエラーを解決するとようやくLDAP認証が動くようになります。しかしoperatorユーザはなぜかログインできず、ブロックされている旨のメッセージが出ます。

これはすでにローカルでoperatorという名前のユーザが登録されていたことが原因ですが、解除するためにはadminアカウントが必要になります。operatorユーザでSSHログインするとsudoでrootになることができるので、GitLabのrails consoleに直接接続してパスワードを書き換えることができます。

~# gitlab-rails console production
user = User.where(id: 1).first
user.password = 'strongpassword'
user.password_confimation = 'strongpassword'
user.save!

これでadminとしてログインして、admin areaにあるユーザの設定でLDAPのoperatorユーザを有効にするとログインできるようになります。

採点基準

GitLabサーバにログインできるようになるのが一つ目のゴールなので、ここまで到達した場合は満点の50%が得られます。

  • LDAPユーザのパスワードを変更する (20%)
  • GitLabサーバにログインできるようになる (30%)
  • GitLabでLDAP認証を使えるようにする
    • LDAPをTLS通信に対応させる (30%)
    • operatorユーザのブロックを解除する (20%)

講評

今回の問題の一番のポイントはLDAPそのもではなく、Undocumentedな仕様を適切に探し出すことができるかという点にありました。OpenLDAPのslapd.confが意図した通りに動かない、SSSDが解釈できる公開鍵のフォーマットに隠された仕様があるなど、ソフトウェアは思わぬところで予想に反する挙動を示すことがあります。事前に知識を持っている場合はすぐに気づくことができますが、多くの場合はそうではありません。今回はLDAP+SSSD+GitLabという、意外と一般的だが普段は使ってなさそうな技術を意図的に選びました。

もっとも高得点を獲得したのはLDAPのSSL証明書の設定を解決したチームでした。残念ながらGitLabサーバへSSHできるところまでたどり着くチームはいませんでしたが、コンテスト時間がこれだけ短く、問題数が多いことを考えると仕方ないのかなと思います。また、得点の高さをみて手をつけなかったチームも結構いたのではないでしょうか。トラコンでは必ずしも問題を解ききる必要はないので、部分点解法を大量に稼いでいくのも一つの戦略です。

 /

問題文

同期から次の依頼がありました。

「会社の上司から勉強用にとサーバーをいただきました!
Webページを立ててみたかったのでNginxをインストールしようとしていたのですが、

sudo apt-get install nginx
をするとエラーが出てしまってインストールできないんです…
どうしたら良いんですかね…?」

問題を解決して、インストールがエラーや警告なしで成功するようにしてください。

問題のスタート状態

  • サーバーへのHTTP通信が失敗する

問題のゴール状態

  • Nginxがapt-getによってエラーや警告なしにインストールされている
  • サーバーへHTTP通信ができる

トラブルの概要

Nginxをインストールしようとしたがエラーが発生してインストールが完了しない。

解説

今回のトラブルの原因は /var/log 配下にあるファイル全てにi属性(Immutable属性)が付与されていたからでした。

今回のトラブルが起こる流れとして /var/log配下にあるdpkg.logをアップデートしようとするとコピーなどは成功するが、インストール後スクリプト(post_install)の実行時にdpkg.logファイルの権限変更(chmod)が失敗し、インストール失敗(再インストール)としてマークされます
そして、次回インストール時にpost_installスクリプトが実行されるように予約がされます。

なので、Nginx(その他のパッケージ)をインストールしようとすると再インストールがマークされたdpkgが優先的にインストールされようとしますが、同じ理由で再失敗するのでそれ以降のインストールが無視される(インストール失敗とマークされる)といった流れでした。

ですので、今回は以下のコマンドでdpkg.logのi属性をはずしたらインストールは完了します。

また、今回のゴールはNginxがapt-getによってエラーや警告なしにインストールされているも含まれています。
エラーや警告を解決していない回答が多く見られました。
今回エラーや警告が出ていた原因は/var/logのaptディレクトリにもi属性が付与されていたためです。
なので、aptディレクトリもi属性をはずしたら今回のトラブルは解決です。

回答例

50%

$ cd /var/log
$ sudo chattr -i dpkg.log

もしくは

$ sudo rm -rf /var/lib/dpkg/info/dpkg.postinst
$ sudo rm -rf /var/lib/dpkg/info/dpkg.postrm
$ sudo dpkg --configure dpkg

以上のようにNginxのインストールに成功すれば手段は問わず50%の点数を与えました。

100%

$ cd /var/log
$ sudo chattr -i *
$ sudo apt-get install nginx

2行目が sudo chattr -i aptでも問題ありません。

Nginxのインストール時にエラーや警告がでなければ100%の点数を与えました。

採点基準

  • sudo apt-get install nginxで正常にインストールされない。(0%)
  • sudo apt-get install nginxでインストールされHTTP通信はできるが、インストール時にwarningが消えていない。(50%)
  • sudo apt-get install nginx正常にインストールされHTTP通信ができ、インストール時にwarningがでない。(100%)
 /

問題文

あなたは新入社員のメンターとして研修に参加していました。
ある日、上司に急用が入り、
「今から日曜まで出張に行ってくるから、それまでに研修用のWebサーバにアクセスできることだけ確認しといて!」
と言われました。しかし、指示されたサーバーである http://192.168.9.1 にアクセスしても何も表示されません。

このサーバーはWebサーバーとしてLighttpdがインストールされており、現在の状態に戻す方法がわかっていれば自由に設定を変更することは許されています。

Webサーバが動作した上で同じ問題が再発しないように修正を行い、以下を上司に報告してください。
– トラブルの原因
– 解決のために行ったコマンド
– 行った変更を元に戻すためのコマンド

情報

  • IPアドレス: 192.168.9.1
  • ユーザー: admin
  • パスワード: hello-world
  • インストールされているWebサーバー: lighttpd

問題のスタート状態

  • サーバーへのHTTP通信が失敗する

問題のゴール状態

  • サーバーへHTTP通信が成功し、ブラウザから見るとLighttpdのプレースホルダのページが表示される
  • 来年以降同じ問題が再発しないよう設定する

トラブルの概要

systemdで起動時のターゲットにWebサーバ(lighttpd)が登録されていない

解説

この問題は、Ubuntu 18.04のinitシステム(起動時に最初に呼び出される、その他に起動に必要なアプリケーションを起動したりシステムを管理するためのアプリケーション)であるsystemdを用いた環境で「サービス」(デーモン)が起動していないというトラブルです。
使用しているサーバーはUbuntu 18.04を使用しています。このディストリビューションでは、initシステムとしてsystemdを採用しています。
initrcやinitngなどを使用しているシステムでは /etc/init.d/ 配下にあるファイルを用いて起動していますが、systemdを使用したシステムでは systemctl というコマンドを使用します。

また、OS起動時に自動起動する設定も同じコマンドで行います。

今回のトラブルでは、Webサーバーはインストール済みの状態ですので、このコマンドを使用してサービスを起動/停止することで解決可能です。

systemctl start lighttpd
systemctl stop lighttpd

また、この変更のみでは自動起動はしないため、設定を元に戻すコマンドは必要ありません。
しかし、自動起動設定を行った場合は自動起動設定を停止するコマンドも投入する必要があります。

自動起動設定コマンドと停止コマンドは以下のとおりです。

systemctl enable lighttpd
systemctl disable lighttpd

回答例

お疲れ様です。解答を送信します。

今回のトラブルは、lighttpdが起動していないことが原因でした。
lighttpdはインストール済みのため、以下の手順でWebサーバの起動をすることができます。

Webサーバの起動

systemctl start lighttpd

また、同じ問題が再発しないよう、研修終了までWebサーバを自動起動するようにします。

Webサーバの自動起動の設定

`systemctl disable lighttpd

これらの設定を元に戻す方法は以下のとおりです。

Webサーバの停止・自動起動の解除

systemctl stop lighttpd
systemctl disable lighttpd

採点基準

  • 上記手順をすべて達成して100%
  • 自動起動をしていない -10点
  • 元に戻す方法を記述していない -10点

講評

この問題は「コンテストサイトの使い方を学ぶための簡単な問題」という位置づけだったこともあり、とても簡単な問題でした。そのおかげで、全チームに満点を付与することができました。作門者もにっこり喜んでいます。
Webサーバの設定ファイルを変更したチームもありましたが、もしかしたら惰性や習慣で systemctl start lighttpd をしてしまったのかなと個人的には思っています。
私も以前に似たようなことを行い、問題解答不能になったことがありました。
実際にサーバーに対して操作を行う際には、原因の調査と解決を分けて考えることで
「なぜこのトラブルが起きているのか?」を正しく判断することができ、
また「どのようにしてこのトラブルを解決するか」が分かれば、トラブル解決の途中で新たなトラブルに遭遇した際にも
「どのようにして環境をロールバックするか」を理解することにつながると思います。
トラブルが起きている当初の環境への復旧(ロールバック)は「ICTSCの問題環境」では簡単に行えますが、実務環境では大抵できません。ここからの締めの文章が浮かびませんが、みなさん頑張ってください。

問題タイトルは、「新人(入社数ヶ月)が新人(入社数日)のメンターになり、新人研修を行っている」ようなシチュエーションを考えて付けましたが、問題文を作る能が無く、なーんか変な内容になってしまいました。

 /

問題文

通話が繋がらない!

離れたオフィス間でも円滑にミーティングをできるように社内ミーティングサービスを運用している。
セキュリティ強化として入社した新入がLinux Routerにとある変更を行ったところ、社内ミーティングサービスが動作しなくなった。

このままだと、別支店のチームがミーティング参加ができなくなる!

一刻も早く修正せよ!

情報

  • 社内ミーティングサービスURL: https://192.168.8.19/

Linux router X

IPアドレス: 192.168.8.1
ユーザー: admin
パスワード: a3nVn22U

Linux router Y

IPアドレス: 192.168.8.9
ユーザー: admin
パスワード: PGrydZ1

Application Server

IPアドレス: 192.168.8.19
ユーザー: admin
パスワード: ani8EZu

使用手元機材の情報

Client A(支店A)

2960-A: FastEthernet 0/1

Client B(支店B)

2960-A: FastEthernet 0/2

問題のスタート状態

参加者PC同士での社内ミーティングサービスを使用したビデオ通話ができない。

問題のゴール状態

参加者PC同士での社内ミーティングサービスを使用したビデオ通話ができる。

構成

注意事項

この問題で使用するブラウザは「Google Chrome」と「Firefox」のみです。
これら以外のブラウザでは問題回答ができませんので、このブラウザで動作確認をしてください。

この問題を解く際は、参加者PCを無線Wifiに接続せず使用手元機材の情報に記述されている有線LANポートのみに接続してください。
参加者は自身のPCからApplication ServerにHTTPSでアクセスすることで社内ミーティングサービスを利用することができます。
このサービスではHTTPS通信に自己署名証明書を使用をしております。そのため、ブラウザからアクセスした際に証明書の警告がされます。

トラブルの概要

社内ミーティングサービスではWebRTCが使用されており、ビデオ通話などのメディアデータの送受信は基本的にP2PでClient同士が通信するものである。
今回Client同士がP2P通信するための経路情報が互いのLinux routerに登録されていないために発生したトラブル。

解説

トラブルの概要の通り、Linux router XとLinux router Yにて、互いの経路情報を登録する。

永続化については問題文に明記していないので、永続化の有無については得点への反映はない。

解答例

Linux router X

# # 経路追加
# route add -net 192.168.8.8 netmask 255.255.255.248 gw 192.168.8.18
# # 永続化
# cat /etc/network/if-up.d/branch-b-routing
#!/bin/sh
/sbin/route add -net 192.168.8.8 netmask 255.255.255.248 gw 192.168.8.18

Linux router Y

# # 経路追加
# route add -net 192.168.8.0 netmask 255.255.255.248 gw 192.168.8.17
# # 永続化
# cat /etc/network/if-up.d/branch-a-routing
#!/bin/sh
/sbin/route add -net 192.168.8.0 netmask 255.255.255.248 gw 192.168.8.17

講評

ICTSC 2018お疲れ様でした。

今回の問題は、解答を見てしまえばとても簡単な解決方法だったかと思います。
ソースコードのほとんどは、ICTSC2018 一次予選のWebRTC問題とほとんど一緒です。

しかし、今回の問題では、テキストチャットがWebRTC通信が確立できていない際に、アプリケーションサーバを介して通信するため、戸惑ったチームも多かったかと思います。

回答率は他の問題に比べ低かったですが、WebRTCに使用されている様々な、プロトコル、技術を考慮している解答も見られ、WebRTCを多くの人に知ってもらいたかった作問者としては非常に嬉しかった結果でした。

 /

問題文

あなたの後輩が「FTPが動かない」と相談してきました。後輩の頼みを断れないあなたはこの問題を見てみることになりました。
どうやら家の外から認証は出来そうなのですが、ディレクトリ一覧を見ることが出来なさそうです。

手元PCから192.168.7.1で接続し、ディレクトリ一覧を取得できるようにしてください。

情報

server

routerがport forwardingを行っているため、routerのIPアドレスにSSHをすればserverに接続することが可能です。

IPアドレス: 192.168.7.1
ユーザー: admin
パスワード: cf1e8c14e54505f6

router

もしログインするのであれば、serverを踏み台にしてアクセスしてください。

IPアドレス: 192.168.7.254
ユーザー: admin
パスワード: f3395cd54cf857dd

トポロジー図

禁止行為

  • 以下の設定を変更してはいけない。
connect_from_port_20=YES
  • 以下のルールを削除・無効化してはいけない。
iptables -A OUTPUT -p tcp --sport 20 -j REJECT

解説

問題文に「家の外から認証は出来そうなのですが、」とあるように、家の外から接続することが出来ないようです。家の外で接続する場合、途中でアドレス変換(NAT)されたり、routerに入力されるパケット(inboundのパケット)を制限されている場合があります。そのため、ACTIVEモードよりもPASVモードで接続する方が接続しやすいでしょう。

まず、192.168.7.1にFTPで接続してみます。

$ ftp 192.168.7.1
Connected to 192.168.7.1.
220 (vsFTPd 3.0.3)
Name (192.168.7.1:root): admin
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pass
Passive mode on.
ftp> ls
227 Entering Passive Mode (192,168,7,129,169,115).
ftp: connect: Connection timed out
ftp>

すると、227 Entering Passive Modeというレスポンスが表示された後タイムアウトになります。このレスポンスの後方にある6つの数字は、PASVモードで接続する際に使用するIPアドレスとポート番号を表しています。RFC959を見てみると次のように説明されています。

DATA PORT (PORT)

The argument is a HOST-PORT specification for the data port
to be used in data connection. There are defaults for both
the user and server data ports, and under normal
circumstances this command and its reply are not needed. If
this command is used, the argument is the concatenation of a
32-bit internet host address and a 16-bit TCP port address.
This address information is broken into 8-bit fields and the
value of each field is transmitted as a decimal number (in
character string representation). The fields are separated
by commas. A port command would be:

PORT h1,h2,h3,h4,p1,p2

where h1 is the high order 8 bits of the internet host
address.

つまり、(192,168,7,129,169,115)という6つ組の数字は、「PASV接続する時は、192.168.7.129:43379に接続してください」という意味になっています。

ですが、192.168.7.129というのは、NAT内でのIPアドレスなので、NATの外から直接接続することはできません。本来であれば、192.168.7.1と表示されてほしいはず。192.168.7.1にアクセスした後に192.168.7.129にパケットが転送されてくれないと困るため、routerに対してrouterが着信した通信を転送する設定(Port forwarding)を入れる必要もありそうです。また、問題文からserverにもiptablesの設定が入っていそうなので、その設定も追記をする必要があるかもしれません。

まず始めに、serverの/etc/vsftpd.confに以下の設定をします。詳しい設定の意味は、こちらを確認すると良いです。ざっくり説明すると「vsftpdはスタンドアロンで動作し、IPv4からの接続を受け付ける。また、PASV接続の際には192.168.7.1の60001~60025番ポートを使用する」という意味になります。この設定を入れたあとは、vsftpdを再起動してください(sudo systemctl restart vsftpd)。

- LISTEN=NO
+ LISTEN=YES
- LISTEN_IPv6=YES
+ LISTEN_IPv6=NO
...
+ pasv_address=192.168.7.1
+ pasv_min_port=60001
+ pasv_max_port=60025

この次に、serverのiptablesの設定を確認してみましょう。

$ sudo iptables -L
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT tcp -- anywhere anywhere tcp dpt:ftp
ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
ACCEPT tcp -- anywhere anywhere tcp dpt:domain
ACCEPT udp -- anywhere anywhere udp dpt:domain
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
REJECT tcp -- anywhere anywhere tcp spt:ftp-data /* DON'T REMOVE IT. */ reject-with icmp-port-unreachable

今回、60001番から60025番ポートをFTP用に使おうとしているので、それを許可する設定を入れる必要があります。

$ sudo iptables -A INPUT -p tcp --dport 60001:60025 -j ACCEPT

最後にrouterのPort forwardingの設定を確認してみましょう。

$ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DNAT tcp -- anywhere router tcp dpt:ftp to:192.168.7.129
DNAT tcp -- anywhere router tcp dpt:ssh to:192.168.7.129

Chain INPUT (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 192.168.7.128/25 anywhere

先程と同様に、60001番から60025番ポートを転送するような設定を入れる必要があります。

$ sudo iptables -t nat -A PREROUTING --dst 192.168.7.1 -p tcp --dport 60001:60025 -j DNAT --to 192.168.7.129

ここまで設定を入れると、次のようにPASVモードで接続できていることが分かります。

ftp 192.168.7.1
Connected to 192.168.7.1.
220 (vsFTPd 3.0.3)
Name (192.168.7.1:root): admin
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pass
Passive mode on.
ftp> ls
227 Entering Passive Mode (192,168,7,1,234,102).
150 Here comes the directory listing.
226 Directory send OK.
ftp>

想定解法

  • serverの/etc/vsftpd.confに以下の設定を入れた。
- LISTEN=NO
+ LISTEN=YES
- LISTEN_IPv6=YES
+ LISTEN_IPv6=NO
...
+ pasv_address=192.168.7.1
+ pasv_min_port=60001
+ pasv_max_port=60025
  • serverのiptablesに以下の設定を入れた。
sudo iptables -A INPUT -p tcp --dport 60001:60025 -j ACCEPT
  • routerのiptablesに以下の設定を入れた。
sudo iptables -t nat -A PREROUTING --dst 192.168.7.1 -p tcp --dport 60001:60025 -j DNAT --to 192.168.7.129

講評

解答率はだいたい30%くらいでもうちょっと解いてくれる人多いかなと期待してました。作問者自身も中学生とかの頃はよく使っていましたが、最近はsftpで済ませてしまうことがほとんどなので、最近触れる人は意外とFTPを使ったことがないのかもしれないと考えています。

この問題は想定解法以外にもいくつかの解法があります。想定解法ではPASVモードで接続するようにしているのですが、ACTIVEモードで接続するのもこの問題のゴールである「手元PCから192.168.7.1で接続し、ディレクトリ一覧を取得できるようにしてください。」を満たしているため正解としています。中には、ip_conntrack_ftpip_nat_ftpを用いた解法もありましたが、基本的に使っても接続できるようにはならないはずです。想定解法以外の解法についてはいつかどこかで説明できたらなと思っています。

 /

問題文

MSPに管理を依頼している踏み台サーバから中にあるアプリケーションサーバに、公開鍵認証でログインできなくなってしまった。君たちの手元からアプリケーションサーバには疎通がなく、踏み台サーバーを経由してsshを行う必要がある。困ったことに、アプリケーションサーバのsshd_configにはパスワード認証を許可する設定がされていない。

どうやらログインできなくなる前が新入社員が踏み台サーバーでなんらかの作業を行っていたらしい、なんとかしてアプリケーションサーバにログインできるようにしてほしい。

社内ではログイン方法がマニュアル化されているため、かならず下記コマンドでログインができるようにしてほしい

ちなみに、管理を依頼している会社は怠け者集団で月曜日までは対応ができないとのことだ。

情報

踏み台サーバ (wow_bastion)

IPアドレス: 192.168.6.1 (踏み台), 192.168.6.129 (アプリケーションネットワーク) ユーザー: admin  パスワード: n0stack/n0stack

アプリケーションサーバ (wow_target)

IPアドレス: 192.168.6.130 ユーザー: admin  パスワード: なし (公開鍵認証)

目標サーバのadminユーザーには、 踏み台サーバの /home/admin/.ssh/id_ecdsa に対応する公開鍵が設置されている。

トラブルの概要

公開鍵認証を用いたsshを行うとき、ローカルの同名ファイル(id_ecdsaとid_ecdsa.pubなど)がキーペアになってない場合に、認証がスキップされてログインできない問題。

解説

この問題は、踏み台サーバから公開鍵認証を用いたsshログインができなくなるトラブルです。

LinuxのSSHクライアントから公開鍵認証で接続する際は、 -i で公開鍵を選択するか、 ~/.ssh の配下にあるファイルが読み込まれます。 ファイルが読み込まれた際に、秘密鍵の名前に加えて .pub がついたファイルが存在していた場合、そのファイルが秘密鍵とペアである公開鍵かのチェックが行われ、一致しなかった場合はログインが出来ないという問題があります。 また、この問題は .pub ファイルが存在しない場合には行われず、接続に成功します。

踏み台サーバの作業履歴をみるとホームディレクトリのtarballが展開されて、公開鍵が上書きされていることがわかります。

想定回答としては、ssh-keygen コマンドを用いて秘密鍵から公開鍵を再生成する事を想定しています。 ssh-keygen -y -f ~/.ssh/id_ecdsa > ~/.ssh/id_ecdsa.pub

それ以外にも、 .pub ファイルが存在しない場合に接続が成功することがあるため、当該ファイルを削除しても解決可能ですが、この問題の場合は一部減点をしています。 rm ~/.ssh/id_ecdsa.pub

回答例

お疲れ様です。解答を提出いたします。

ssh admin@192.168.6.130 -vvv で踏み台のid_ecdsaのオファーが途中でスキップされていることを確認しました。

history で ~/pubkey.tar.gz が展開された形跡があることを確認しました。

ssh-keygen -l -f ~/.ssh/id_ecdsa ssh-keygen -l -f ~/.ssh/id_ecdsa.pub でこの2つのファイルがキーペアになっていないことを確認しました。

mv ~/.ssh/id_ecdsa.pub ~/.ssh/id_ecdsa.pub.old のようなコマンドで展開された公開鍵のバックアップを取りました。

ssh-keygen -yf ~/.ssh/id_ecdsa > ~/.ssh/id_ecdsa.pub でキーペアになる公開鍵を生成しました。

ssh admin@192.168.6.130 でログインできることを確認しました。

採点基準

/home/admin/.ssh 内に配置されている id_ecdsa と id_ecdsa.pub がキーペアでなくなっていることに気づく: 50%

目標サーバに /home/admin/.ssh/id_ecdsa を用いでログインができる 100%

/home/admin/.ssh/id_ecdsa から公開鍵を生成していない場合は減点

 /

問題文

ここに以前新入社員が作ったXenサーバーと、業務での検証用のVMがあります。

今年の新入社員に、Xenサーバーに入っているVMの更新を依頼し、作業を行ってもらっていましたが、

アップデートを行い再起動をしたらVMがインターネットに接続できなくなったと連絡を受けました。

このサーバーは今日は使用しませんが、新入社員の明後日以降の実習環境になるため、それまでに復旧させなければならず、

また、あなたは明日予定があり早く退社しなければならず、

明日の業務終了時間である日曜日の15:00(コンテスト終了時間)までに復旧させる必要があります。

また、新入社員の研修を兼ねているため、メンターであるあなたは今年の新入社員に「なぜダメだったのか」を説明する必要があり、

新入社員の教育につなげる必要があります。

このVMがインターネットに接続できない原因を突き止め、起動させてください。

また、その原因を説明してください。

情報

Xenの動いているサーバー

IPアドレス: 192.168.5.1

ユーザー: admin

パスワード: xenhost

OS: Ubuntu 18.04 ( Xen Hypervisor Kernel )

Xen上のゲストVM

IPアドレス: 192.168.5.100

ユーザー: root

パスワード: なし

OS: ArchLinux ( Para-Virtualization )

SSHサーバーは動作していません。ping疎通ができればOKです。

構成

UbuntuがDom-0として稼働し、ArchLinuxがDom-Uとして稼働していた

解説

この問題はXenサーバのカーネルに原因があり発生していました。
仮想マシンが使用しているカーネルのバージョンと、Xenサーバ側が仮想マシンの起動に指定しているカーネルのバージョンが一致していません。その状態でも仮想マシンの起動はできるのですが、Xenサーバが指定したカーネルのネットワークモジュールが見つからないためNICを認識せず、外部と通信できません。

解決方法は以下の3つがあります。未完

  1. arch.cfgに `bootloader = “pygrub”` を追加しkernel, ramdiskとextraを消してarchを起動
  2. ゲストマシン上で動作しているvmlinuz-linuxとinitramfs-linux.imgをXenサーバにコピーし、それを使って起動させる
  3. Xenサーバのキャッシュから圧縮されたカーネルを解凍しそれを使って起動させる

それぞれについて解説をしていきます。

まず1つ目は 起動する際に使用するDomain-Uの設定ファイルである arch.cfg に以下の操作をします。
bootloader = "pygrub" を追加
– kernel, ramdiskとextraの行を削除
その後、編集後のファイルを使って sudo xl create arch.cfg を実行し仮想マシンを起動します。そうすると起動時に仮想マシンのディスクに設置してあるブートローダーであるgrubの設定ファイルを読み、ゲストの起動時に「ゲストのディスクに実際にインストールされたカーネル」で起動することができます。
その結果、正しいカーネルモジュールを使用することができるようになり、NICが認識され外部と通信できるようになります。

編集前

name = "arch"
kernel = "/guest/vmlinuz-linux"
ramdisk = "/guest/initramfs-linux.img"
extra = "root=UUID=24225d68-7d38-41fa-b8b5-d8b6c7cfa3b9 rw"
memory = 768
hostname = "arch"
disk =  [ "phy:/guest/arch.img,xvda,w" ]
vif = [ 'mac=00:16:3e:00:00:01,bridge=xenbr0,vifname=arch' ]

編集後

name = "arch"
bootloader = "pygrub"
memory = 768
hostname = "arch"
disk =  [ "phy:/guest/arch.img,xvda,w" ]
vif = [ 'mac=00:16:3e:00:00:01,bridge=xenbr0,vifname=arch' ]

2つめは、動作している仮想マシンのディスクをXenサーバ側にマウントし、仮想マシンのカーネルをXenサーバにコピーします。仮想マシンのカーネルを使用して起動することでNICが認識され外部と通信が可能になります。以下のコマンドを順に実行すると、起動した仮想マシンがNICを認識し外部と通信が可能となります。

<br />[Xenサーバ]$ sudo mount -t ext4 -o loop,offset=1048576 /guest/arch.img /mnt

[Xenサーバ]$ sudo cp /mnt/boot/vmlinuz-linux /guest/

[Xenサーバ]$ sudo cp /mnt/boot/initramfs-linux.img /guest/

[Xenサーバ]$ sudo xl create -c arch.cfg

3つめは仮想マシン内にXenサーバが指定しているバージョンのカーネルが残っていました。それを使って起動すると同様にNICが認識され外部と通信が可能になります。以下にカーネルのバージョンを合わせて起動するコマンドを記載します。

<br />[仮想マシン]# pacman -U /var/cache/pacman/pkg/linux-4.19.1.arch1-1-x86_64.pkg.tar.xz

[仮想マシン]# reboot

講評

この問題の解答を提出した8チームのうち完全解答したチームは4チームでした。私の想定では3つの想定解のうち1個目の pygrub で解いて、他の解答はしてこないだろうと思っていたら完全解答した4チーム全てが pygrub を使わずに来たので驚きました。さらに1チームだけ仮想マシン内のキャッシュから起動させていたのでよく気づいたなと思いました。

解答数が少なかったので難しすぎたかなと思いましたが、完全解答したチームはすごいなと思いました。

ちなみに

カーネルのバージョンはfileコマンドで確認することもできます。

 /

問題文

192.168.19.10のインターフェースから192.168.19.125にICMPパケットが通らないので通してください。

192.168.19.66のインターフェースから192.168.19.140にICMPパケットが通らないので通してください。

※192.168.19.125と192.168.19.140からもICMPパケットを常に送っております。

情報

IPアドレス: 192.168.19.100 ユーザー: admin パスワード: ospf

問題のゴール状態

192.168.19.10のインターフェースから192.168.19.125にICMPパケットが通る。

192.168.19.66のインターフェースから192.168.19.140にICMPパケットが通る。

解説

トポロジーはこのようになっていました。

 

トポロジーを見て貰えれば分かりますが、ゲートウェイがサブネット外にあります。

よってeth1、eth2ともにサブネットを/27にして、eth1ではパケットにvlanタグがついているのでeth1をvlan56のインターフェースにすることで解決する問題でした。

解き方

192.168.19.125への通し方

eth1のインターフェースでsudo tcpdump -ei eth1を行うと、802.1qタグでVLAN 56がついたARP Requestを見ることができます。

そこで/etc/network/interfacesに書いてあるeth1のインターフェースをeth1.56のインターフェースにすることで、リクエストされている192.168.19.10のARPに応えることが可能となります。

しかし、ARPが来ていた192.168.19.30がサブネット外であるため、192.168.19.125から来たICMPパケットをデフォルトゲートウェイであるeth0に流してしまうので、
eth1のサブネットを/28から/27に直し、/etc/network/interfacesに書いてあるStatic routeを反映します。

これで192.168.19.125に対してICMPパケットが通ります。

192.168.19.140への通し方

eth2においてsudo tcpdump -i eth2を行うとICMP リクエストパケットが来ているがリプライを返していないことがわかります。

sudo tcpdump -i eth0 を行うと192.168.19.100であるeth0のインターフェースよりICMPのリプライを返しています。

そこで/etc/network/interfacesに書いてあるStatic routeが反映されているかip routeを確認すると反映されていないことが分かります。

/etc/network/interfacesに書いてあるStatic routeをコマンドで入力するとNetwork Unreachableと出てきます。

ゲートウェイがルーティングテーブル内にないことが分かり、Static routeのゲートウェイのIPアドレスを確認すると、eth2のサブネット外になっていることがわかります。

eth2のL2内にあるゲートウェイのIPアドレスを調べるために送られてきているICMPパケットのMACアドレスを確認し、ARPテーブルを確認するとStatic routeに書いてあるゲートウェイのIPアドレスがあるので、そのアドレスがサブネット内に来るように計算し、設定を行うとICMPが通るようになります。


ちなみにこの問題はパスワードがospfとなっていたが全く関係なく、L2の調査が出来るかどうかを問う問題でした。

 /

本問題は、 iptables の設定を間違えたように見せかけて、運用者が Docker、ないし docker-compose に詳しくなかったためにハマってしまう、というシナリオで出題した問題です。

問題文

あなたの会社では、最近会社のブログを Docker へ移行した。
ブログには WordPress を使っており、DBは MariaDB、そしてその管理用に phpMyAdmin を 8080 番ポートで動作させている。
phpMyAdmin は外部からのアクセスを防ぐため、ローカルホストからのみアクセスを許可するようにしたつもりであったが、どうやら Docker に移行した際にうまく設定が適用されなくなってしまったらしい。

上記の問題を解決してください。

アクセスに必要な情報

IPアドレス: 192.168.20.1 ユーザー: admin パスワード: bloom-into-docker

  • WordPress の管理者ID / パスワード: admin / admin
  • データベースの ID / パスワード: wordpress / wordpress

問題のゴール状態

問題文に示した意図を達成すること

 

解説

情報として与えられたサーバへログインすると、ホームディレクトリに docker-compose.yml が置かれており、問題文で説明されていたアプリケーションが Docker のコンテナオーケストレーションを行う docker-compose で設定されていたことが分かります。
また、ブラウザから 192.168.20.1 にアクセスすると WordPress で構築されたブログが表示され、8080番ポートへアクセスすると phpMyAdmin が表示されるようになっていました。

Docker の動作中コンテナを確認するコマンドである docker ps を叩くと以下のような結果が得られます。 (コンテナIDは異なる場合があります)

# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3424d4d54bae wordpress:5.0.3-php7.1-apache "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp wordpress
f195b424d1cf phpmyadmin/phpmyadmin:4.8 "/run.sh supervisord…" 5 minutes ago Up 5 minutes 9000/tcp, 0.0.0.0:8080->80/tcp phpmyadmin
b9c12504b150 mariadb:10.2.22 "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:3306->3306/tcp database

この問題のゴール状態は「問題文に示した意図を達成すること」とありますが、意図とはなんでしょうか? それは問題文に示されています。
問題文には「外部からのアクセスを防ぐため、ローカルホストからのみアクセスを許可するようにしたつもりであったが」と書かれています。
意図をエスパーして iptables -L  で iptables のルールを確認してみると、それらしいルールが登録されていることが分かります。 (dpt:http-alt に関連する2行がそうです)

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     tcp  --  localhost            anywhere             tcp dpt:http-alt
DROP       tcp  --  anywhere             anywhere             tcp dpt:http-alt
ACCEPT     tcp  --  10.0.0.0/8           anywhere             tcp dpt:ssh
ACCEPT     tcp  --  172.16.0.0/12        anywhere             tcp dpt:ssh
ACCEPT     tcp  --  192.168.0.0/16       anywhere             tcp dpt:ssh
DROP       tcp  --  anywhere             anywhere             tcp dpt:ssh

つまり、TCPの8080ポートに対するアクセスは、送信元がlocalhostからのものを許可し、それ以外は拒否するようになっています。
「どうやら Docker に移行した際にうまく設定が適用されなくなってしまった」と問題文にあるとおり、おそらく普通に Apache や nginx のような HTTPサーバをローカルで実行していればこのルールは正しく適用されます。

このルールが意図の通りに動作しないのは Docker 上のコンテナで HTTPサーバが動いていることによるものです。
詳しい説明は参考で示したリンク先を参照して頂きたいのですが、 Docker でコンテナのポートを外部に公開した場合、外部からのアクセスは iptables のDOCKERチェインへジャンプするルールがPREROUTINGチェインに追加されます。
そのため、上記のINPUTチェインに追加された 8080ポート (http-alt) に対するルールは処理されません。

さて、問題の背景が分かったところでこの問題に対処する必要がありますが、実は本問題の解答にあたり、 iptables と格闘する必要はありません。この問題のジャンルが軽量コンテナであることもヒントになっています。

そもそもなぜ外部から TCPの8080ポートへアクセスできるのかといえば、それは外部のインタフェースからのアクセスを受け付けているからに他なりません。

docker ps の結果における PORTS の列に表示されているように、 0.0.0.0:8080->80/tcp というのは、ホストが全てのIPからTCPの8080ポートでアクセスを受け付けることを意味しています。
これを拒否するようにするためには、コンテナが受け付けるIPアドレスを制限すれば良いのです。

docker-compose のコンフィグファイルのドキュメントを参照すれば、 docker-compose.yml"8080:80""127.0.0.1:8080:80" に変更することで問題が解決できることが分かります。

反映は docker-compose up -dで可能ですが、とりあえずホストを再起動しても反映されます。困ったら再起動しましょう。

また、WordPressやデータベースの認証情報はただの罠です。特に意味はありません。

別解

これは意表を突かれたのですが、 phpMyAdmin の Docker イメージは内部で nginx を使っているようで、その nginx のコンフィグを書き換えることで対処してきたチームが1チームいました。
Docker のコンテナのライフタイムや設定の柔軟性を考えるとあまり望ましくはありませんが、大会における条件は満たしていたため満点としました。

また、解決できることは分かっていましたが望ましくない解答として、 iptables と格闘することによる解答方法があります。このときは、以下のDNATのルールに着目する必要があります。

iptables -L DOCKER -t nat
Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere
DNAT       tcp  --  anywhere             anywhere             tcp dpt:mysql to:172.18.0.2:3306
DNAT       tcp  --  anywhere             anywhere             tcp dpt:http-alt to:172.18.0.3:80
DNAT       tcp  --  anywhere             anywhere             tcp dpt:http to:172.18.0.4:80

内部的には、このルールが Docker 内のコンテナへの NAT を実現しているため、このルールが適用される送信元IPをローカルホストへ絞るようにすることで本来望んでいた動作を得ることができます。
具体的な操作は述べませんが、上記の設定を修正した上で、これを永続化するようにすることができれば目的を達成できます。
しかし、 Dockerチェインのiptables のルールの操作は Docker が責任を追っており、これをユーザが弄ることは望ましい操作ではありません。事実、このルールを変更しても再起動後にはDockerのデーモンによりルールが上書きされてしまいます。
大会中の解答では dockerd が iptables を操作しないよう修正し、なんとか再起動後もルールが維持されるようにしたチームが2チームいました。
しかし、これは他のコンテナ等が存在する場合には管理が大変煩雑になるためあまり望ましくありません。

本来であればこのような解答を行うことが望ましくないと分かるよう誘導するべきであり、これは作問を行った私のミスだと考えています。

講評

採点基準として、解法の良し悪しに関係なく、ホストの再起動後も期待した動作を維持したチームには満点の200点、再起動後は期待した動作を維持できなかったチームには100点を与えました。

本戦においては、12チームが想定解法で満点となりました。また、2チームが別解により満点、1チームが別解により100点となりました。

本問題は想定していたよりも正答率がかなり高く、最近の学生は Docker が使えるのだな、と個人的には少し感動しました。
まだまだ Docker や Kubernetes は登場したてではありますが、アンケート等の結果も見ると興味を持って触っている方が多いように思いました。

この講評を書きながら、そういえば私は ICTSC5において  FreeBSD における軽量コンテナシステムである Jail の問題を出題したことを思い出しました。Dockerがここまで流行るとは思ってなかった……
皆さんも FreeBSD、そしてJail を使ってみてください。

参考

パブリックIPを持つサーバでDockerを起動するとportが全開放される問題の対処法 – grep Tips *

記事中の問題とは関係がありませんが、 Docker が iptables へ設定するルール等が詳しく説明されています。

また、出題した環境で用いられていた docker-compose.yml および永続化されていた iptables コンフィグ (/etc/iptables/rules.v4) はこちらです。 https://gist.github.com/kyontan/d96a6afbaae615b1b156844a1be261a1
問題環境を再現するためには Ubuntu Server 18.04 において netfilter-persistent および iptables-persistent を導入し、上記の iptables のルールを読み込んだ上で docker-compose up -d でコンテナを起動してもらえればと思います。

 /

問題文

/ictsc/*/etc/toracon/*を消去してしまった。 それぞれ別の手段でマシンの中にバックアップが取られているらしい。

すぐにデータを復旧してほしい。

情報

  • IPアドレス: 192.168.18.1
  • SSHユーザー: admin
  • SSHパスワード: savefile
パス バックアップツール
/ictsc snapper
/etc/toracon etckeeper

問題のゴール状態

ファイルを復旧し消去してしまう前のパスに配置する

トラブルの概要

異なるツールでバックアップしていたファイルを、それぞれ復元させる問題。

解説

/ictsc2018  

この問題はSnapperとetckeeperという2つのアプリケーションを使ってバックアップを取っていた環境で、そのファイルを誤って削除したため復元する、という問題でした。

/ictsc2018 は、snapperとbtrfs subvolumeでバックアップが取られています。 snapper undochangeを実行するか、/ictsc2018/.snapshot/1/snapshots/以下に実ファイルが存在するので、そこからコピーすることで復元ができます。

/etc/toracon

/etc/toraconは、etckeeperとgitで管理されていました。

etckeeperはGitを用いてファイルのバージョン管理を行っているため、sudo git logsudo git statusでコミットを特定した後、 sudo git checkoutすることで復元できます。
また、etckeeperのコマンドラインを利用してetckeeper vcs checkoutなどすれば、特定のファイルを復元できます。

回答例

「/ictsc2018を復元させるため、snapper status 0..1で変更点を見つけ、snapper undochange 0..1をした」

「/etc/toraconを復元させるため、git showやgit log、git diffを実行してコミットを確認し、git checkout HEAD@DSC_0745.JPGを実行した」

採点基準

  • Snapperのスナップショットから、/ictsc2018/ictsc8-story.mdを復元できる 50%
  • etckeeperのコミットから、/etc/toracon/DSC_0745.JPGを復元できる 50%

※ ictsc8-story.mdはictsc8での語彙設定、画像ファイルは猫の写真