「SBOM
SBOMの必要性
昨今のソフトウェアは多種多様なライブラリに依存しながら構築されています。太古のC言語のプログラムなら、シンプルなものならlibcだけ、そこそこ複雑なものでも2、3個のライブラリに依存するだけで済むことが大半でした。それが今風のプログラミング言語になると、特定の便利そうなライブラリに依存するだけで、
結果的に広く使われているライブラリで脆弱性が発生すると、入れ子的に依存しているけれどもあまり関係のないソフトウェアにまで影響を及ぼしてしまいます。最近だとJavaプログラムで広く使われているApache Log4jに脆弱性が見つかり、その影響範囲の広さから大きな問題となりました。結果的にさまざまなソフトウェアコミュニティにおいて、いろんな人から
問題の本質は、あるソフトウェアがどんなライブラリを使っているのかわかりにくい点です。各プログラミング言語のビルド環境が充実した結果、ビルド時にライブラリの依存関係等も自動的に解決してくれるようになりました。結果的にソフトウェア上で何が使われていて、何が使われるかわかりにくくなっているのです。
そこで出てくるのが
ただしソフトウェアの場合、開発周期やメンテナンス性を考えると、プロセスやツールをできるだけ自動化することが肝要です。そこで現在は様々な自動化ツールが登場しています。たとえばMicrosoftはUbuntuでも動くSBOM構築ツールであるSBOM Toolを公開しています。これはソースツリーの中身を精査して、利用しているソフトウェアのメタデータ情報をJSONファイルにまとめるものです。おそらく今後ソフトウェア開発を行うメーカーも、このようなツールを用いてSBOMを作成・
これによりSBOMを定期的に作成しておくことで、特定のソフトウェアの特定のバージョンに問題が発生したとき、それに影響を受けるのがどのバージョンなのかをすぐに把握できるようになります。結果的に必要な対応・
Ubuntuにインストールされているソフトウェアを調べる
今回は
インストール済みの環境を使用する場合、dpkg -l
」
$ dpkg -l 要望=(U)不明/(I)インストール/(R)削除/(P)完全削除/(H)保持 | 状態=(N)無/(I)インストール済/(C)設定/(U)展開/(F)設定失敗/(H)半インストール/(W)トリガ待ち/(T)トリガ保留 |/ エラー?=(空欄)無/(R)要再インストール (状態,エラーの大文字=異常) ||/ 名前 バージョン アーキテクチ 説明 +++-==========================================-==========================================-============-================================================================================ ii accountsservice 22.07.5-2ubuntu1.3 amd64 query and manipulate user account information ii acl 2.3.1-1 amd64 access control list - utilities ii acpi-support 0.144 amd64 scripts for handling many ACPI events ii acpid 1:2.0.33-1ubuntu1 amd64 Advanced Configuration and Power Interface event daemon (後略)
このdpkg -l
」dpkg-query --list
」dpkg-query --show
」
$ dpkg-query --show accountsservice 22.07.5-2ubuntu1.3 acl 2.3.1-1 acpi-support 0.144 acpid 1:2.0.33-1ubuntu1 (後略)
このように何も指定しなければパッケージ名とバージョンをタブ連結で表示してくれます。dpkg-queryのmanページにもあるように、--showformat='フォーマットリスト'
」
$ dpkg-query --show --showformat='${Package}\t${Version}\t${Homepage}\n' accountsservice 22.07.5-2ubuntu1.3 https://www.freedesktop.org/wiki/Software/AccountsService/ acl 2.3.1-1 https://savannah.nongnu.org/projects/acl/ acpi-support 0.144 acpid 1:2.0.33-1ubuntu1 http://sourceforge.net/projects/acpid2/ (後略)
これでTSV
$ dpkg-query --show --showformat='${Package}\t${Version}\t${Homepage}\n' | sed 's/\t/,/g' accountsservice,22.07.5-2ubuntu1.3,https://www.freedesktop.org/wiki/Software/AccountsService/ acl,2.3.1-1,https://savannah.nongnu.org/projects/acl/ acpi-support,0.144, acpid,1:2.0.33-1ubuntu1,http://sourceforge.net/projects/acpid2/
Debianパッケージのパッケージ名やバージョン名にタブ文字やカンマが含まれることはありませんので、エスケープの検討は不要なはずです。
ただしdpkg-queryは必ずしも現在インストールされているパッケージだけを表示するわけではありません。dpkg -l
でいうところのrc
」${Status}
」
次の例では
$ sudo apt install jq $ jq -Rsn '{ "packages": [ inputs | . / "\n" | (.[] | select(length > 0) | . / "\t") as $input | {"name": $input[0], "version": $input[1], "site": $input[2] } ] }' \ <(dpkg-query --show --showformat='${Package}\t${Version}\t${Homepage}\t${Status}\n' | grep "install ok installed") { "packages": [ { "name": "accountsservice", "version": "22.07.5-2ubuntu1.3", "site": "https://www.freedesktop.org/wiki/Software/AccountsService/" }, { "name": "acl", "version": "2.3.1-1", "site": "https://savannah.nongnu.org/projects/acl/" }, { "name": "acpi-support", "version": "0.144", "site": "" }, { "name": "acpid", "version": "1:2.0.33-1ubuntu1", "site": "http://sourceforge.net/projects/acpid2/" }, (後略)
ここからさきはこの結果を
ちなみにaptコマンドでもインストール済みのパッケージをリストアップできます。
$ apt list --installed 一覧表示... accountsservice/jammy-updates,jammy-security,now 22.07.5-2ubuntu1.3 amd64 [インストール済み、自動] acl/jammy,now 2.3.1-1 amd64 [インストール済み、自動] acpi-support/jammy,now 0.144 amd64 [インストール済み、自動] acpid/jammy,now 1:2.0.33-1ubuntu1 amd64 [インストール済み、自動] (後略)
こちらは完全に人間が読む目的で作られているため、スクリプトで操作することはあまり想定していません。
インストールされていないパッケージのメタデータについては、grep-dctrlコマンドなどが使えます。grep-dcrtrlの使い方については第312回の
ソフトウェアごとのファイルリストを調べる
インストールされているパッケージの名前やバージョンについては簡単に取得できましたが、ストレージの内容のファイルをすべて網羅しているかはこれだけではわかりません。パッケージが提供するファイルやディレクトリのリストはdpkg -L パッケージ名
」
$ dpkg -L acpi-support /. /etc /etc/acpi /etc/acpi/asus-keyboard-backlight.sh /etc/acpi/asus-wireless.sh /etc/acpi/events /etc/acpi/events/asus-keyboard-backlight-down (後略)
上記の結果を見るとわかるように、ファイルの上位ディレクトリもリストに含まれます。また、インストール時や実行時に自動生成されるファイルは含まれません。前者の例としてはパッケージのインストールスクリプトで必要に応じて作られるファイルなどsystemd enable
した結果など)、後者の例としては起動時にユーザーのホームディレクトリに作られる設定ファイルなどが該当します。これらの自動生成ファイルの元を機械的に調べるのはそこそこ難しいことだけ念頭に置いておくと良いでしょう。
実はこのファイルリストはストレージの中にファイルとして保存されています。それが/var/
」dpkg -L
」
$ cat /var/lib/dpkg/info/acpi-support.list /. /etc /etc/acpi /etc/acpi/asus-keyboard-backlight.sh /etc/acpi/asus-wireless.sh /etc/acpi/events /etc/acpi/events/asus-keyboard-backlight-down (後略)
ただし注意点としてここで使われる
$ file /var/lib/dpkg/info/zlib1g.list /var/lib/dpkg/info/zlib1g.list: cannot open `/var/lib/dpkg/info/zlib1g.list' (No such file or directory) $ file /var/lib/dpkg/info/zlib1g:amd64.list /var/lib/dpkg/info/zlib1g:amd64.list: ASCII text
dpkg-queryの場合このパッケージ名は${binary:Package}
」
ちなみにDebianパッケージでは、/var/
」
$ cat /var/lib/dpkg/info/acpi-support.conffiles /etc/acpi/asus-keyboard-backlight.sh /etc/acpi/asus-wireless.sh /etc/acpi/events/asus-keyboard-backlight-down (後略)
一般的に設定ファイルは/etc/
以下に配置されます。可変なファイルを特別扱いしたい場合は、このファイルを参考にリストアップを行うと良いでしょう。
前項で作成したsbom.
$ for p in $(dpkg-query --show --showformat='${binary:Package}\t${status}\n' | grep "install ok installed" | awk '{ print $1 }'); do cat /var/lib/dpkg/info/$p.list | jq -R . > files.json jq --arg name "$p" --slurpfile files files.json '(.packages[] | select(.name == $name) | .files) |= $files' sbom.json > sbom.json.tmp; mv sbom.json.tmp sbom.json; done $ jq '.packages[0]' sbom.json { "name": "accountsservice", "version": "22.07.5-2ubuntu1.3", "site": "https://www.freedesktop.org/wiki/Software/AccountsService/", "files": [ "/.", "/lib", "/lib/systemd", "/lib/systemd/system", "/lib/systemd/system/accounts-daemon.service", "/usr", "/usr/libexec", "/usr/libexec/accounts-daemon", "/usr/share", "/usr/share/accountsservice", "/usr/share/accountsservice/user-templates", "/usr/share/accountsservice/user-templates/administrator", "/usr/share/accountsservice/user-templates/standard", "/usr/share/dbus-1", "/usr/share/dbus-1/interfaces", "/usr/share/dbus-1/interfaces/org.freedesktop.Accounts.User.xml", "/usr/share/dbus-1/interfaces/org.freedesktop.Accounts.xml", "/usr/share/dbus-1/system-services", "/usr/share/dbus-1/system-services/org.freedesktop.Accounts.service", "/usr/share/dbus-1/system.d", "/usr/share/dbus-1/system.d/org.freedesktop.Accounts.conf", "/usr/share/doc", "/usr/share/doc/accountsservice", "/usr/share/doc/accountsservice/README.md", "/usr/share/doc/accountsservice/TODO", "/usr/share/doc/accountsservice/copyright", "/usr/share/doc/accountsservice/spec", "/usr/share/doc/accountsservice/spec/AccountsService.html", "/usr/share/language-tools", "/usr/share/language-tools/language-options", "/usr/share/language-tools/language-validate", "/usr/share/language-tools/language2locale", "/usr/share/language-tools/locale2papersize", "/usr/share/language-tools/main-countries", "/usr/share/language-tools/save-to-pam-env", "/usr/share/language-tools/set-language-helper", "/usr/share/language-tools/update-langlist", "/usr/share/polkit-1", "/usr/share/polkit-1/actions", "/usr/share/polkit-1/actions/org.freedesktop.accounts.policy", "/var", "/var/lib", "/var/lib/AccountsService", "/var/lib/AccountsService/icons", "/var/lib/AccountsService/users", "/usr/share/doc/accountsservice/changelog.Debian.gz" ] }
愚直にひとつずつ処理しているため、実行環境によってはそれなりに時間がかかりますし、sbom.
のファイルサイズも数十メガバイト程度となることでしょう。もしかするとファイルリストはjsonに含まずに別ファイルでアーカイブする形でも良いかもしれません。
もしかするとjqにもsedのinplaceのようなオプションがあるのかもしれませんが、見つけられなかったのでここではファイルをコピーしています。このあたり無理やりjqだけでやろうとしているから非効率なのであって、Python等を用いてスクリプト化すれば、もっとスマートで効率よく実施できるはずです。
ソフトウェアの個々のライセンスを調べる
ソフトウェアにはライセンスが存在します。UbuntuやDebianに含まれるソフトウェアは原則として
ソフトウェアのライセンススキャナーとしてはFOSSologyなどが有名です。FOSSologyは原則としてソースツリー
ただしcopyrightの内容にはいくつかの懸念点があります。まず最初に、このファイルは基本的にパッケージメンテナーが手動で更新しています。よって抜け漏れは起こりえますし、最新の状態に追随できていない可能性もあります。
もうひとつは
copyrightファイルは/usr/
」/usr/
」
そこで${Package}
」copyright
がdep5に対応していたらライセンスのリストを、そうでないなら
$ for p in $(dpkg-query --show --showformat='${Package}\t${status}\n' | grep "install ok installed" | awk '{ print $1 }'); do if [ -f /usr/share/doc/$p/copyright ]; then if grep -q '^Format: ' /usr/share/doc/$p/copyright; then cat /usr/share/doc/$p/copyright | grep '^License: ' | sort -u | cut -d' ' -f 2- | jq -R . > licenses.json; else echo '"no dep5"' > licenses.json; fi jq --arg name "$p" --slurpfile licenses licenses.json '(.packages[] | select(.name | startswith($name)) | .licenses) |= $licenses' sbom.json > sbom.json.tmp; mv sbom.json.tmp sbom.json; fi done $ jq '.packages[0:3] | .[] | .name, .licenses' sbom.json "accountsservice" [ "GPL-2+", "GPL-3+" ] "acl" [ "GPL-2+", "LGPL-2+" ] "acpi-support" [ "GPL-2+" ]
これでdep5に対応しているパッケージについてはライセンス情報を記録できました。ただしファイルリストと同じくこちらもあまり効率的な方法ではありません。特にライセンス情報は本来コンポーネントごとにわかれているはずなので、ただライセンスを列挙するだけでなく、コンポーネントも合わせて書くほうが正しい対応となります。このあたりも、Pythonスクリプト等を使ってより柔軟に作成できるようにすると良いでしょう。
Ubuntuのソフトウェアのバージョンと脆弱性情報
理想的には
ちなみに世の中には上記に対応した
しかしながらUbuntuの場合、上記の脆弱性の修正を次のような流れで取り込みます。たとえばUbuntu 22.
Ubuntuにおいて、個々のリリースの対応は次のようになります。
- 22.
10のパッケージのバージョンを3. 0.2 (パッケージバージョン:3. 0.2-1ubuntu-1) にあげる - 3.
0.2のうち脆弱性修正部分をパッチとして22. 04の3. 0.1に取り込み、パッケージバージョンを3. 0.1-1ubuntu1. 1とする - 2.
0.4のうち脆弱性修正部分をパッチとして20. 04の2. 0.3に取り込み、パッケージバージョンを2. 0.3-1ubuntu2. 1とする
これによりそのソフトウェアの表面上のバージョンはそれぞれ
このあたりの事情については、Ubuntu Weekly Topicsの
さて、Ubuntuの特定のパッケージが、ある脆弱性に対応しているかどうかを確認する方法はいくつか存在します。
- UbuntuのCVEレポートを検索する
- パッケージのchangelogを確認する
- ua/
proコマンドで確認する
UbuntuのCVEレポートは、Ubuntuのセキュリティチームがメンテナンスしている個々のCVEに対するUbuntuリリースごとの対応状況のまとめです。確認したいCVE番号が明らかな場合はまずはここを見ると良いでしょう。
ちなみにセキュリティ対応が行われてリリースされたパッケージは、Ubuntu Security Noticesとして通知されます。メーリングリストやRSSもあるので、定期的に情報が欲しい場合は登録しておきましょう。本連載の姉妹連載であるUbuntu Weekly Topicsでは、1週間ごとのUbuntu Security Noticesの内容をまとめていますので、日本語の情報が欲しい場合はそちらも参考になるでしょう。
Ubuntuのパッケージによっては、対応したCVE番号をパッケージのchangelogファイルに記載します。ただしこれはパッケージメンテナー次第です。パッケージによってはソフトウェアの開発元
$ apt changelog kitty kitty (0.21.2-1ubuntu0.22.04.1) jammy-security; urgency=medium * SECURITY UPDATE: an attacker controlled desktop notification escape sequence allows execution of arbitrary commands - debian/patches/CVE-2022-41322.patch: sanitizes desktop notification escape sequence - CVE-2022-41322 -- Mark Esler <mark.esler@canonical.com> Tue, 04 Oct 2022 18:46:33 -0500
最後のua/
たとえば先程のkittyの脆弱性CVE-2022-41322だと次のように確認できます。
$ pro fix CVE-2022-41322 CVE-2022-41322: kitty vulnerabilities https://ubuntu.com/security/CVE-2022-41322 No affected source packages are installed. ✔ CVE-2022-41322 does not affect your system.
そもそもkitty関連のパッケージをインストールしていない環境
$ pro fix CVE-2022-2929 CVE-2022-2929: DHCP vulnerabilities https://ubuntu.com/security/CVE-2022-2929 1 affected source package is installed: isc-dhcp (1/1) isc-dhcp: A fix is available in Ubuntu standard updates. The update is already installed. ✔ CVE-2022-2929 is resolved.
こちらはインストール済みの関連パッケージがリストアップされ
2022年10月9日時点で修正パッケージがまだ作られていないCVE-2022-42010の場合は、次のような結果になります。
$ pro fix CVE-2022-42010 CVE-2022-42010: [Unknown description] https://ubuntu.com/security/CVE-2022-42010 1 affected source package is installed: dbus (1/1) dbus: Ubuntu security engineers are investigating this issue. 1 package is still affected: dbus ✘ CVE-2022-42010 is not resolved.
バツマークとともに
Debianパッケージ以外や出力フォーマット
ここまでの流れでインストール済みのDebianパッケージの一覧およびそのファイルリストやライセンスを取得できるようになりました。実はUbuntuシステムの場合、Debianパッケージ以外にもsnapパッケージがインストールされています。snapパッケージはsnap list
」/snap/パッケージ名/current/
」
またDebianパッケージ自体も、インストール済みのファイルやバイナリは取得できるものの、この方法ではapt source パッケージ名
」
出力フォーマットも、今回はJSONベースでオリジナルのフォーマットを利用しましたが、SBOMが広く作られるようになるとフォーマットも共通化されていくことになるでしょう。標準化済みのオープンなフォーマットで目下の有力候補はSPDX
Linxuカーネルのソースコードや、Debianのcopyrightファイルのdep5においても、ライセンスの名前としてSPDXの識別子をしていできることからもわかるとおり、今後ソースコードやメタデータ内のライセンス情報の記述方法としてSPDXフォーマットが増えていくことは確実でしょう。