スクエニ ITエンジニア ブログ

Windows10+WSL2で無理矢理auditdを動かした話

はじめに

こんにちは。ネバ―・フレンズ・Tデス!

WSL2便利ですね!Windows10を利用しなければならない環境で、なんとかしてLinuxの環境を手元に置きたいという願いは人類不変の願いです。

今回はWSL2でauditdが動いてくれなかったので、無理矢理auditdを動かしてみた件を書きます。

WSL2でaudit.logを取りたいっ

WSL2のubuntu 20.04で、libseccomp2を活用したプログラムを検証していました。ここで、seccompで引っ掛かったのはどのsystem callか?を調べる必要が出てきました。

どうもこの時のログの出力はaudit系の出力先にでるそうです。では、手元のWSL2でauditdが動けば/var/log/audit/audit.logにログを集めれるかも?と思いWSL2でauditdを動かしてみました。

WSL2のubuntu20.04だとauditdは動かない…

早速auditdをWSL2のubuntu20.04で動かそうとしました。

$ uname -a
Linux your-machine 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -a
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
Codename:       focal
$ sudo service auditd start
 * Starting audit daemon auditd     [fail] 

failとありますので、動いてくれません…

CONFIG_AUDITがOFFなのが怪しい

 WSL2のubuntu20.04ではjournaldすらも動いていないので、そのままではシステムに関するログは出ません。なぜauditdが動かないのかはログに頼らず調べる必要があります。

$ sudo /sbin/auditd -n -f
Config file /etc/audit/auditd.conf opened for parsing
...中略...
distribute_network_parser called with: no
Error - audit support not in kernel
Cannot open netlink audit socket
The audit daemon is exiting.

どうもaudit用のnetlinkがWSL2で動作しているLinuxカーネルでは未サポートで開けないらしいです。裏取りします。

$ zfgrep AUDIT /proc/config.gz 
# CONFIG_AUDIT is not set
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
CONFIG_AUDIT_ARCH=y
# CONFIG_KVM_MMU_AUDIT is not set

CONFIG_AUDITのconfigがOFFでKernelが構築されていました。Kernelにあるはずのaudit機能が根こそぎOFFなのでそりゃ動かないです。

CONFIG_AUDITをONにしてもauditdは動かない

WSL2のLinux kernelの再構築は簡単で、手軽にほいほい出来てしまいます。やり方はhttps://github.com/microsoft/WSL2-Linux-KernelのBuild Instrucitonsの通りです。

ここではCONFIG_AUDITをまずは有効にして再コンパイルして起動します。make に-j $(nproc --all)つけて、CPU全部割り当てましょう!すると,「圧倒的じゃないか我が軍は…」と思えてくる速度で再構築すすむのでおすすめです!

$ sudo apt install build-essential flex bison dwarves libssl-dev libelf-dev git
$ git clone https://github.com/microsoft/WSL2-Linux-Kernel.git
$ cd WSL2-Linux-Kernel
$ git checkout -b OreOreKernel-1
$ cat >> Microsoft/config-wsl <<_HERE
CONFIG_AUDIT=y
CONFIG_NETFILTER_XT_TARGET_AUDIT=y
_HERE
$ sed -i -e '/CONFIG_LOCALVERSION/s/microsoft-standard-WSL2/&-oreore1/' Microsoft/config-wsl
$ make -j $(nproc --all) KCONFIG_CONFIG=Microsoft/config-wsl
...しばらく待つ...
$ cp arch/x86_64/boot/bzImage /mnt/c/Users/yours
$ tee /mnt/c/Users/yours/.wslconfig
[wsl2]
kernel=C:\\Users\\yours\\bzImage
# <Ctrl-D> をここで押す
$ exit
ここでpower shellを開く
> wsl --shutdown

再度WindowsTerminalを開く
$ uname -a
Linux your-machine 5.10.16.3-microsoft-standard-WSL2-oreore1 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ zfgrep AUDIT /proc/config.gz 
CONFIG_AUDIT=y
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
CONFIG_AUDITSYSCALL=y
CONFIG_AUDIT_ARCH=y
# CONFIG_KVM_MMU_AUDIT is not set
CONFIG_NETFILTER_XT_TARGET_AUDIT=y

無事CONFIG_AUDITが有効になったkernelが起動してきました。早速auditdを起動してみます。

$ sudo /usr/sbin/auditd -f -n
[sudo] password for yours: 
Config file /etc/audit/auditd.conf opened for parsing
local_events_parser called with: yes
...中略...
config_manager init complete
Error sending status request (Operation not permitted)
Error sending enable request (Operation not permitted)
type=DAEMON_ABORT msg=audit(1645762985.024:8805): op=set-enable auid=4294967295 pid=348 uid=0 ses=4294967295 res=failed
Unable to set initial audit startup state to 'enable', exiting
The audit daemon is exiting.

うぁぁぁ…動きませんねーw。一体いつから俺はCONFIG_AUDITをONにすれば動くと錯覚していた?

仕方ないからkernelを改造してauditdを動かす

