Skip to content

Instantly share code, notes, and snippets.

@otofune
Created March 6, 2023 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save otofune/236952a4dc0001e8254b6fa94dc220a9 to your computer and use it in GitHub Desktop.
Save otofune/236952a4dc0001e8254b6fa94dc220a9 to your computer and use it in GitHub Desktop.
ICTSC2022

原因・解決方法

2つあります。

1. protoc を実行するための共有ライブラリが不足している

Alpine Linux には最小限のライブラリしか入っていないため、そのままではバイナリが動作しません。 builder コンテナで ldd すると次の結果が得られます。

# ldd $(which protoc)
   	/lib64/ld-linux-x86-64.so.2 (0x7f7f183d2000)
   	libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f7f183d2000)
   	libm.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f7f183d2000)
   Error loading shared library libstdc++.so.6: No such file or directory (needed by /usr/local/bin/protoc)
   Error loading shared library libgcc_s.so.1: No such file or directory (needed by /usr/local/bin/protoc)
   	libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f7f183d2000)

このように No such file or directory が出ています。 よって libstdc++ および libgcc をインストールするとすべてリンクされた状態になりますが、まだ動作しません。

Alpine Linux の libc が musl であり、挙動の違いによって動作していないのではないかと考えられます。

よって Alpine Linux Wiki の Running glibc programs を参考に gcompat を追加すると実行できるようになります。

2. protoc-gen-go が入っておらず、実行できない

共有ライブラリを正しくインストールできているとしても、Go 向けの protoc 生成には protoc-gen-go が必要です。 例えば https://protobuf.dev/overview/ であったり Quick Start https://grpc.io/docs/languages/go/quickstart/ を見るとよいです。

手順

もともとの Dockerfile を次のように変更します。

7a8,9
> 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
11c13,14
< COPY --from=dl /download/protobuf/bin /usr/local/bin/
---
> RUN apk add --no-cache libstdc++ libgcc gcompat
> COPY --from=dl /go/bin /download/protobuf/bin /usr/local/bin/

概要

現時点の zpool が空であることを、スナップショットがなく、ファイルがないことをもって追加で確認しました。

バックアップスクリプトを見たところ、常にスナップショットを作成し、初回はスナップショット全体を、それ以降は前回のスナップショットとの差分を保存していました。 また、バックアップファイルはすべて同じ形式、すなわち openssl にて同じ鍵で暗号化され、pigz により圧縮されているようでした。 増分バックアップに欠損はないようなので、復元方法を調べたところ、zfs recv で順番に復元すればよいとわかりました。

参考文献: https://docs.oracle.com/cd/E24845_01/html/819-6260/gbchx.html

解決方法

よって次のようなスクリプトを作成し、実行しました。 zfs recv では -F(force) オプションを使うことで、必要なロールバックなどの手順を判断なしに踏んでから実行することになります。既に zpool は壊れているので、今回はこのオプションを使います。

#!/bin/bash

set -ex

dec() {
    openssl enc -iter 100 -aes-256-cbc -d -k ictsc2022
}

decomp() {
    pigz -d
}

recv() {
    # force recv
    zfs recv -F zpool
}

for snapshot in $(ls backups); do
    echo $snapshot
    read -p \"Continue?\"
    cat backups/$snapshot | dec | decomp | recv
done

実行結果は次のようになりました。

user@storage:~$ sudo ./recovery.sh
++ ls backups
+ for snapshot in $(ls backups)
+ echo 2022-12-03.enc
2022-12-03.enc
+ read -p 'Continue?'
Continue?
+ decomp
+ recv
+ zfs recv -F zpool
+ pigz -d
+ dec
+ openssl enc -iter 100 -aes-256-cbc -d -k ictsc2022
+ cat backups/2022-12-03.enc
+ for snapshot in $(ls backups)
+ echo 2022-12-03-to-2022-12-10.enc
2022-12-03-to-2022-12-10.enc
+ read -p 'Continue?'
Continue?
+ recv
+ zfs recv -F zpool
+ decomp
+ pigz -d
+ dec
+ openssl enc -iter 100 -aes-256-cbc -d -k ictsc2022
+ cat backups/2022-12-03-to-2022-12-10.enc
+ for snapshot in $(ls backups)
+ echo 2022-12-10-to-2022-12-17.enc
2022-12-10-to-2022-12-17.enc
+ read -p 'Continue?'
Continue?
+ dec
+ openssl enc -iter 100 -aes-256-cbc -d -k ictsc2022
+ decomp
+ pigz -d
+ recv
+ zfs recv -F zpool
+ cat backups/2022-12-10-to-2022-12-17.enc
+ for snapshot in $(ls backups)
+ echo 2022-12-17-to-2022-12-24.enc
2022-12-17-to-2022-12-24.enc
+ read -p 'Continue?'
Continue?
+ recv
+ zfs recv -F zpool
+ decomp
+ pigz -d
+ dec
+ openssl enc -iter 100 -aes-256-cbc -d -k ictsc2022
+ cat backups/2022-12-17-to-2022-12-24.enc
+ for snapshot in $(ls backups)
+ echo 2022-12-24-to-2022-12-31.enc
2022-12-24-to-2022-12-31.enc
+ read -p 'Continue?'
Continue?
+ recv
+ zfs recv -F zpool
+ decomp
+ pigz -d
+ dec
+ openssl enc -iter 100 -aes-256-cbc -d -k ictsc2022
+ cat backups/2022-12-24-to-2022-12-31.enc

