Ubuntu Weekly Recipe

第443回 再起動なしにカーネルを更新する「Canonical Livepatch Service」

この記事を読むのに必要な時間:およそ 7 分

Livepatch Serviceの初期設定

Livepatch Serviceを使うためには,環境によってはあらかじめ初期設定を行う必要があります。たとえばプロキシ経由でアクセスする場合やセキュアブートを利用している場合がそれに該当します。

Livepatch Serviceでは「livepatch.canonical.com:443」へアクセスする必要があります。プロキシ経由で接続する必要がある場合は,環境にあわせて次のような/var/snap/canonical-livepatch/configファイルを作成してください。

http-proxy: "http://proxy.example.co.jp:8080"
https-proxy: "https://proxy.example.co.jp:8080"
no-proxy: "example.co.jp"

設定を行ったら,canonical-livepatchdサービスを再起動します。

$ sudo systemctl restart snap.canonical-livepatch.canonical-livepatchd.service

Livepatch Serviceではカーネルモジュールをダウンロードしてカーネルに取り込みます。セキュアブート環境においてはロードするカーネルモジュールも検証の対象になります。つまりこのモジュールの署名検証用の証明書をあらかじめ取り込んでおく必要があります。セキュアブート環境はまず,以下のコマンドを実行した上で,再起動してください。

$ sudo mokutil --import /snap/canonical-livepatch/current/keys/livepatch-kmod.x509

Livepatch Serviceを有効化する

一通りの設定が終わったら,canonical-livepatchコマンドで,Livepatch Serviceの機能を有効化しましょう。

$ sudo canonical-livepatch enable トークン
Successfully enabled device. Using machine token: マシントークン
$ canonical-livepatch status --verbose
client-version: "5"
machine-id: マシンID
machine-token: マシントークン
architecture: x86_64
cpu-model: Intel Xeon E3-12xx v2 (Ivy Bridge)
last-check: 2016-10-22T20:08:49.442817865+09:00
boot-time: 2016-10-22T19:43:15+09:00
uptime: 27m20s
status:
- kernel: 4.4.0-45.66-generic
  running: true
  livepatch:
    state: nothing-to-apply
    version: ""
    fixes: ""

statusサブコマンドを実行すると,現在の状態が表示されます。この記事を執筆している時点での最新カーネルパッケージである「4.4.0-45.66」には,緊急度の高い修正パッチは提供されていないため,⁠nothing-to-apply」となっています。そこで,一つ前のカーネルバージョンに変更して起動してみましょう。まず,古いカーネルパッケージをインストールした上で,再起動します。

$ sudo apt install linux-image-4.4.0-43-generic linux-image-extra-4.4.0-43-generic

再起動するときにGRUBの画面で「Advanced options for Ubuntu」を選択し,そこから「Ubuntu, with Linux 4.4.0-43-generic」を選択してください。これで「4.4.0-43.63」カーネルになるはずです。この状態で再びstatusサブコマンドを実行します。

$ uname -a
Linux ubuntu-desktop 4.4.0-43-generic #63-Ubuntu SMP Wed Oct 12 13:48:03 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$ canonical-livepatch status --verbose
client-version: "5"
machine-id: マシンID
machine-token: マシントークン
architecture: x86_64
cpu-model: Intel Xeon E3-12xx v2 (Ivy Bridge)
last-check: 2016-10-22T20:30:04.652+09:00
boot-time: 2016-10-22T20:29:30+09:00
uptime: 45s
status:
- kernel: 4.4.0-43.63-generic
  running: true
  livepatch:
    state: applied
    version: "13.3"
    fixes: '* CVE-2016-5195 LP: #1633547'

どうやらCVE-2016-5195対応のパッチが適用されているようです。実際にlsmodを実行すると,Livepatchのモジュールをロードしていることがわかります。

$ lsmod | grep livepatch
kpatch_livepatch_Ubuntu_4_4_0_43_63_generic_13    16384  1

カーネルモジュールは/var/snap以下にダウンロードされます。

$ sudo ls /var/snap/canonical-livepatch/common/payload/
changelog  copyright  cvelist.yaml  livepatch_Ubuntu_4_4_0_63_generic_13.ko  meta

Dirty COW対応を検証する

CVE-2016-5195はいわゆるDirty COWとも呼ばれる問題で,COW(copy-on-write)の操作時に競合状態が発生し,本来書き込めないはずの領域に書き込めてしまうという問題です。すでにこの脆弱性に対する実証コードはいくつも公開されており,これを使えばローカル環境でも問題を再現できます。ためしに書き込み可能フラグを落としているはずのファイルに一般ユーザーが書き込めてしまうdirtyc0w.cを試してみましょう。

$ wget https://raw.githubusercontent.com/dirtycow/dirtycow.github.io/master/dirtyc0w.c
$ sudo -s
$ echo this is not a test | sudo tee foo
$ sudo chown root: foo
$ sudo chmod 0404 foo
$ ls -lah foo
-r-----r-- 1 root root 19 Oct 22 18:03 foo
$ cat foo
this is not a test
$ gcc -pthread dirtyc0w.c -o dirtyc0w
$ ./dirtyc0w foo m00000000000000000
mmap 7f3cf7423000

madvise 0

procselfmem 1800000000

$ cat foo
this is not a test

dirtyc0wコマンドは,指定したファイルをmmap()し2つのスレッドを立ち上げ,片方ではひたすら/proc/self/mem経由でmmap()したアドレスに書き込みを行い,もう片方はmadvice()mmap()したアドレスとMADV_DONTNEEDを渡し続けるというコードです。いずれも1億回同じ処理を繰り返すことで,どこかのタイミングで競合状態を発生させ,書き込みを成功させようとします。1億回繰り返すので,環境によってコマンドの完了までに相当時間がかかります。

脆弱性の存在するはずの4.4.0-43カーネルであるにも関わらず,Livepatch Serviceのカーネルモジュールをロードしているおかげで,fooファイルの内容は書き換えられていません。これがLivepatch Service導入前であれば,本来変更できないはずのfooの内容がdirtyc0wコマンドに渡したm00000000000000000になってしまいます。

Livepatch Serviceを無効化する

現在のところ,一度ロードしたLivepatchモジュールをアンロードする方法はありませんrmmodコマンドもエラーとなります。

$ sudo rmmod kpatch_livepatch_Ubuntu_4_4_0_43_63_generic_13
rmmod: ERROR: Module kpatch_livepatch_Ubuntu_4_4_0_43_63_generic_13 is in use

現時点で無効化する唯一の方法は,canonical-livepatchコマンドからCanonical Livepatchのサービスそのものを停止することです。

$ sudo canonical-livepatch disable
Successfully disabled device. Removed machine-token: マシントークン

この状態で再起動すると,次回からはLivepatchがロードされなくなります。クライアントのソースが公開されていないため確実なことはわかりませんが,これによりマシントークンが一個なくなり,再び3台のマシンでLivepatch Serviceを有効化できるものと思われます。

当然のことながらsudo apt update && sudo apt upgradeでカーネルパッケージを更新した上で再起動すれば,不要となったLivepatchモジュールはロードされなくなります。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。

コメント

コメントの記入