ここまで来たらkernel改造して動くようにするしかありません!どこで失敗しているか?をstraceで探ります。

$ sudo strace /usr/sbin/auditd -f -n
...中略...
socket(AF_NETLINK, SOCK_RAW, NETLINK_AUDIT) = 3
...中略...
sendto(3, {len=16, type=0x3e8 /* NLMSG_??? */, flags=NLM_F_REQUEST|NLM_F_ACK, seq=1, pid=0}, 16, 0, {sa_family=AF_NETLIN
K, nl_pid=0, nl_groups=00000000}, 12) = 16
poll([{fd=3, events=POLLIN}], 1, 500)   = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, {{len=36, type=NLMSG_ERROR, flags=0, seq=1, pid=370}, {error=-EPERM, msg={len=16, type=AUDIT_GET, flags=NLM_
F_REQUEST|NLM_F_ACK, seq=1, pid=0}}}, 8988, MSG_PEEK|MSG_DONTWAIT, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000},
 [12]) = 36
recvfrom(3, {{len=36, type=NLMSG_ERROR, flags=0, seq=1, pid=370}, {error=-EPERM, msg={len=16, type=AUDIT_GET, flags=NLM_
F_REQUEST|NLM_F_ACK, seq=1, pid=0}}}, 8988, MSG_DONTWAIT, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, [12]) = 
36
write(2, "Error sending status request (Op"..., 54Error sending status request (Operation not permitted)) = 54
...中略...

AF_NETLINKのrecvfromがtype=AUDIT_GETで error=-EPERMです。Operation not permittedです。auditdの動作にkernelはお怒りです。

kernelのソースを見てみます。auditなだけに雰囲気でWSL2-Linux-Kernel/kernel/audit.cあたりだろう…と見当をつけます。

ここのあたりのつけ方を「雰囲気で」と書きました。実際慣れると、まあまあの確率ですが、「雰囲気で」あたりをつけることができるようになります。というのも、基本的に多数の人が長期間にわたり無事保守しているOSSのソースコードは、あたりをつけやすい、理解しやすいように維持されてます。なので、ファイル名から機能の在り処はある程度予想ができるようになってますし、そのうち慣れてくるとだいたい当たるようになります。

-EPERMがヒントですので、-EPERMを元にaudit.cを検索します。

$ cd WSL2-Linux-Kernel
$ less kernel/audit.c
... -EPERMで検索して眺める...
/*
 * Check for appropriate CAP_AUDIT_ capabilities on incoming audit
 * control messages.
 */