また、復元が成功していることは次のようにスナップショットが確認できること、ファイルがあることから確認できます。

user@storage:~$ zfs list -t snapshot
NAME               USED  AVAIL     REFER  MOUNTPOINT
zpool@2022-12-03    14K      -     24.5K  -
zpool@2022-12-10    15K      -     1.03M  -
zpool@2022-12-17    16K      -     1.03M  -
zpool@2022-12-24    16K      -     1.03M  -
zpool@2022-12-31    16K      -     1.03M  -
user@storage:~$ tree -D /mnt/zpool01/
[Feb 17 17:44]  /mnt/zpool01/
├── [Dec  1 00:00]  hello_zfs.txt
├── [Feb 17 17:47]  nikki
│   ├── [Feb 17 17:45]  2022-12-09.txt
│   ├── [Feb 17 17:46]  2022-12-15.txt
│   └── [Feb 17 17:47]  2022-12-24.txt
└── [Feb 17 17:39]  photo
    ├── [Feb 17 17:38]  FishKawaii.jpeg
    └── [Feb 17 17:38]  TrackPointIsBest.jpeg

2 directories, 6 files

お世話になっております。 次の通り修正しましたので、ご確認ください。

1. s3 path-style の問題

なぜか test.192.168.19.2:9000 にアクセスし、失敗していた原因について解説します。

AWS S3 には path-style と virtual-hosted style という2つのバケット指定方法があり、既に AWS では path-style は廃止されています。 https://aws.amazon.com/jp/blogs/news/amazon-s3-path-deprecation-plan-the-rest-of-the-story/

では minio はどうかというと、デフォルトでは path-style を提供します。 https://github.com/minio/minio/tree/master/docs/config#domain

td-agent の s3 plugin のオプションを見ると、デフォルトでは false の force_path_style というものがあり、すなわち virtual-hosted style を使っていることがわかります。

これらより、/etc/td-agent/td-agent.conf の s3 オプションに force_path_style を加えます。次のように編集します。

16a17
>   force_path_style true

これにより S3 のエラーは出なくなりました。

2. ファイル権限の問題

1 が完了したあとにログを見ると次のようなエラーが出ています。

2023-03-04 15:34:34 +0900 [warn]: #0 Permission denied @ rb_sysopen - /var/log/nginx/access.log

systemd の設定を見ると、td-agent は td-agent user として動いています。 /var/log/nginx の権限を確認すると次のようになっており、td-agent user からはアクセスできないことがわかります。

$ ls -la /var/log/nginx
total 12
drwxr-xr-x  2 root     adm    4096 Mar  3 01:50 .
drwxrwxr-x 11 root     syslog 4096 Mar  4 04:29 ..
-rw-r-----  1 www-data adm     320 Mar  4 15:40 access.log
-rw-r-----  1 www-data adm       0 Mar  3 01:50 error.log

読み込み (r--) 権限のある adm グループに td-agent を加えることで、動作するようになります。

sudo gpasswd -a td-agent adm

awscli でもファイルが新しくできていることがわかります。

$ aws --profile minio --endpoint-url http://192.168.19.2:9000 s3 ls --recursive s3://test/
2023-03-01 03:36:37        129 logs/2023/03/01_0.gz
2023-03-01 03:37:43        127 logs/2023/03/01_1.gz
2023-03-01 03:41:29        162 logs/2023/03/01_2.gz
2023-03-01 03:45:13        168 logs/2023/03/01_3.gz
2023-03-04 15:41:13        161 logs/2023/03/04_0.gz

原因は2つあります。

1. enable 忘れ

deploy スクリプト内で unit を enable しておらず、自動起動の設定が有効になっていません。

2. WantedBy の設定ミス

仮に有効にしても、次のような文が出ます。

Unit /home/user/webapp/webapp.service is added as a dependency to a non-existent unit multi-user.target.

