内部NLBによるパケットの書き換えのまとめ

そういえば、内部 NLB 構成ってどうやって同一セグメントのクライアントにパケットが返ってきてるんだろ? リアルサーバーからみたときに、パケットの送信元がクライアントのになる DSR だとすると返せるのはわかるんだけど、VIP (NLBのIPアドレス) 宛のパケットを受ける設定してなくても動いてるし、NAT (DNAT) 型だとすると(クライアントが同一セグメントにいるので)戻りのパケットが NAT した NLB を経由しないんでクライアントに破棄されるだろうし??? とふと疑問を持ったので調べてみた結果です。

調べたところ、リアルサーバーに届くパケットはこんな感じでした:

  • ターゲットをインスタンスIDで指定した場合
    • src MAC = NLBのMAC
    • src IP = クライアントのIP
    • dst MAC = リアルサーバーのMAC
    • dst IP = リアルサーバーのIP
  • ターゲットをIPアドレスで指定した場合
    • src MAC = NLBのMAC
    • src IP = NLBのIP
    • dst MAC = リアルサーバーのMAC
    • dst IP = リアルサーバーのIP

まとめると:

  • 内部 NLB は NAT型 の L4LB
  • dstはリアルサーバーのMACアドレスIPアドレスに書き換えられる(DNAT)
  • srcのMACアドレスはNLBのMACアドレスに書き換えられる
    • リアルサーバーからの戻りのパケットが必ずNLBを経由するようにこうなってると思われ
    • DNATのみだと、srcはクライアントのMAC、IPになるので、リアルサーバーはNLBを経由せずに直接クライアントに戻りのパケットを送信しちゃって、クライアントは送ったのと違うところからパケットが返って来ることになるので破棄しちゃう
  • ターゲットタイプで違うのは、srcのIPアドレスのみ
  • ターゲットをインスタンスIDで指定したときの注意点

な感じでした。なるほどー

良いお年を!

MySQLで生パスワードからauthentication_stringを得る方法

SELECT PASSWORD('mypass');

mysql.user の authentication_string に格納されるのを得られるんですが、MySQL 8から PASSWORD() 関数がなくなったのでどうすればいいかというと、

SELECT CONCAT('*', UPPER(SHA1(UNHEX(SHA1('mypass')))));

こうすればいいです。

と、 @yoku0825 さんに教えてもらいました!

あるファイルがn日以上更新されてないか調べる方法

find -mtime を使う

# 最終更新から 24 時間以上経過しているか
if [[ "$(find /path/to/file -mtime +0)" != "" ]]; then
  echo 'OLD!'
else
  echo 'NEW!'
fi

-mtime +0 がなぜ「24時間以上前」になるのか?について:

-mtime n
  ファイルの最終内容更新日時が、基点となる時刻から計算して n 日前に当たれば、真を返す (訳注: 基点となる時刻は、デフォルトでは find を実行している今現在である)。
  (snip)
  なお、デフォルトの動作のように、現在時刻から数えて 24 時間前から 48 時間前までを 1 日前とする

つまり、

  • 1日前 = 24時間前から48時間前

なので、

  • n日前 = n*24時間前から(n+1)*24時間前

つまり、

-mtime 0 = 最終更新が0日前 =  0時間前から24時間前
-mtime 1 = 最終更新が1日前 = 24時間前から48時間前
-mtime 2 = 最終更新が2日前 = 48時間前から72時間前

次に n の記法について。

数値の引き数は、以下の形で指定することができる。

+n
  n より大きい。
-n
  n より小さい。
n
  ちょうど n。

なので、

  • -mtime +0
  • = -mtime 1 または -mtime 2 または ...
  • = 最終更新が0日前より過去
  • = 24時間前より過去

となる。

ほかにイケてる方法があったら教えてください!!><

これだけ覚えればOK、rsyncのディレクトリ、ファイルパスの指定方法

ディレクトリどうしをまるごとコピーしたい場合

コピー元・先両方に末尾に / をつけて、コピー先のディレクトリまで指定する。