static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
{
	int err = 0;

	/* Only support initial user namespace for now. */
	/*
	 * We return ECONNREFUSED because it tricks userspace into thinking
	 * that audit was not configured into the kernel.  Lots of users
	 * configure their PAM stack (because that's what the distro does)
	 * to reject login if unable to send messages to audit.  If we return
	 * ECONNREFUSED the PAM stack thinks the kernel does not have audit
	 * configured in and will let login proceed.  If we return EPERM
	 * userspace will reject all logins.  This should be removed when we
	 * support non init namespaces!!
	 */
	if (current_user_ns() != &init_user_ns)
		return -ECONNREFUSED;

	switch (msg_type) {
	case AUDIT_LIST:
	case AUDIT_ADD:
	case AUDIT_DEL:
		return -EOPNOTSUPP;
	case AUDIT_GET:
	case AUDIT_SET:
	case AUDIT_GET_FEATURE:
	case AUDIT_SET_FEATURE:
	case AUDIT_LIST_RULES:
	case AUDIT_ADD_RULE:
	case AUDIT_DEL_RULE:
	case AUDIT_SIGNAL_INFO:
	case AUDIT_TTY_GET:
	case AUDIT_TTY_SET:
	case AUDIT_TRIM:
	case AUDIT_MAKE_EQUIV:
		/* Only support auditd and auditctl in initial pid namespace
		 * for now. */
		if (task_active_pid_ns(current) != &init_pid_ns)
			return -EPERM;

		if (!netlink_capable(skb, CAP_AUDIT_CONTROL))
			err = -EPERM;
		break;
...中略...

それらしい箇所が見つかりました。具体的にはここです。

	case AUDIT_GET:
        ...中略...
		/* Only support auditd and auditctl in initial pid namespace
		 * for now. */
		if (task_active_pid_ns(current) != &init_pid_ns)
			return -EPERM;

とても親切なコメントがあります。kernelの中では起動初期(init)のネームスペースでAUDIT_GETが要求されないと-EPERMにしちゃうぞ!という内容です。static関数であることから同じソースに呼び出し元があるはずで、audit_netlink_okを元にaudit.cを検索してみます。

...audit.cの中...
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
	u32			seq;
	void			*data;
	int			data_len;
	int			err;
	struct audit_buffer	*ab;
	u16			msg_type = nlh->nlmsg_type;
	struct audit_sig_info   *sig_data;
	char			*ctx = NULL;
	u32			len;

	err = audit_netlink_ok(skb, msg_type);
	if (err)
		return err;

ありました!関数名もaudit_receive_msgから、audit_netlink_ok呼び出して状況のチェックしてますから、そのものずばりです!

というわけで、audit_netlink_okからネームスペース確認を削っちゃえばWSL2でも動くんじゃないか?って気がしてきますよね!お前が信じる、お前を信じろ!でも、見込み外れたらとても悲しい気分になるので、一度git commitして作業を保存しておきます。

$ git commit -a
$ vi kernel/audit.c
...audit.cの中...
	case AUDIT_MAKE_EQUIV:
		/* Only support auditd and auditctl in initial pid namespace
		 * for now. */
#if 0
		if (task_active_pid_ns(current) != &init_pid_ns)
			return -EPERM;
#endif

ああっ、改造してしまいました。#if 0, #endifって便利ですねw

早速kernelを再構築します。

$ make distclean
$ sed -i -e '/CONFIG_LOCALVERSION/s/oreore1/oreore2/' Microsoft/config-wsl
$ make -j $(nproc --all) KCONFIG_CONFIG=Microsoft/config-wsl
$ cp arch/x86_64/boot/bzImage /mnt/c/Users/yours/bzImage-new
$ exit

power shellから
> wsl --shutdown
> cd \Users\yours
> del bzImage
> ren bzImage-new bzImage

再びWindowsTerminalを立ち上げる
$ uname -a
Linux your-machine 5.10.16.3-microsoft-standard-WSL2-oreore2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

改造したkernelがWSL2で起動しました。はやる気持ちを抑えつつauditdを立ちあげてみます。

$ sudo service auditd start
 * Starting audit daemon auditd                                                      [ OK ] 
$ ps -auxww | fgrep auditd
root       354  0.0  0.0  11360  1620 ?        S<sl 00:21   0:00 /sbin/auditd
yours     371  0.0  0.0   8028   724 pts/1    S+   00:21   0:00 grep -F --color=auto auditd
$ sudo less /var/log/audit/audit.log
...中略...
type=DAEMON_START msg=audit(1645802476.622:6268): op=start ver=2.8.5 format=raw kernel=5.10.74.3-microsoft-standard-WSL2
-oreore2 auid=4294967295 pid=354 uid=0 ses=4294967295 res=success
type=CONFIG_CHANGE msg=audit(1645802476.626:3): op=set audit_backlog_limit=8192 old=64 auid=4294967295 ses=4294967295 re
s=1
type=CONFIG_CHANGE msg=audit(1645802476.626:4): op=set audit_failure=1 old=1 auid=4294967295 ses=4294967295 res=1
type=CONFIG_CHANGE msg=audit(1645802476.626:5): op=set audit_backlog_wait_time=0 old=6000 auid=4294967295 ses=4294967295
 res=1
type=USER_END msg=audit(1645802476.636:6): pid=498 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:session_close granto
rs=pam_env,pam_env,pam_permit,pam_umask,pam_unix acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 r
es=success'
type=CRED_DISP msg=audit(1645802476.636:7): pid=498 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:setcred grantors=pa
m_permit acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 res=success'

うおおお、動いたーっ。

seccompのログも取れた!

今回seccompの状況をauditdで見たいというのがauditdを動かす理由なので、ここがちゃんと動かなければ意味がありません。本来起動初期(init)のネームスペースで動くべきsystem callを、無理矢理にそうじゃない状況で動かすわけですから、確認しないとですね。python3使って即席でテストプログラムを作ってテストしましょう。テストにgetpidシステムコールをseccompでブロックして結果がaudit.logで捕捉されれば、めでたしめでたし!

$ sudo apt install python3-seccomp
$ cat >test-seccomp.py <<_HERE
#!/usr/bin/env python3
import seccomp
import os
if __name__ == "__main__":
        f = seccomp.SyscallFilter(seccomp.ALLOW)
        f.add_rule(seccomp.KILL, "getpid")
        f.load()
        print(os.getpid())
_HERE 
$ chmod 755 ./test-seccomp.py
$ ./test-seccomp.py
Bad system call  #←無事getpid systemcall がブロックされる。
$ sudo ausearch --syscall getpid
----
time->Sat Feb 26 01:17:16 2022
type=SECCOMP msg=audit(1645805836.456:27): auid=4294967295 uid=1000 gid=1000 ses=4294967295 pid=953 comm="python3" exe="
/usr/bin/python3.8" sig=31 arch=c000003e syscall=39 compat=0 ip=0x7fd12a3c424b code=0x0

無事seccompにてgetpidがブロックされた記録がauditdで記録され、ausearchでみれました!

おわりに

kernelを細工することで、WSL2の元では動かないauditdを動かすことが簡単にできることが解りました。WSL2があれば、Windows10のもとでも、快適なkernel改造ライフを送れます。明日からあなたもLet’s enjoy kernel改造!

この記事を書いた人

記事一覧
SQUARE ENIXでは一緒に働く仲間を募集しています!
興味をお持ちいただけたら、ぜひ採用情報ページもご覧下さい!