user mode の systemd には multi-user.target は存在せず、依存関係の中にないことになります。もし user mode で行うならば default.target が的確でしょう。 同様に After に指定されている network.target も存在しませんが、After に関しては同時に起動しない限りは影響を及ぼすことがないため、そのままでも問題ありません。

修正方法

まず webapp/webapp.service を次のように修正しました。

10c10
< WantedBy=multi-user.target
---
> WantedBy=default.target

次に webapp/deploy を次のように修正しました。 なおエラー時に止まるように set -e を追加していますが、これは問題としては関係のない部分です。単によりよくしているというだけです。

2a3,4
> set -e
>
5a8
> systemctl --user enable webapp

最後に webapp/deploy を実行しました。

$ ./deploy
   Created symlink /home/user/.config/systemd/user/default.target.wants/webapp.service → /home/user/webapp/webapp.service.

このあと再起動し、正常に動作することを確認しました。

原因・解決方法

2つあります。

ファイアウォールによって接続が確立できない

nc および tcpdump を使い client → server への 3306 port に対するパケットが届いているか調べましたが、届いていませんでした。 また、いくつかのランダムなポートについても届いていないことがわかりました。 これは二種類あり

  1. router によるファイアウォール
  2. server 自身 によるファイアウォール

の二段階がありました。

server で iptables -vL をしたところ ufw の設定が見えます。 一度 ufw disable して router の影響下にしても繋がらず、どちらからも制限されていることがわかります。

実際の設定は router では PC が属する 172.16.1.0/24 ネットワーク (eth1) からは限定された通信しか通さないようになっていました。 これは show configure などの実行結果からわかります。

また、server では ufw で OpenSSH 以外を受けつけない設定となっていました。

MySQL bind-address

server の ufw を停止し、router にすべての通信を通すような設定を投下し、tcpdump でパケットを確認できるようになっても connection refused のままです。

user@pc:~$ nc -vz 172.16.2.128 3306
nc: connect to 172.16.2.128 port 3306 (tcp) failed: Connection refused

これは MySQL が lo のみで待ち受けており、eth1 からの接続を受けつけないためです。 bind-address を削除あるいは変更することで、待ち受けさせることができ、晴れて nc で接続が確認できます。

user@pc:~$ nc -vz 172.16.2.128 3306
Connection to 172.16.2.128 3306 port [tcp/mysql] succeeded!

MySQL ユーザー設定

これまでの問題を解決してもすぐに user でのログインはできません。 次のようなエラーが出るためです。

ERROR 1130 (HY000): Host '172.16.1.128' is not allowed to connect to this MySQL server

server 上で現在の user を確認すると次のようになっています。

mysql> select current_user();
+----------------+
| current_user() |
+----------------+
| user@localhost |
+----------------+
1 row in set (0.00 sec)

localhost に限定されたユーザーであることがわかります。

さて、Ubuntu ではインストール時に root ユーザーにパスワードを設定するかどうか聞かれ、空である場合は auth_socket 認証 によって root ユーザーならばパスワードレスで mysql の root ユーザーとして入れるようです。 今回のサーバーでは apt の mysql-server をインストールしており、 sudo mysqlroot@localhost として MySQL に入ることができました。

これを用いて user の host を % に変更することで、host の制限を取り除くことができます。

実際に行なった変更

router

PC側 (eth1) から開始される通信のうち server へ向かう MySQL port の通信を許可するようにしました。

set firewall name client-in rule 6 action accept
set firewall name client-in rule 6 destination address 172.16.2.128
set firewall name client-in rule 6 destination port 3306
set firewall name client-in rule 6 protocol tcp_udp
commit

また、再起動後にも保持されるよう、save コマンドを実行しています。

server

MySQL bind-address

/etc/mysql/mysql.conf.d/mysqld.cnfbind-address を外し、全てのインターフェースで受けつけることにしました。 ファイアウォールは別途 ufw で設定されているため、ここを緩めてもセキュリティ上の問題はないはずです。

24d23
<
31c30
< bind-address		= 127.0.0.1
---
> #bind-address		= 127.0.0.1
ufw

既に ufw が設定されていたため、追加で 172.16.0.0/16 からの MySQL への通信を許可するようにしました。 コマンドとしては sudo ufw allow to any port 3306 from 172.16.0.0/16 を実行しています。

MySQL ユーザーの host を変更

次のように変更しました。接続元が自由になりますが、ufw にて弾かれることから問題はないはずです。

mysql> RENAME USER `user`@`localhost` TO `user`@`%`;
  Query OK, 0 rows affected (0.02 sec)

次のように解決しました。

1. rt03 の ufw が OSPF パケットを破棄している

dmesg を見ると、パケットが破棄されている様子がわかります。 今回制限したいものは incoming ではなく routed であるため、default allow にすることで接続できるようにします。 また、それに伴なって不要になる設定を削除しました。次のように設定しました。

