/

問題文

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

「会社の上司から勉強用にとサーバーをいただきました!
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での語彙設定、画像ファイルは猫の写真

 /

問題文

「時代はコンテナでオンプレだ!」が口癖である高圧的な上司は、最近、グローバルアドレスの持つ自宅のゲートウェイルータからフォワーディングを行うことで自宅サーバを全世界に公開しているみたいです。
ある日、上司は鬼のような形相で、「レンタルサーバーで動いていたDockerコンテナを自宅サーバに移行したらhttpが通らなくなった!pingは通るのにだぞ!!お前Docker詳しいって言ってたよな、サーバにだけsshで入れるようにしてやるから原因を究明して直してくれよ!」とあなたに無茶振りをしてきました。重ねて、「Dockerはどの環境でも動くものだろ!もともと動いてたんだからイメージに修正は加えるな!」とのことです。
上司との会話より、レンタルサーバーから自宅サーバに移行したコンテナは http://hpn.finals.ictsc へのGETリクエストが失敗するとエラー出力がされるものであり、また hpn.finals.ictsc は上司の自宅のゲートウェイルータに振られるグローバルアドレスに解決されることがわかりました。

またこの問題を先輩に相談したところ、先輩はニヤニヤした顔で「これは俺も家でサーバやるときにハマった事あるなぁ・・大きく2通りやり方があるんだが今回はRouterにログインできないから片方しか試せないかな・・・そうだ!明日の15時までに2通りの解決法を思いついた上で問題を解いたら焼き肉に連れてってやるよ!」と言ってきました。

上司の命令、そして焼き肉のために問題を解決してください。

  • 基準点
    • connection-check コンテナが、 connection successd! を返すように修正し、その解決法を上司に報告する。
  • 満点
    • 問題の解決法その2 (Routerにログインできる場合の解法) を述べ、原理を先輩に報告する。

トラブルの概要

内部から、ルータの外部アドレスを宛先とした内部への通信が成功しない

解説

グローバルアドレスを持つルータ (以下 ルータ) の内部のプライベートネットワークに存在するサーバ (以下 内部サーバ) がある環境において、外部からのルータ宛のパケットの宛先IPアドレスを内部サーバのアドレスに変換する (DNAT) 設定を行うことで、内部サーバのサービスを外部に提供できます。
ここで、ルータ宛のパケットに対しDNATのみ設定する場合、ある内部PCからルータのグローバルアドレスを宛先とした内部サーバへの通信を行うことはできません。なぜなら、内部PCから見た行きパケットの宛先アドレスと戻りパケットの送信元アドレスが一致しないためであるからです。

以下のどちらかの設定をすることでこの問題は解決します。
1. ドメイン名に対しローカルアドレスが解決されるよう設定
2. ルータにヘアピンNAT (NATループバック) の設定

サーバのみにログインできる当問題では、解法1を行うことで基準点を与えました。
解法1の具体的な例として、コンテナ起動 ( docker run ) 時に --add-host オプションを付与し hpn.finals.ictsc が内部サーバのアドレスに解決されるようにするといった方法があります。
なお、当問題の趣旨は「他人の環境を直す」ことであるため、再起動したら問題が再度起きる解法である場合減点としました。
ルータに触れられる場合の解法として、ルータに対しSNATおよびDNAT (ヘアピンNAT) の設定が必要であることを述べたチームに追加点を与えました。

この問題は、作問者が実際に自宅サーバを運用している際に起こった問題を再現しました。
作問者の家のルータにはヘアピンNATの設定項目が無かったため、内部にDNSサーバを建てた上で、内部マシン群はすべて内部DNSサーバに問い合わせることで解決しました。これにより、コンテナを起動時のオプション ( --add-host ) が必要なくなるといった利点があります。
これから自宅サーバを始める方に対して、この問題を通して得た知識が役に立てば幸いです。

回答例

  • 解法1: コンテナを一度 docker rm -f connection-check で落とした後、docker run -itd --name connection-check --add-host hpn.finals.ictsc:192.168.22.130 local/connection-check でコンテナを起動する。
  • 解法2: ヘアピンNAT (NATループバック) の設定が必要であることを述べる。

採点基準

  • 計160点
    • 基準点: 上司から与えられた問題の解決 (110点)
    • 満点: 先輩から与えられた問題の解決 (50点)