2018年07月28日
_ [debian] secure apt、再掲
常時SSL化が普及する中、Chromeの新しいバージョンでhttpsでないと「保護されていない」と怒られることが多くなったそうで、そういえばftp.jp.debian.orgなどのDebianのミラーで「保護されていない」と出ると心配になる人が多そうだな、と思った。
Debianのミラーは商業CDNで配布されているわけではなく、有志のHTTP/FTPサーバで運用されている都合上、IPアドレスもサーバ設定もバラバラで、すべてをDebianプロジェクトのメンバーが直接管理しているわけでもない。そのため、「Let's Encrypt使えばいいじゃん」と簡単にはいかないし、おそらくTLSの仕組み的にもできないのではと思う。
そこで、どうDebianではパッケージの整合性を保護しているか、ということについて2006年に別の日記に書いていた古い記事を拾い出して書き直してみた。以下はJoey HessがDebian Wikiに記したSecureAPTを参考にしている。
まずは基本概念から。Debianミラーには大量のパッケージがあるが、悪意を持つ者によって改変されるのを防ぐ必要がある。信頼できる組織のミラーを使うというのも1つだが、Debianプロジェクトがすべてのミラーに目を光らせるというわけにもいかない。
Debianの各パッケージは、パッケージメンテナがビルドした時点で、そのメタ情報ファイルにパッケージ内のファイルのMD5ハッシュ値が記載される。MD5自体には同じ値で中身が異なる「コリジョン」が発見されている弱いアルゴリズムだが、メンテナが悪意を持っているのでもない限りはパッケージの範囲内でコリジョンが起こることはまずないので、ここでは特に問題にならない。
このメタ情報ファイルと展開したファイルを比較すれば、パッケージファイルの破損、あるいは何らかの不正加工は発見できる(パッケージインストール時にこれが行われる)。インストールしたときに/var/lib/dpkg/info/に「〜.md5sums」というファイルがあるはずだ。たとえばapt.md5sumsは以下のようになる。
71b58009120db0e61412f7610f27e8db lib/systemd/system/apt-daily-upgrade.service 6f1973de70bf3594436cc1a68adc441b lib/systemd/system/apt-daily-upgrade.timer 563ce75e507e1f6cc3f413e285651af5 lib/systemd/system/apt-daily.service 57b964b4d70e49baf77ca7c971358acf lib/systemd/system/apt-daily.timer 724928d79215b0e199085bbcc04ddb4d usr/bin/apt ...
次に、Packagesファイルがある。これは、大雑把に言えば、各パッケージのメタ情報ファイル内の情報(パッケージ名など)とパッケージファイル(.deb)のハッシュ値を合わせ、一覧にしてローカル側のデータベースとして使えるようにしたものだ(ソース用はSources)。通常は圧縮されているが「dists/{RELEASE}/{main/contrib/non-free}/binary-{ARCH}/Packages」という単位で存在する。
Packagesファイルは基幹となるデータベースのため、ハッシュ値に脆弱なMD5だけでは甚だ心許ない。そこで、異なるアルゴリズムでのハッシュ値も記載されている(通常ミラー側はMD5とSHA256、security.debian.orgはMD5、SHA1、SHA256)。
Package: apt Version: 1.4.8 Installed-Size: 3539 ... Size: 1231676 MD5sum: 4963240f23156b2dda3affc9c0d416a3 SHA256: bc319a3abaf98d76e7e13ac97ab0ee7c238a48e2d4ab85524be8b10cfd23d50d
apt-get updateを実行すると、このPackagesがローカルにダウンロードされ、APTデータベースによって参照されるようになる。このPackagesが「信頼できるならば」、その情報にある各パッケージのハッシュ値をもとにパッケージが「信頼できる」ことを証明できる。
次に登場するのがReleaseファイルだ。実はReleaseファイルには2種あり、dists/{RELEASE}/Releaseと、dists/{RELEASE}/{main/contrib/non-free}/binary-{ARCH}/Releaseがある。後者はそのアーカイブがstable/testing/unstableのうちどれかやリリースバージョンなどが書かれているだけだが、前者はこれ以外に配下にあるPackagesファイル(圧縮したものやSources/Releaseも含む)のハッシュ値(Packages同様に通常ミラーではMD5とSHA256、security.debian.orgではMD5、SHA1、SHA256)とサイズを記述している。だんだんややこしくなってきたが、このReleaseファイルが「信頼できるならば」、Packagesファイルを「信頼でき」、その中に書かれているパッケージも「信頼できる」ということになるわけだ。
Origin: Debian Label: Debian Suite: stable Version: 9.4 Codename: stretch Changelogs: http://metadata.ftp-master.debian.org/changelogs/@CHANGEPATH@_change log Date: Sat, 10 Mar 2018 10:21:19 UTC Acquire-By-Hash: yes Architectures: amd64 arm64 armel armhf i386 mips mips64el mipsel ppc64el s390x Components: main contrib non-free Description: Debian 9.4 Released 10 March 2018 MD5Sum: 00216699f7cd377bd5532d38c2c4a390 1181375 contrib/Contents-amd64 ... 4c8923dcd9d71154f8eb067edcd2ec25 7121632 main/binary-amd64/Packages.xz ... SHA256: ... 987a992a471a92af2bcdff2447e7604203d8b8568289edd25c34adb0486140a6 7121632 main/binary-amd64/Packages.xz
ということで、ようやくテーマであるRelase.gpgにたどりついた。dists/{RELEASE}/Release.gpgファイルは、Debianの自動FTPマネージャのGnuPG(PGP鍵暗号方式の実装の1つ)の秘密鍵を使って、dists/{RELEASE}/Releaseファイルを電子署名した結果値だ。
このGnuPG秘密鍵は、DebianプロジェクトのFTP管理者によって作成されたもので、Debianプロジェクトの極めて限定的なアクセス環境(ごく一部の選ばれたDebianプロジェクトメンバーだけがアクセスできる)の下、純粋にRelaseファイル署名のためだけに使われている。この秘密鍵に対応するGnuPG公開鍵、Releaseファイル、Release.gpgファイルの3つが揃っていれば、Releaseファイルが改ざんの行われていない、正当にFTPマネージャに署名されたものであることを証明できる。署名時から1ビットでもReleaseファイルが加工されていれば、Release.gpgの署名に対して不整合となり、エラーになる。Release.gpgを加工したらGnuPG公開鍵での署名検証に失敗し、やはりエラーとなる。
さて、残るはGnuPG公開鍵の信頼性である。ここが一番難しいところで、これが破綻したらここまでに築いてきたものが全部ダメになってしまう。公開鍵は、ftp-master.debian.org(ここはhttpsだ!)のWebサイト、およびPGPサーバーで配布されている(Stretch向けはE0B11894F66AEC98)。Debianプロジェクトの(本当に信頼できる)FTP管理者に直に話して、鍵が本物かどうか聞くのがベストではあるけれども、それは難しいので、こちらにせいぜいできるのはそのGnuPG鍵に付けられているsign(「署名」と語が似てしまうけど「この鍵は確かにこの人のものですよ」ということを示すもの。対面で会う機会にキーサインパーティなどをやる目的はこれで、友人関係を表すものではない)を見て、DebianプロジェクトFTP管理者のものだからまぁOKだろう、という判断になるだろう。自分で試すならこんな感じ。
$ gpg --keyserver pgp.mit.edu --recv-keys E0B11894F66AEC98 ←鍵をPGPサーバーから取り込み gpg: 鍵E0B11894F66AEC98: 公開鍵"Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org>"をインポートしました gpg: marginals needed: 3 completes needed: 1 trust model: classic gpg: 注意: アルゴリズム MD5 を用いた署名は拒否されました gpg: 深さ: 0 有効性: 1 署名: 149 信用: 0-, 0q, 0n, 0m, 0f, 1u gpg: 深さ: 1 有効性: 149 署名: 530 信用: 149-, 0q, 0n, 0m, 0f, 0u gpg: 次回の信用データベース検査は、2019-03-08です gpg: 処理数の合計: 1 gpg: インポート: 1 $ gpg --list-sigs E0B11894F66AEC98 ←signを見てみる elemental{kmuto}% gpg --list-sigs E0B11894F66AEC98 pub rsa4096 2017-05-22 [SC] [有効期限: 2025-05-20] E1CF20DDFFE4B89E802658F1E0B11894F66AEC98 sig R E0B11894F66AEC98 2017-05-22 Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> sig R E0B11894F66AEC98 2017-05-22 Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> sig R E0B11894F66AEC98 2017-05-22 Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> uid [ 不明 ] Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> sig E7F6F29ECED41113 2018-06-08 [ユーザIDが見つかりません] sig F14E580128030B19 2018-03-07 [ユーザIDが見つかりません] sig 11B4E5FF15B0FD82 2017-05-24 [ユーザIDが見つかりません] sig 3 7638D0442B90D010 2017-05-25 [ユーザIDが見つかりません] sig 3 9D6D8F6BC857C906 2017-05-25 [ユーザIDが見つかりません] sig 25ADF665CD2D5AEB 2017-08-05 [ユーザIDが見つかりません] sig BB0E4759B633D17C 2018-02-18 [ユーザIDが見つかりません] sig B0FBE415662EF139 2017-08-04 [ユーザIDが見つかりません] sig E553E8B2DB568A08 2018-03-26 [ユーザIDが見つかりません] sig BC372252CA1CF964 2017-05-23 [ユーザIDが見つかりません] sig 3 E0B11894F66AEC98 2017-05-22 Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> sig P DB16CF5BB12525C4 2017-05-23 [ユーザIDが見つかりません] sub rsa4096 2017-05-22 [S] [有効期限: 2025-05-20] sig E0B11894F66AEC98 2017-05-22 Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> $ gpg --keyserver keyring.debian.org --recv-keys BC372252CA1CF964 ←署名者の鍵をDebianの鍵サーバ(Debianプロジェクトの人のみが登録)から1つ取ってきてみる pg: 鍵BC372252CA1CF964: 公開鍵"Ansgar Burchardt <ansgar@debian.org>"をインポートしました gpg: marginals needed: 3 completes needed: 1 trust model: classic gpg: 注意: アルゴリズム MD5 を用いた署名は拒否されました gpg: 深さ: 0 有効性: 1 署名: 149 信用: 0-, 0q, 0n, 0m, 0f, 1u gpg: 深さ: 1 有効性: 149 署名: 531 信用: 149-, 0q, 0n, 0m, 0f, 0u gpg: 次回の信用データベース検査は、2019-03-08です gpg: 処理数の合計: 1 gpg: インポート: 1 $ gpg --list-sigs BC372252CA1CF964 ←FTPマスターの1人、Ansgarの鍵に署名している人を確認 pub rsa4096 2009-05-12 [SC] [有効期限: 2019-05-23] 80E976F14A508A48E9CA3FE9BC372252CA1CF964 uid [ 未定義 ] Ansgar Burchardt <ansgar@debian.org> sig DBBE9D4D99D2A004 2011-07-23 [ユーザIDが見つかりません] ... sig 3A936196C095D941 2011-07-27 Bdale Garbee <bdale@gag.com> ... sig 3 587979573442684E 2011-07-28 Steve McIntyre <steve@einval.com> ... sig 5D328D082AAAB140 2011-08-07 Hideki Yamane (private) <henrich@iijmio-mail.jp> ... sig 32247FBB40AD1FA6 2011-11-22 Nobuhiro Iwamatsu <iwamatsu@debian.org> ... sig 00B45EBD4CA7BABE 2015-09-04 NIIBE Yutaka <gniibe@fsij.org> ... (知っている人たちがちゃんと署名しているので信頼できそうだ!)
まとめると、FTP GnuPG公開鍵を信頼できる→Release.gpg署名を検証できる→Releaseファイルを信頼できる→Packages/Sourcesを信頼できる→Packagesのハッシュ値を信頼できる→パッケージを信頼できる→パッケージ内のファイルを信頼できる、となる。
やれやれ。ともかく、APTでの各パッケージの正当性の検証はハッシュ値とGnuPG署名にある、ということだ。
なお、apt-getなどは内部でGnuPG鍵を管理している(/etc/apt/trusted.gpg、/etc/apt/trusted.gpg.d/)が、制御するフロントエンドがapt-keyだ。
$ apt-key list /etc/apt/trusted.gpg -------------------- /etc/apt/trusted.gpg.d/debian-archive-stretch-automatic.gpg ----------------------------------------------------------- pub rsa4096 2017-05-22 [SC] [有効期限: 2025-05-20] E1CF 20DD FFE4 B89E 8026 58F1 E0B1 1894 F66A EC98 uid [ 不明 ] Debian Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> sub rsa4096 2017-05-22 [S] [有効期限: 2025-05-20] /etc/apt/trusted.gpg.d/debian-archive-stretch-security-automatic.gpg -------------------------------------------------------------------- pub rsa4096 2017-05-22 [SC] [有効期限: 2025-05-20] 6ED6 F5CB 5FA6 FB2F 460A E88E EDA0 D238 8AE2 2BA9 uid [ 不明 ] Debian Security Archive Automatic Signing Key (9/stretch) <ftpmaster@debian.org> sub rsa4096 2017-05-22 [S] [有効期限: 2025-05-20] /etc/apt/trusted.gpg.d/debian-archive-stretch-stable.gpg -------------------------------------------------------- pub rsa4096 2017-05-20 [SC] [有効期限: 2025-05-18] 067E 3C45 6BAE 240A CEE8 8F6F EF0F 382A 1A7B 6500 uid [ 不明 ] Debian Stable Release Key (9/stretch) <debian-release@lists.debian.org> ...
最後に、メンテナが新たなパッケージをDebianプロジェクトにアップロードするときにも、パッケージのビルド時にパッケージファイルのSHA1とSHA256のハッシュ値がアップロード用情報ファイルに記載される。これにメンテナのGnuPG鍵による署名を行ってからアップロードすれば、あとはDebianプロジェクトのサーバが署名およびハッシュ値の検証をして正当であれば取り込まれる。
これで完璧だ!と言いたいところだけれども、実際のところ問題がなくもない。
- (悪意の有無はともかくとして)通信途中に監視者が存在する場合、どのパッケージをダウンロードした、という情報はわかってしまう。これを避けるには、ftp.jp.debian.orgといったTLS化できない抽象名ではなく、httpsを提供しているサーバを使う。たとえば「https://dennou-k.gfd-dennou.org/debian/」はいける模様。
- Debian以外のサードパーティの鍵をapt-keyを使って登録している場合、サードパーティのリポジトリ経由でパッケージのハイジャックをされる可能性がある。たとえばサードパーティのリポジトリがクラックされて「apt」の悪意あるバージョンが配布される、という可能性はあり得る。APT pinを使えば防げなくはないけれども手間なので、信頼性に欠けるサードパーティのものはインストールを終えたらsources.listからコメントアウトしたほうがよいかもしれない。必要なパッケージのインストール時にapt-keyでの鍵登録はせず、そのときだけ「信頼しないけどインストールする」で済ませるという手もある(postinstで入れられたらどうしようもないけれども)。
- オリジナルのソフトウェア、あるいはパッケージメンテナに悪意がある場合に防げない。これは配布の問題ではないので……。