user@rt03:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22                         ALLOW IN    192.168.0.0/16
80                         ALLOW IN    172.16.0.0/24

user@rt03:~$ sudo ufw default allow
Default incoming policy changed to 'allow'
(be sure to update your rules accordingly)
user@rt03:~$ sudo ufw delete allow to any port 22 from 192.168.0.0/16
Rule deleted
user@rt03:~$ sudo ufw delete allow to any port 80 from 172.16.0.0/24
Rule deleted
user@rt03:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: allow (incoming), allow (outgoing), deny (routed)
New profiles: skip

2. rt01 と rt02 の router-id が同じである

どちらも router-id が 1.1.1.1 となっており被っており、交換に失敗します。 次のように修正しました。

user@rt02:~$ sudo vtysh

Hello, this is FRRouting (version 8.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

rt02# configure
rt02(config)# router ospf
rt02(config-router)# ospf router-id 2.2.2.2
rt02(config-router)# exit
rt02(config)# exit
rt02# write
Note: this version of vtysh never writes vtysh.conf
Building Configuration...
Integrated configuration saved to /etc/frr/frr.conf
[OK]
rt02#

また、この設定後、すべてのルーターを再起動しています。

3. rt01 に 172.16.40.0/26 に関するstatic route が設定されている

過去に static routing をしていた関係か、rt02 を経由して 172.16.40.0/26 に向かう経路が残っていました。 次のように削除しました。

user@rt01:~$ sudo vtysh

Hello, this is FRRouting (version 8.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

rt01# configure
rt01(config)# no ip route 172.16.40.0/26 172.16.10.2
rt01(config)# exit
rt01# write
Note: this version of vtysh never writes vtysh.conf
Building Configuration...
Integrated configuration saved to /etc/frr/frr.conf
[OK]
rt01#

この時点で、rt03 の ufw を無効にした状態で正常にどちらも 2 hop になっていることを確認しています。 なお、ufw が有効の状態では traceroute の ICMP パケットが落とされるような挙動があります。(問題とは関係ないため、解決していません)

user@pc:~$ traceroute 172.16.30.2
traceroute to 172.16.30.2 (172.16.30.2), 30 hops max, 60 byte packets
 1  _gateway (172.16.0.1)  0.329 ms  0.305 ms  0.301 ms
 2  172.16.10.2 (172.16.10.2)  0.497 ms  0.497 ms  0.485 ms
 3  172.16.30.2 (172.16.30.2)  0.672 ms *  0.640 ms
user@pc:~$ traceroute 172.16.40.2
traceroute to 172.16.40.2 (172.16.40.2), 30 hops max, 60 byte packets
 1  _gateway (172.16.0.1)  0.276 ms  0.253 ms  0.246 ms
 2  172.16.200.2 (172.16.200.2)  0.481 ms  0.464 ms  0.450 ms
 3  172.16.40.2 (172.16.40.2)  0.546 ms * *

4. rt03 で routed のパケットを許可

1. で設定されている ufw では deny (routed) がデフォルトとなっています。 よって、どの端末から来たパケットもルーティングしない設定になっていました。

制約より、PC 端末のいるサブネットのみを許可する設定を次のように投下しました。

user@rt03:~$ sudo ufw route allow to any port 80 from 172.16.0.0/27
Rule added
$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: allow (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
80                         ALLOW FWD   172.16.0.0/27

これにより、pc01 からはどちらも curl でき、srv01 → srv02 は curl できない状態になりました。

原因は2つありました。

シンボリックの貼り間違い

次のように実行したとき、絶対パスには解決されないという罠があります。

ln -s 相対パス シンボリックリンク

今回、おそらく sites-available 内から作ったのであろうシンボリックリンクがあり、無限ループを引き起こして nginx が次のエラーで止まっていました。

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

よって、絶対パスによるシンボリックリンクに修正しました。 コマンドは次の通りです。

sudo ln -s /etc/nginx/sites-available/blog.example.jp /etc/nginx/sites-enabled/blog.example.jp

この時点で nginx -t は通り、nginx は正常に起動するようになっています。

proxy_pass の間違い

さて、前述の変更を行なったあと bastion から接続を試すと次のように 502 エラーになります。

blog サービスのログを見ると Host 192.168.255.9 で待ち受けていることがわかります。 よって、proxy_pass のホストを待ち受けホストに変更することで修正できます。 具体的には /etc/nginx/sites-available/blog.example.jp を次のように修正しました。

 6c6
   <     proxy_pass  http://localhost:8080;
   ---
   >     proxy_pass  http://192.168.255.9:8080;

以上の対応によって接続ができるようになりました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment