👏

Pacman: Landlock関連エラーの調査

2024/11/01に公開

What is this

Pacmanでのパッケージ更新時に出力された表題のエラーについて調査した際のメモ。
Stackable Linux Security Module(LSM)であるLandlockについても概要をまとめている。

背景

ArchLinuxを使っている中、pacmanでパッケージを更新しようとした際に以下のようなエラーメッセージが出力された。
error: restricting filesystem access failed because the landlock rule for the temporary download directory could not be added!
問題なくパッケージの更新は進んだように見えるがエラー発生の理由が判然としないため調査した。

環境

  • OS: Arch Linux on WSL2(GNU/Linux 5.15.153.1-microsoft-standard-WSL2 x86_64)
  • Pacman: Pacman v7.0.0 - libalpm v15.0.0

結論

Landlock ABIがv3未満の場合でもv3の機能をPacmanが利用してしまうことがエラーの原因だった。
現時点で修正コードが取り込まれているため、今後Pacman自体のアップデートで修正される見込み。
アップデートが来るまでは、pacmanコマンドに対して--disable-sandboxオプションを付与してエラー出力を回避できる。

調査方法

以下のようにしてエラーの原因を調査した

  • Landlockについて、そもそもどのような機能なのかドキュメントから調べた
  • pacmanでのLandlockの使用方法についてRelease noteなどから調べた
  • pacmanのどの箇所でエラーが出力されているかを調べた
  • pacman(libalpm)をビルドし直してエラーの原因を調べた

調査結果

Landlockとは

本家ドキュメントやヘッダーファイルを読んでまとめた内容を示す。
本家ドキュメントは以下の通り。

概要

Linux Kernel 5.13から導入されたサンドボックス機能で、プロセスびその子孫プロセスのシステムリソース(ファイルシステムおよびTCPポート)に関する権限を制限することができる。
Stackable Linux Security Moduleの名の通り、既存のアクセス制限設定に重ねてアクセス制限を設定できる。
重ねてアクセスを制限するのみで、他の機能(AppArmorやseccomp等)でかけたアクセス制限を緩和するものではない。
Landlockにおいて各アクセス権はルールと呼ばれそれをまとめたものがルールセットと呼ばれる。

Landlock機能の有効化確認

Landlockが有効か無効かはカーネルログに"landlock: Up and running"と出力されているか否かで判断できる。
Linux Kernelのバージョンが5.13以上なのに上記ログが出力されていない場合、カーネルビルド時にCONFIG_SECURITY_LANDLOC=yとなっていない可能性がある。

アクセスの制限方法

ここでは対象をファイルシステムに限り、ざっくりとしたアクセスの制限方法を示す。

  1. struct landlock_ruleset_attrにデフォルトで許可しないルールを列挙する
    ファイルシステムに関するルールはhandled_access_fsというメンバにビットマスクで設定する
  2. landlock_create_ruleset()に上記構造体を渡してルールセットを定義する
  3. struct landlock_path_beneath_attrに許可するルールと対象のパスを定義する
    ルールはallowed_accessというメンバにビットマスクを渡すことで設定する
    対象のパスはparent_fdというメンバにファイルディスクリプタを渡すことで設定する。
  4. landlock_add_rule()を用いてルールを適用する
  5. 必要に応じ3,4を繰り返し、複数のパスに対してルールを適用する。
  6. landlock_restrict_self()を用いて自らのプロセスにルールセットを適用する。

PacmanでのLandlockの使用方法について

パッケージ関連ファイルをダウンロードする時、
ダウンロードを行うプロセスがダウンロードディレクトリ以外の場所にファイルをダウンロードさせないようLandlockが使われている。
この仕組みはPackman v7.0.0で導入されており、Release Notesの以下記載が該当する。

  • Add DownloadUser configuation option used to drop-privileges when downloading files.
  • Download files to a temporary directory owned by DownloadUser
  • On Linux systems, ensure the download process does not write outside the download directory
  • Add DisableSandbox option and --disable-sandbox flag to disable the download write restrictions on Linux systems

pacmanでのエラーメッセージ出力箇所について

今回のエラーメッセージは pacman/lib/libalpm/sandbox_fs.cで出力されている。

$ git clone https://gitlab.archlinux.org/pacman/pacman.git -b v7.0.0 && cd pacman
$ rg 'restricting filesystem access failed because the landlock rule for the temporary download directory could not be added!' .
./lib/libalpm/sandbox_fs.c
160:            _alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the landlock rule for the temporary download directory could not be added!\n"));

libalpmをビルドしエラーの原因を調査

sandbox_fs.cを読むと、今回のエラーはlandlock_add_rule(2)の戻り値が0でなかったためにエラーが出力されている。
sandbox_fs.cを修正し、landlock_add_rule(2)の戻り値を出力させたところ22になっていた。
landlock_add_rule(2)のmanには生じうる戻り値が記載されており、22に該当するのはEINVALである。
EINVALの説明は以下の通り。

EINVAL: flags is not 0, or the rule accesses are inconsistent(i.e., rule_attr->allowed_access is not a subset of the ruleset handled accesses).

landlock_add_rule(2)に渡しているflagsは0決め打ちなので、ruleset_attr.handled_access_fsもしくは
path_beneath.allowed_accessが適切でなさそう。
実際両bitmaskを出力させてみると、以下のようにやはりpath_beneath.allowed_accessruleset_attr.handled_access_fsのサブセットになっていなかった。

struct bitmask
ruleset_attr.handled_access_fs 0001111111111111
path_beneath.allowed_access 0101111111111110

太字で示しているLANDLOCK_ACCESS_FS_TRUNCATEのフラグ(15bit目)がpath_beneath.allowed_accessで立っている。
sandbox_fs.cにおいて、landlock ABIがv3未満だとruleset_attr.handled_access_fsからLANDLOCK_ACCESS_FS_TRUNCATEのビットを消す処理がある一方でpath_beneath.allowed_accessからは消していないため不整合が起きていた。

対処

Pacmanのリポジトリでは既にMerge Requestがmasterに取り込まれているので、アップデートを待てばエラーは出力されなくなる見込み。
アップデート前に出力を抑えたい場合以下の方法がある。

  • --disable-sandboxオプションをpacmanコマンドに付与する
  • カーネルをアップデートしLandlock ABIv3を利用可能にする

補足

この節でアクセス権はビットマスクで設定すると記載した。
ここではアクセス権とビットマスクの対応を示す。

ファイルシステムフラグ

Landlockで制限可能なファイルシステムへのアクセス権は以下の通り。
landlock_ruleset_attr.handled_access_fsに渡すことでルールセットを定義する。

Name Bitmask 備考
LANDLOCK_ACCESS_FS_EXECUTE 0000000000000001
LANDLOCK_ACCESS_FS_WRITE_FILE 0000000000000010
LANDLOCK_ACCESS_FS_READ_FILE 0000000000000100
LANDLOCK_ACCESS_FS_READ_DIR 0000000000001000
LANDLOCK_ACCESS_FS_REMOVE_DIR 0000000000010000
LANDLOCK_ACCESS_FS_REMOVE_FILE 0000000000100000
LANDLOCK_ACCESS_FS_MAKE_CHAR 0000000001000000
LANDLOCK_ACCESS_FS_MAKE_DIR 0000000010000000
LANDLOCK_ACCESS_FS_MAKE_REG 0000000100000000
LANDLOCK_ACCESS_FS_MAKE_SOCK 0000001000000000
LANDLOCK_ACCESS_FS_MAKE_FIFO 0000010000000000
LANDLOCK_ACCESS_FS_MAKE_BLOCK 0000100000000000
LANDLOCK_ACCESS_FS_MAKE_SYM 0001000000000000
LANDLOCK_ACCESS_FS_REFER 0010000000000000 Landlock ABIv2から利用可能
LANDLOCK_ACCESS_FS_TRUNCATE 0100000000000000 Landlock ABIv3から利用可能
LANDLOCK_ACCESS_FS_IOCTL_DEV 1000000000000000 Landlock ABIv5から利用可能

ネットワークフラグ

Landlockで制限可能なファイルシステムへのアクセス権は以下の通り。
landlock_ruleset_attr.handled_access_netに渡すことでルールセットを定義する。

Name Bitmask 備考
LANDLOCK_ACCESS_NET_BIND_TCP 01 Landlock ABIv4から利用可能
LANDLOCK_ACCESS_NET_CONNECT_TCP 10 Landlock ABIv4から利用可能

Discussion