rsync -av /path/to/dir/ remote:/path/to/dir/

rsync -av /path/to/dir remote:/path/to とか別の書き方もあるけどパット見わかりづらいので ダメ

ディレクトリの末尾に / をつけるのが肝要。

ファイルをコピーしたい場合

コピー元・先両方をファイルのパスで指定する、 もしくは、 コピー元をファイルのパスで、コピー先を末尾に / をつけたディレクトリのパスで指定する。

rsync -av /path/to/file remote:/path/to/file
もしくは
rsync -av /path/to/file remote:/path/to/

おまけ

コピー元・先でパスが同じ場合

コピー元・先でパスが同じなら -R を使うのもよい。コピー先のパス指定ミスが防げるので。

rsync -avR /path/to/dir/ remote:/
rsync -avR /path/to/file remote:/

コピー元がリモートで、複数指定する場合

rsync -av remote:/path/to/file1 remote:/path/to/file2 /path/to/dir/

と書いてもよいが、リモートホスト名は省略できる。

rsync -av remote:/path/to/file1 :/path/to/file2 /path/to/dir/

Ubuntu 18.04 で Postfix の multi instances の service unit を有効にする方法

Managing multiple Postfix instances on a single host に従い、multi instances の設定をした後、その service unit を用意する方法のメモ。

  • /lib/systemd/system-generators/* などにあるファイルはOS起動時や systemd のリロード (systemctl daemon-reload) 時に実行される
  • postfix パッケージに /lib/systemd/system-generators/postfix-instance-generator が含まれている
  • postfix-instance-generator は、 postconf -h multi_instance_directories の結果に応じて /run/systemd/generator/postfix.service.wants/postfix@postfix-XXX.service な symlink を作る
    • postmulti -I postfix-XXX -G mta -e create すると、 /etc/postfix/main.cfmulti_instance_directories/etc/postfix-XXX が追加される
  • /run/systemd/generator/postfix.service.wants/* は、 systemctl start postfix したときに一緒に start される
  • なので、systemctl start postfix で子インスタンスも起動される。stopとかrestartも同様

結論

インスタンスの設定したあとに systemctl daemon-reload すればおk

Apache が AH00144 で落ちる件

事象

  • Ubuntu 18.04
  • apache2 (2.4.29-1ubuntu4.12)

で、apache2 プロセスが次のエラーメッセージを吐いて落ちるという連絡を受けて調べました。これはその原因と対処法のメモです。

[mpm_prefork:emerg] [pid 18633] (43)Identifier removed: AH00144: couldn't grab the accept mutex
[mpm_prefork:emerg] [pid 18632] (43)Identifier removed: AH00144: couldn't grab the accept mutex
[core:alert] [pid 18624] AH00050: Child 18632 returned a Fatal error... Apache is exiting!
[:emerg] [pid 18624] AH02818: MPM run failed, exiting

原因

簡単にいうと、Apache が Mutex に使っているセマフォを systemd-logind が消してしまうのが原因です。

Apache の Mutex

Mutex ディレクティブ のデフォルトは Mutex default で、どの機構が採用されるかは APR に委ねられています。

どの機構が採用されるかはこのようにして確認できます。

$ cat mutex-default.c
#include <apr_portable.h>

int main(int argc, char** argv)
{
    printf("%s\n", apr_proc_mutex_defname());
    return 0;
}

$ gcc $(apr-config --includes) mutex-default.c $(apr-config --link-ld)
$ ./a.out
sysvsem

存在する IPC セマフォなどの確認は ipcs コマンドでできます。

Apache が起動していれば、セマフォがいくつか表示されるはずです。

$ ipcs -a
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status

------ Semaphore Arrays --------
key        semid      owner      perms      nsems
0x00000000 5111808    gyoza      600        1
0x00000000 5144577    gyoza      600        1
0x00000000 5046274    gyoza      600        1
0x00000000 5177347    gyoza      600        1
0x00000000 5210116    gyoza      600        1

systemd-logind の RemoveIPC

systemd-logind はデフォルトで RemoveIPC=yes になっています。

RemoveIPC とは logind.conf(5) によれば次のように説明されています。

RemoveIPC=
    Controls whether System V and POSIX IPC objects belonging to the user shall be removed
    when the user fully logs out. Takes a boolean argument. If enabled, the user may not
    consume IPC resources after the last of the user's sessions terminated. This covers
    System V semaphores, shared memory and message queues, as well as POSIX shared memory
    and message queues. Note that IPC objects of the root user and other system users are
    excluded from the effect of this setting. Defaults to "yes".

つまり、

  • logind 管理下のセッションが全てなくなるときに、そのユーザーの IPC セマフォなどを全て削除する
  • ただし、root とシステムユーザーは除外される

ということです。

システムユーザーとは、 uid が 999 以下のユーザーのことです。

再現方法

  • Apache を非システムユーザー (uid が 1000 以上) で起動する → セマフォが作られる)
  • その非システムユーザーで SSH ログインする
  • ログアウトする → セマフォが消される
  • Apache にリクエストが来るとプロセスが死ぬ

これで再現します。

通常は Apache はシステムユーザである www-data の権限で起動されるのでこの問題は発生しませんが、この環境では深淵な理由で非システムユーザーで起動していました。

対処法

内製のデーモンで IPC を使っていると同じようにハマりそうなので、後者の対処法を採用しました。

Apacheセマフォではない Mutex を使う

Mutex file:/var/lock/apache2 default

systemd-logind で IPC を消さないようにする

# install -d -o root -g root -m 755 /etc/systemd/logind.conf.d/

# cat << EOF > /etc/systemd/logind.conf.d/ipc.conf
[Login]
RemoveIPC=no
EOF

# systemctl restart systemd-logind.service

kernel panic 時の oops メッセージを netconsole でインターネット越しに送信する

自宅サーバー (!) がちょいちょい落ちるんです。たぶんコンソールには kernal panic 時の oops メッセージが表示されてると思うんですが、モニタは繋いでないし宅内には他にサーバーもいないのでシリアル経由で送信することもできないので確認する術がなく。

で、そういえば netconsole ってのがあったなーと思い出したんで、設定してみた次第です。

ちなみに送信先GCE の Always Freeインスタンス (ちゃんと中国とオーストラリアからのアクセスはフィルタしています) です。

疎通の確認

netconsole は UDP を使います。受信側のポート番号を決めて (6666 とします) 、まずはそれが通るようにフィルタリングの設定をします。

疎通確認はこんな感じで。

# 受信側
sudo tcpdump -i any -nlxX port 6666
# 送信側
echo konyanya-chiwa > /dev/udp/DEST_IP_ADDRESS/6666

送信側の設定

echo 'options netconsole netconsole=6665@SRC_IP_ADDRESS/eth0,6666@DEST_IP_ADDRESS/GW_MAC_ADDRESS' | sudo tee /etc/modprobe.d/netconsole.conf
echo netconsole | sudo tee -a /etc/modules

sudo modprobe netconsole

試行錯誤するときはこんな感じでおk

sudo modprobe netconsole 'netconsole=6665@SRC_IP_ADDRESS/eth0,6666@DEST_IP_ADDRESS/GW_MAC_ADDRESS'

sudo rmmod netconsole

受信側の設定

syslog は使わずに udplogger を使いました。

curl -L -o udplogger.c 'https://osdn.net/projects/akari/scm/svn/blobs/head/branches/udplogger/udplogger.c?export=raw'
gcc -Wall -o udplogger udplogger.c

mkdir /var/log/netconsole
udplogger dir=/var/log/netconsole port=6666

動作確認

送信側で

echo s | sudo tee /proc/sysrq-trigger

すると、受信側で

# /var/log/netconsole/YYYY-MM-DD.log
2020-02-21 13:25:36 192.0.2.100:6665 [271267.178610] sysrq: Emergency Sync

と出力されるはずです。

参考