背景
どこかで聞いたようなタイトルですが・・・。
さて、DockerやSingularityなどのコンテナは、インターネット回線があれば公開リポジトリにあるイメージをどんどん持ってきて、自分の環境で利用することができます。反面、そのイメージの品質やセキュリティレベルについては自己責任です。しかし、中に何が入っているかつぶさに調べてからでないと使えないというのでは、せっかくの利便性が活きません。昨年Docker hubに登録されていたイメージの中に、仮想通貨のマイニングツールが仕込まれているものが複数見つかるという事件が起きたことから、利便性に隠れて見て見ぬふりをされてきた議論が一気に活発になりました。
誰がそのイメージを作り、その内容に責任を持つのは誰かを明らかにして、イメージを使う人がいつでも確認できるようにするにはどうしたら良いのでしょうか?本記事では、そうした機能を簡単に利用できるSingularityを題材にして、実際に試してみたいと思います。
PGP
Pretty Good Privacy という冗談のようなネーミングですが、公開鍵の考え方でファイルの暗号化とデジタル署名に広く使われています。ここでは後者のみ考えます。LinuxディストリビューションのパッケージもPGPを使って署名され、誤ったパッケージが紛れ込まないかのチェックがされています。
まず、イメージの作成者は自分の秘密鍵と公開鍵のペアを作成します。この秘密鍵は作成者によって厳重に管理され、データの暗号化に用いられます。公開鍵はその秘密鍵で暗号化された内容のみ復号が可能です。公開鍵はユーザーに渡されます。
イメージ作成者は、イメージファイルのハッシュ値を取得し、それを自分の秘密鍵で暗号化してイメージに追加します。検証する人(ユーザー)は、イメージ作成者の公開鍵を使い、ハッシュ値を復号しつつ、自分でもイメージのハッシュ値を取って比較します。
ハッシュ値が正しく復号でき、かつそれがイメージファイルのハッシュ値と同じであれば、そのイメージは正しくその秘密鍵を保持する作者が作ったものであり、1ビットの狂いもないことが証明されます。
PGPのツールとしてはオープンソースのGnuPGなど、いくつかありますが、SingularityにはPGPの仕組みでイメージファイルにサインしたり、検証したりする機能が標準で含まれています。また、公開鍵を登録するキーストアのサービスを、開発元であるSylabs.Incが提供しています。この一連の流れを試してみましょう。
鍵の作成
これからはsingularity-3.2.1を例に実行します。他のバージョンでは操作方法などが異なる可能性がありますのでご注意下さい。
鍵の作成はkey
コマンドでnewpair
オプションを用います。公開鍵と秘密鍵を同時に作るのでペアですね。
# singularity key newpair Enter your name (e.g., John Doe) : HPC Tarou Enter your email address (e.g., john.doe@example.com) : Tarou.HPC@hpc.co.jp Enter optional comment (e.g., development keys) : Sample Key for Tech-Blog Enter a passphrase : Retype your passphrase : Generating Entity and OpenPGP Key Pair...done Would you like to push it to the keystore? [Y,n] : n Done.
名前とメールアドレス、コメントを入れ、この鍵を使うためのパスフレーズを入力すればPGPの鍵ペアを作成します。最後に聞かれるのは、キーストアという検証用の公開鍵を登録しておくサーバーへアップロードするかどうかですが、現時点ではnを選んでおきます。
手元には何のファイルもできませんが、~/.singularity/sypgp/以下にpgp-public, pgp-secretというファイルにそれぞれの鍵が保存されています。
# ls -l ~/.singularity/sypgp/ -rw------- 1 root root 2214 6月 3 14:17 pgp-public -rw------- 1 root root 4824 6月 3 14:17 pgp-secret
所持しているキーの公開鍵(検証用)リストはlist
オプションで確認します。秘密鍵(署名用)は--secret
が必要です。
# singularity key list Public key listing (/root/.singularity/sypgp/pgp-public): 0) U: HPC Tarou (Sample Key for Tech-Blog) <Tarou.HPC@hpc.co.jp> C: 2019-06-03 14:17:14 +0900 JST F: 9CD55B794151B006BBFB9383C4661A3A1EDBB411 L: 4096 -------- # singularity key list --secret Private key listing (/root/.singularity/sypgp/pgp-secret): 0) U: HPC Tarou (Sample Key for Tech-Blog) <Tarou.HPC@hpc.co.jp> C: 2019-06-03 14:17:14 +0900 JST F: 9CD55B794151B006BBFB9383C4661A3A1EDBB411 L: 4096 --------
F:の項目はFinger print(指紋)といい、鍵を判別する文字列です。これが同じ鍵であれば、鍵の内容を比較しなくとも同一鍵ないし同一ペアであることがわかります。PGPではこれとは別にキーIDという名前がついています。Lはビット数です。
鍵は複数作成して使い分けることもできます。複数の鍵がある場合、番号(最初の数字、ここでは0)が振られていきます。また、不特定多数の鍵の中から特定の鍵を探したり、指定するにはFinger printを使います。
署名と検証
作成した秘密鍵を使って実際イメージに署名し、検証までをやってみましょう。イメージは先行記事で作ったLAMMPSのもの(ファイル名lammps.sif)を使います。現時点では持っている秘密鍵は一つしかないので、デフォルトでそれが使われます。
# singularity sign lammps.sif Signing image: lammps.sif Enter key passphrase : Signature created and applied to lammps.sif
パスフレーズの入力だけでサインが終わってしまいました。前後で比べてみると、署名データの分だけわずかにファイルサイズが変わっています。
# ls -l lammps.sif 署名前 -rwxr-xr-x 1 root root 75169792 6月 3 14:36 lammps.sif 署名後 -rwxr-xr-x 1 root root 75170747 6月 3 14:38 lammps.sif
では検証してみましょう。verify
コマンドを使います。
# singularity verify lammps.sif Verifying image: lammps.sif INFO: Container is signed Data integrity checked, authentic and signed by: HPC Tarou (Sample Key for Tech-Blog) <Tarou.HPC@hpc.co.jp>, Fingerprint 9CD55B794151B006BBFB9383C4661A3A1EDBB411
確かに先程作成した鍵で署名されていることと、データが改ざんされていないことの両方がわかりました。内部的には手元に持っている公開鍵を用いてこの検証を行っています。つまり、イメージの作成者の公開鍵を手元にストックしておき、ペアとなっている秘密鍵の所有者によって署名されたままのイメージか検証を行うことができます。そのため、例えば不特定多数の利用者がいるイメージを配布する場合、作成者は公開鍵をインターネット上の信頼できるキーストアなどで公開しておく必要があります。
他の公開鍵の取得とイメージの検証
では他人が作成した公開鍵を取得して手元におき、イメージの検証をするところまでやってみましょう。Sylabsがテストで公開している署名入りイメージを持ってきます。
# singularity pull library://sylabs/tests/signed:1.0.0 INFO: Downloading library image 660.93 KiB / 660.93 KiB [=====================================================================================] 100.00% 58.40 KiB/s 11s INFO: Key with ID EDECE4F3F38D871E not found in local keyring, downloading from keystore... INFO: Container is signed Data integrity checked, authentic and signed by: Sylabs Admin <support@sylabs.io>, Fingerprint 8883491F4268F173C6E5DC49EDECE4F3F38D871E WARNING: no local key matching entity
手元には公開鍵が見当たらなかったため、Sylabsのキーストアから公開鍵をダウンロードして検証しています。インターネットアクセスができない環境にこのイメージを持っていってverify
すると、以下のように怒られます。手元にある公開鍵で検証するには--local
オプションを用いますが、公開鍵を持っていなければ検証のしようがありません。
# singularity verify signed_1.0.0.sif FATAL: Unable to get library service URI: error making request to server: # singularity verify --local signed_1.0.0.sif Verifying image: signed_1.0.0.sif FATAL: unable to verify container: openpgp: signature made by unknown entity
このイメージに署名した鍵の公開鍵をキーストアから持ってきましょう。何らかの方法で、このイメージを署名した鍵のフィンガープリントを手に入れ、そのフィンガープリントを用いて公開鍵をキーストアからダウンロードします。取得後はlist
コマンドで公開鍵がID1で追加されているのが確認できます。
# singularity key pull 8883491F4268F173C6E5DC49EDECE4F3F38D871E 1 key(s) added to keyring of trust /root/.singularity/sypgp/pgp-public # singularity key list Public key listing (/root/.singularity/sypgp/pgp-public): 0) U: HPC Tarou (Sample Key for Tech-Blog) <Tarou.HPC@hpc.co.jp> C: 2019-06-03 14:17:14 +0900 JST F: 9CD55B794151B006BBFB9383C4661A3A1EDBB411 L: 4096 -------- 1) U: Sylabs Admin () <support@sylabs.io> C: 2018-11-02 07:20:02 +0900 JST F: 8883491F4268F173C6E5DC49EDECE4F3F38D871E L: 4096 --------
この公開鍵で検証ができればいいので、--local
で試してみましょう。また、検証が済んで手元に公開鍵を置いておく必要なくなれば、remove
コマンドで削除して整理することができます。
# singularity verify --local signed_1.0.0.sif Verifying image: signed_1.0.0.sif INFO: Container is signed Data integrity checked, authentic and signed by: Sylabs Admin <support@sylabs.io>, Fingerprint 8883491F4268F173C6E5DC49EDECE4F3F38D871E # singularity remove 8883491F4268F173C6E5DC49EDECE4F3F38D871E # singularity key list Public key listing (/root/.singularity/sypgp/pgp-public): 0) U: HPC Tarou (Sample Key for Tech-Blog) <Tarou.HPC@hpc.co.jp> C: 2019-06-03 14:17:14 +0900 JST F: 9CD55B794151B006BBFB9383C4661A3A1EDBB411 L: 4096 --------
複数の署名
書類のハンコリレーが好きな人はいないでしょうが、作成者だけでなく、品質保証担当者、受け入れ検査担当者などからもイメージにサインをもらっておきたいという需要は理解できます。PGPの署名はイメージ自体は全く変更せず(暗号化もしていません)ハッシュ値の受け渡しにのみ公開鍵認証を用いています。そのため、1つのファイルに署名を複数付けることが容易です。先程取得したSylabs製イメージに、自分で受け入れ署名を入れてみましょう。
# singularity sign signed_1.0.0.sif Signing image: signed_1.0.0.sif Enter key passphrase : Signature created and applied to signed_1.0.0.sif # singularity verify signed_1.0.0.sif Verifying image: signed_1.0.0.sif INFO: Container is signed Data integrity checked, authentic and signed by: Sylabs Admin <support@sylabs.io>, Fingerprint 8883491F4268F173C6E5DC49EDECE4F3F38D871E HPC Tarou (Sample Key for Tech-Blog) <Tarou.HPC@hpc.co.jp>, Fingerprint 9CD55B794151B006BBFB9383C4661A3A1EDBB411
きちんと2つの署名が表示されました。2つ目の署名は1つ目の署名がついた(サイズが変わっていたことを思い出して下さい)ファイルに対するハッシュ値をベースに署名が作成されるため、順番も正しく確認ができるわけです。署名を傾けてお辞儀をさせる必要もありません。
まとめ
いかがでしょうか?コンテナに限った話ではありませんが、アプリケーションやサーバー環境がブラックボックスのようになってしまい、中身について問題があった場合や、セキュリティをどう担保するのかという話は、単にアプリケーションを使いたいだけのユーザーとしては、できるだけ避けて通りたい話かもしれません。しかし、こうした対策は特にエンタープライズ用途で使うためには必須の枠組みです。導入しているパッケージやコンテナが誰が作成したのか、きちんと追えるトレーサビリティをどう確保するのか、利便性を損なわず安心して使える環境を担保する仕組みを、どう標準化していくのか、今も真剣な議論が続いてます。
セキュリティは性悪説で確保する必要がありますが、疑心暗鬼でおっかなびっくりアプリを使わなくてはならないのでは、精神衛生上よろしくありません。Singularityのアプリ自体についても、誰がビルドしたか分からない(つまり改変が入っていても確認できない)ものではなく、きちんとSylabsInc.の署名付きのバイナリを使うことのできる、有償サポート付きSyngularityProもあります。興味のある方は問い合わせフォームからご連絡願います。