ICTSC2020 本選にご参加いただいた学生の皆様、お疲れ様でした。
今回のICTSC2020を以ってICTSCは活動休止となり、学生の皆様は非常に驚かれたと思います。我々運営としても、こういった活動の場が無くなってしまうのは非常に寂しいですが、ここで培った知識・技術を今後に活かしていただければと思います。
今回出題した問題の問題文、及び問題解説を公開します。
ぜひ今後の研鑽にご利用いただければと思います。
ICTSC2020 本選にご参加いただいた学生の皆様、お疲れ様でした。
今回のICTSC2020を以ってICTSCは活動休止となり、学生の皆様は非常に驚かれたと思います。我々運営としても、こういった活動の場が無くなってしまうのは非常に寂しいですが、ここで培った知識・技術を今後に活かしていただければと思います。
今回出題した問題の問題文、及び問題解説を公開します。
ぜひ今後の研鑽にご利用いただければと思います。
k8sクラスターにcert-managerをインストールしhttp01認証で証明書を発行するissuerを設定した。
その上でhost
のnginxのpodを展開するDeploymentと、それを公開するService、Ingressを記述した/home/user/manifests/web.yaml
を作成しapplyした。
しかし、なぜか証明書が発行されない。その理由と解決方法を報告してほしい。
トポロジを以下の図で示す。
host
やcert-manager
など必要なものにはルートCA証明書をインストール済みhost
のuser
ユーザーでkubectl
コマンドが利用できるhost
の/home/user/manifests
に今回applyしたmanifestが保存されているcert-manager.yaml
ingress-controller.yaml
root-ca.yaml
は変更しないことweb.yaml
の再展開(kubectl delete
, kubectl apply
など)可acme-server
には、追加でDNSサーバーが稼働しており以下のレコードが設定されているIN NS ictsc.test. IN A 192.168.20.125 ca IN A 192.168.20.125 www IN A 192.168.20.1
lb
にはkeepalivedがインストールされている。host
でcurl -L www.ictsc.test
を実行したとき以下のようなエラーになるcurl: (60) SSL certificate problem: unable to get local issuer certificate More details here: https://curl.haxx.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above.
host
でkubectl get cert
を実行したときwww-ictsc-test-tls
のREADYがFalse
になっているhost
でcurl -L www.ictsc.test
を実行したときにnginxの初期ページが表示されるhost
でkubectl get cert
を実行したときにwww-ictsc-test-tls
のREADYがTrue
になっているlb
とhost
のみSSHログインできる。
VM名 | ホスト名 | ユーザ | パスワード |
---|---|---|---|
lb | 192.168.20.1 | user | ictsc2020 |
host | 192.168.20.20 | user | ictsc2020 |
acme-server | 192.168.20.125 | ||
k8s-master | 192.168.20.129 | ||
k8s-worker1 | 192.168.20.130 | ||
k8s-worker2 | 192.168.20.131 |
cert-managerはhttp01認証を行うときにacme challengeが行えるかセルフチェックを行います。その際、www.ictsc.test
つまり192.168.20.1
宛てにGETリクエストを行いますが、このリクエストの戻りのパケットがnginxのPODから直接cert-managerのPODに送られてしまいます。
その時の送信元アドレスが、リクエストの送信時の宛先と異なるためセルフチェックのリクエストは失敗してしまい証明書の取得が行われませんでした。
以下のようにセルフチェックが失敗していることを確認できます。
user@host:~$ kubectl describe challenges.acme.cert-manager.io Name: www-ictsc-test-tls-dhksj-696946275-1037365539 Namespace: default Labels: <none> Annotations: <none> API Version: acme.cert-manager.io/v1 Kind: Challenge ... 省略 ... Status: Presented: true Processing: true Reason: Waiting for HTTP-01 challenge propagation: failed to perform self check GET request 'http://www.ictsc.test/.well-known/acme-challenge/c4mi9je6PJ87H9ulTtAPOu1xSjXEyDo9': Get "http://www.ictsc.test/.well-known/acme-challenge/c4mi9je6PJ87H9ulTtAPOu1xSjXEyDo9": dial tcp 192.168.20.1:80: connect: connection timed out State: pending ... 省略 ...
解決策としては以下のことが考えられます。
今回は、keepalivedを変更できないのでヘアピンNATの設定を行います。
/etc/sysctl.conf
に以下の設定を追記し更新します。
$ sudo vim /etc/sysctl.conf net.ipv4.vs.conntrack=1 # 最終行に追記 $ sudo sysctl -p
iptablesで内部からの通信をMASQUERADEする設定を行い永続化します。
$ sudo iptables -t nat -A POSTROUTING -s 192.168.20.128/25 -m ipvs --vaddr 192.168.20.1/32 -j MASQUERADE $ sudo netfilter-persistent save
/home/user/manifests/web.yaml
をapplyし直すとkubectl get cert
でREADYがTrue
になることを確認できます。
またcurl -L www.ictsc.test
で正常にnginxのページが表示されます。
user@host:~/manifests$ kubectl delete -f web.yaml deployment.apps "web" deleted service "web" deleted Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress ingress.extensions "a-web" deleted user@host:~/manifests$ kubectl apply -f web.yaml deployment.apps/web created service/web created Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress ingress.extensions/a-web created user@host:~/manifests$ kubectl get cert NAME READY SECRET AGE www-ictsc-test-tls True www-ictsc-test-tls 35s user@host:~/manifests$ curl -L www.ictsc.test <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
問題を解いていただきありがとうございました。
また上記のような想定解法での解答はなく、以下のような解答を頂きました。DNATの解法は、想定はしていたのですがCoreDNSは完全に想定しなかったので、確かにそういう方法もあるのかと思いました。(self checkとは?という感じではありますが、問題の終了条件を満たせるので満点としました。)
作問者のabeです. この問題は昔バックアップパーティションに救われた経験から作りました.
ベテランエンジニアの虎魂くんはWEBサーバの運用を行っていました. ある日,記憶から消えかけていたような昔のプロジェクトのお客さんから電話がありました.
どうやらWEBサーバにアクセスするとエラーページが表示されるようです. 原因の理由と解決方法を回答してください.
手元のPCから $ curl 192.168.11.1
をしてもエラーコードが帰ってくる.
$ curl 192.168.11.1
をするとステータスコード200のレスポンスが返ってくる./dev/sdb
のパーティションが壊れていることに気が付く 30%まずは終了状態の確認を行います.400番台なのでエラーが発生しています.
ictsc@web:~$ curl localhost <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> <hr><center>nginx/1.18.0 (Ubuntu)</center> </body> </html>
一応Nginxが起動しているか確認します.起動しています.
ictsc@web:~$ systemctl status nginx ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Sun 2021-03-07 13:31:36 JST; 27min ago Docs: man:nginx(8) Main PID: 585 (nginx) Tasks: 2 (limit: 1168) Memory: 11.5M CGroup: /system.slice/nginx.service ├─585 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; └─586 nginx: worker process Warning: some journal files were not opened due to insufficient permissions.
/var/log/nginx/error.log
を確認すると以下のエラーが出力されています.
2021/03/07 13:55:27 [error] 586#586: *1 directory index of "/mnt/data/" is forbidden, client: 127.0.0.1, server: _, request: "GET / HTTP/1.1", host: "localhost"
ls /mnt/data/
で確認するとディレクトリが空なのがわかります. ここでパスを見てみるとmntである事がわかります. mntディレクトリはファイルシステムをマウントする用途に使われるので/etc/fstab
を確認します.
以下のようなマウントポイントが設定されてました.
/dev/sdb1 /mnt/data ext4 defaults 0 0
対象のデバイスを確認します.
sudo gdisk /dev/sdb
するとエラーメッセージが出力されます.抜粋した以下を確認するとメインのパーティションテーブルが壊れている事がわかります. しかし,バックアップは生きているようなので復旧していきます.
Main header: ERROR Backup header: OK Main partition table: ERROR Backup partition table: OK
復旧するので1番を選択
Found invalid MBR and corrupt GPT. What do you want to do? (Using the GPT MAY permit recovery of GPT data.) 1 - Use current GPT 2 - Create blank GPT Your answer: 1
リカバリモードに入り,バックアップから復元し書き込みます.
Command (? for help): r Recovery/transformation command (? for help): c Warning! This will probably do weird things if you've converted an MBR to GPT form and haven't yet saved the GPT! Proceed? (Y/N): y Recovery/transformation command (? for help): w Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!! Do you want to proceed? (Y/N): y OK; writing new GUID partition table (GPT) to /dev/sdb. The operation has completed successfully.
マウントしてみます.
sudo mount -a
確認すると正常に復旧できた事がわかります.
ictsc@web:~$ cat /mnt/data/index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="style.css"> <title>タイトル</title> <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> </head> <body> <main> <p>ICTSC WEB!!!</p> </main> <script> $(function(){ }); </script> </body> </html>
この問題は物理ディスクを2つ使うため,自分たちで仮想基盤を運用する本戦で出そうと思ってました. 150点問題としては多くのチームに解いていただけたので嬉しかったです.
gdiskではなくtestdiskを用いて回答していたチームもいました. どちらを使用しても終了条件を満たせるので満点にしています.
来年から社会人なので,最後の作問でしたが楽しかったです.
作問者のabeです. 実はこの問題は史実に基づいて作られた問題です.
回答を見ていると私と同じ罠に引っかかったチームが居て嬉しかったです(?).
部長さんへ
新米エンジニアの捕鯨太郎です。 プロジェクトでWEBサーバを構築するとのことなので、 docker-composeを使用してWEBサーバを構築しました。 無事に起動し、WEBページも確認できたのですが、 再起動するとアクセスできなくなってしまいました。
自動的に再起動するように記述してあるので不可解です。 しかも、トラブルシュートしているといつの間にか アクセスできるようになります。自分には原因がわかりません。
お知恵を拝借したいのですが、 お時間をいただけないでしょうか。
踏み台から $ curl 192.168.17.1
をしても応答がない
再起動しても踏み台から $ curl -I 192.168.17.1
をするとステータスコード200のレスポンスが返ってくる
まずは初期状態を確認しましょう.
~ curl 192.168.17.1 curl: (7) Failed to connect to 192.168.17.1 port 80: Connection refused
WEBページにアクセスできないのが確認できます. 次にコンテナの様子をみてみましょう.
user@docker:~$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4e7db3e7cc97 nginx:latest "/docker-entrypoint.…" 3 minutes ago Up Less than a second 0.0.0.0:80->80/tcp web-server_nginx_1
あれあれ,起動しているな…
もう一度確認すると… いつの間にか復活してる!!!
~ curl 192.168.17.1 -I HTTP/1.1 200 OK Server: nginx/1.19.6 Date: Sun, 07 Mar 2021 03:33:11 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 15 Dec 2020 13:59:38 GMT Connection: keep-alive ETag: "5fd8c14a-264" Accept-Ranges: bytes
確認作業を行っているうちに復活してしまいました.
実はdockerコマンドを叩くとデーモンが起動する
という罠があります.先ほどのdocker ps -a
を見てみるとUp Less than a second
とあり,起動したてなのがわかります.
以下のコマンドで確認するとデーモンは起動していますが,enableになっていない事がわかります.
ictsc@docker:~$ systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; disabled; vendor preset: enabled) Active: active (running) since Sun 2021-03-07 12:32:53 JST; 9min ago TriggeredBy: ● docker.socket Docs: https://docs.docker.com Main PID: 2176 (dockerd) Tasks: 17 Memory: 114.7M CGroup: /system.slice/docker.service ├─2176 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock └─2362 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.18.0.2 -container-port 80 ictsc@docker:~$ systemctl is-enabled docker disabled
そう,enableのし忘れですね.以下のコマンドで解決です.
systemctl enable docker
docker
サービスをenable
できたか.
前提条件に書いた2点,docker-compose.yml
が編集可能である,docker ps -a
すると起動している, に引っかかってくれたチームがいて嬉しかったです.
冷静に確認されると一瞬で解決してしまう問題なので簡単枠で出題しました.
BGPの検証ネットワークを構築してみたところ、PC1<–>PC2間でpingが飛ばないという現象が起きました。
ただしR3実環境ではとある組織の所有物なので設定の変更はできないものとし、R3は何かしらのダイナミックのルーティングプロトコルが動作しているときいている。
PC1<–>PC2間の間で通信ができないので原因を突き止めてほしい。
手元のPCから $ ping 192.168.19.30
をしても応答がない。
PC1からPC2までpingが正しく飛ぶ。
この問題はMTUの不一致でOSPFのネイバーがR2<–>R3間で確立できておらずEBGPのピアが張れないため、PC1<–>PC2間で通信ができないという問題でした。
この問題はR3が触れないためR3のR2側のインターフェースのMTUを割り出す必要があります。
割り出しにはPCからのpingを使います。ping -s MTU値 -M do IPアドレス
を使えばIPアドレスまでの経路で指定したMTU値でpingが通れるかどうかを調べることができます。
PC1でping -s MTU値 -M do 192.168.19.26
を実行し、もし経路上のMTUより指定したMTU値が大きい場合は以下のように経路上のMTUの上限を通知してくれます。
user@PC:~$ ping -s 1450 -M do 192.168.19.26 PING 192.168.19.26 (192.168.19.26) 1450(1478) bytes of data. ping: local error: message too long, mtu=1350 ping: local error: message too long, mtu=1350 ping: local error: message too long, mtu=1350 ^C --- 192.168.19.26 ping statistics --- 3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2034ms
ここで注意すべきはpingの宛先IPアドレスでPC1からpingが通らないと思って192.168.19.26ではなく192.168.19.25に上記のオプションでpingを飛ばすと192.168.19.25のインターフェースのMTU値は見ることができません。このオプションのpingのパケットが出ていくインターフェースのMTU値しかわからないので192.168.19.26まで飛ばしてパケットを192.168.19.25から出す必要があります。
なお上記オプションでMTU値を変えて複数回pingをする場合、linuxがMTU値をキャッシュするためpingをする前にsudo ip route flush cache
を実行してキャッシュを削除する必要があります。
R3のMTU値が解ればR2のR3側のインターフェースに同じMTU値を設定すればOSPFのネイバーが確立できるのでEBGPのネイバーも張ることができ、PC1<–>PC2間でpingが通るようになります。
実際の本選参加者は上記のようなまどろっこしい解法は行わずR2のVyOS上でtcpdumpを行いOSPFのパケットの中身を見てMTU値を割り出していました。おそらくそちらのほうがメジャーなやり方ですが、tcpdumpを使えない環境等でpingでMTU値を割り出す方法も知っていると便利かと思います。
この問題は50点問題なので部分点などは与えずPC1<–>PC2間で疎通が取れるようになる解答のみに点数を与えています。