みんなで管理するapt/yumリポジトリに署名するキーの作り方 - 2012-05-09 - ククログ

ククログ

株式会社クリアコード > ククログ > みんなで管理するapt/yumリポジトリに署名するキーの作り方

みんなで管理するapt/yumリポジトリに署名するキーの作り方

注: セキュリティーの専門家が書いた文章ではありません。セキュリティー面で心配のある方は専門家に相談することをお勧めします。

複数人で管理しているapt/yumリポジトリの署名用GPGキーをどうやって作って運用するのがよいかという案とその具体的な実現方法を紹介します。

背景

groongamilter managnerは最新バージョンをすぐに簡単に利用できるように独自にdeb/rpmパッケージを作成し、apt/yumリポジトリ(以下、単にリポジトリとする)を提供しています。どちらのプロジェクトも1人だけがリポジトリを更新できるのではなく、リポジトリを更新できる権限を持つ人(以下、パッケージマネージャーとする)が複数人います。

リポジトリではGPGキーで署名することにより1、そのリポジトリが改ざんされていないかを確認することができます2。パッケージマネージャーが複数人いる場合、だれか1人の個人用のGPGキーを署名用のキーとして使うとその人しかリポジトリを更新できなくなります。個人的なGPGキーの秘密鍵3とパスフレーズはその人だけしか知らない情報だからです。それでは、複数人でリポジトリを更新できるようにするはどうしたらよいでしょうか。以下の2つの案が考えられます。

  1. パッケージマネージャー全員のGPGキーをそのリポジトリの公式な署名用GPGキーとする。

  2. 新しく署名用GPGキーを作成し、パッケージマネージャー全員で共有する。リポジトリの公式な署名用GPGキーはその新しく作ったキー1つとする。

どちらの方法も実現できますが、署名用のGPGキーを作成する方が一般的です。Debian GNU/LinuxやCentOSなどの公式リポジトリでは署名用のGPGキーを作っています。aptではdebian-keyringやdebian-multimedia-keyringなどXXX-keyringというパッケージ名が一般的で、yumではcentos-releaseやfedora-releaseなどXXX-releaseというパッケージ名が一般的です4

個人のGPGキーを使う場合、新しくパッケージマネージャーが増えるとユーザーすべてにその人のGPGキーの公開鍵を配布しなくてはいけません。署名用の公開鍵はdeb/rpmパッケージで配布することが一般的です。そのため、apt/yumで更新できるので、ユーザーにとってそんなに難しい作業ではありません。しかし、ユーザーが通常のパッケージを更新する前に公開鍵用パッケージを更新しなくてはいけないため、通常のパッケージ更新手順に比べるとひと手間増えてしまいます。

一方、署名用GPGキーを作成する場合、パッケージマネージャーが増えてもユーザー側は特に追加の作業をする必要はありません。リポジトリ提供側だけの作業で済みます。問題はパッケージマネージャー間でどうやってセキュアに署名用GPGキーの秘密鍵とパスフレーズを共有するかです。

ということでようやく本題です。

署名用GPGキーの運用方法案

新しくパッケージマネージャーが増えたとします。その人に署名用GPGキーの秘密鍵とパスフレーズをセキュアに渡すにはUSBメモリなどを手渡しするのが確実です5。しかし、住んでいる場所が離れているなどの理由で手渡しするのが難しいこともあるでしょう。その場合はネットワーク経由でセキュアに渡します。

ここでは、新しいパッケージマネージャーが個人的なGPGキーをすでに持っていて、既存のパッケージマネージャーから信頼されている6とします。この場合、新しいパッケージマネージャーの公開鍵で署名用のGPGキーの秘密鍵とパスフレーズを暗号化してネットワーク経由で渡します。こうすれば、新しいパッケージマネージャーの秘密鍵でしか復号できないため、署名用のGPGキーの秘密鍵とパスフレーズをセキュアに渡せます。

それでは、実際にどうやってこれを実現するかを説明します。

実現方法

実現方法は以下のような流れになります。

  1. 署名用GPGキーの生成

  2. 署名用GPGキーの公開鍵に署名し、キーサーバーに送信

  3. 署名用GPGキーの秘密鍵とパスフレーズを暗号化

  4. 暗号化した署名用GPGキーの秘密鍵とパスフレーズを渡す

  5. 署名用GPGキーの秘密鍵とパスフレーズを取り込む

  6. 署名用GPGキーで署名する

署名用GPGキーの生成

まず、署名用のGPGキーを生成します。CentOS 5用のyumリポジトリも作る場合はDSAを選ぶことと鍵の長さを1024ビットにすることがポイントです。CentOS 5で使っているrpmコマンドではRSAや1024ビット以上の鍵の長さを扱うことができないからです7

以下は実際にコマンドを実行した結果です。コマンド実行時にパスフレーズを聞かれるのですが、そのときに使うランダムなパスフレーズの作り方は後述します。

% gpg --gen-key
gpg (GnuPG) 1.4.12; Copyright (C) 2012 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

ご希望の鍵の種類を選択してください:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (署名のみ)
   (4) RSA (署名のみ)
選択は? 3
DSA keys may be between 1024 and 3072 bits long.
What keysize do you want? (2048) 1024
要求された鍵長は1024ビット
鍵の有効期限を指定してください。
         0 = 鍵は無期限
      <n>  = 鍵は n 日間で満了
      <n>w = 鍵は n 週間で満了
      <n>m = 鍵は n か月間で満了
      <n>y = 鍵は n 年間で満了
鍵の有効期間は? (0)
Key does not expire at all
これで正しいですか? (y/N) y

あなたの鍵を同定するためにユーザーIDが必要です。
このソフトは本名、コメント、電子メール・アドレスから
次の書式でユーザーIDを構成します:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

本名: groonga Key
電子メール・アドレス: packages@groonga.org
コメント: groonga Official Signing Key
次のユーザーIDを選択しました:
    “groonga Key (groonga Official Signing Key) <packages@groonga.org>”

名前(N)、コメント(C)、電子メール(E)の変更、またはOK(O)か終了(Q)? o
秘密鍵を保護するためにパスフレーズがいります。

今から長い乱数を生成します。キーボードを打つとか、マウスを動かす
とか、ディスクにアクセスするとかの他のことをすると、乱数生成子で
乱雑さの大きないい乱数を生成しやすくなるので、お勧めいたします。
++++++++++.+++++...++++++++++.+++++++++++++++.++++++++++++++++++++.++++++++++.++++++++++++++++++++.+++++..+++++.++++++++++++++++++++++++++++++>++++++++++...>+++++....+++++

十分な長さの乱数が得られません。OSがもっと乱雑さを収集
できるよう、何かしてください! (あと241バイトいります)
gpg: 鍵45499429を絶対的に信用するよう記録しました
公開鍵と秘密鍵を作成し、署名しました。

gpg: 信用データベースの検査
gpg: 最小の「ある程度の信用」3、最小の「全面的信用」1、PGP信用モデル
gpg: 深さ: 0  有効性:   2  署名:   0  信用: 0-, 0q, 0n, 0m, 0f, 2u
pub   1024D/45499429 2012-04-28
                 指紋 = C97E 4649 A205 1D0C EA1A  73F9 72A7 496B 4549 9429
uid                  groonga Key (groonga Official Signing Key) <packages@groonga.org>

Note that this key cannot be used for encryption.  You may want to use
the command "--edit-key" to generate a subkey for this purpose.

パスフレーズは十分に長いものを使います。例えば、以下のコマンドでランダムな文字列を生成できます。(実行するたびに結果は変わります。)

% head /dev/urandom | base64 --wrap=0
1FMbKE8tAa4lZ2AgxBL3+J1U4M5MKhfDHNt(...省略...)snCg==

これで署名用のGPGキーができました。

署名用GPGキーの公開鍵に署名し、キーサーバーに送信

署名用GPGキーの公開鍵に自分の秘密鍵で署名しましょう。そうすれば、あなたを信頼している人がこの新しく生成した署名用GPGキーを信用しやすくなります。

署名用GPGキーの公開鍵に署名するには署名用GPGキーの鍵IDが必要です。鍵IDはgpg --list-keysで確認できます。

% gpg --list-keys
...
pub   1024D/45499429 2012-04-28
uid                  groonga Key (groonga Official Signing Key) <packages@groonga.org>
...

「pub ...」の行の「/」のあとの「45499429」が鍵IDです。

鍵IDを指定して署名します。

% gpg --sign-key 45499429

署名したらキーサーバーに送信して簡単に取得できるようにしましょう。

% gpg --keyserver keyserver.ubuntu.com --send-keys 45499429

署名用GPGキーの秘密鍵とパスフレーズを暗号化

署名用GPGキーができて、公開鍵をキーサーバーに送信したので、新しいパッケージマネージャーに秘密鍵を渡すために秘密鍵をエクスポートします。

鍵IDを指定して秘密鍵をエクスポートします。

% gpg --armor --export-secret-keys 45499429
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.12 (GNU/Linux)

...
-----END PGP PRIVATE KEY BLOCK-----

このエクスポートした秘密鍵とキー作成時に設定したパスフレーズを一緒にしたファイルを作ります8

release-key-secret.asc:

1FMbKE8tAa4lZ2AgxBL3+J1U4M5MKhfDHNt...snCg==

-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.12 (GNU/Linux)

...
-----END PGP PRIVATE KEY BLOCK-----

このファイルを新しいパッケージマネージャーの公開鍵で暗号化します。ここでは鍵ID「079F8007」が新しいパッケージマネージャーのGPGキーだとします。

% gpg --encrypt --recipient 079F8007 release-key-secret.asc

これで、release-key-secret.asc.gpgができるので、その人だとわかる情報を最後につけます。例えば、release-key-secret.asc.gpg.kouというようにします。

暗号化した署名用GPGキーの秘密鍵とパスフレーズを渡す

新しいパッケージマネージャーでしか復号できないように暗号化した署名用GPGキーの秘密鍵とパスフレーズができたので、これを新しいパッケージマネージャーに渡します。メールで送ったり、リポジトリ9にコミットして渡すとよいでしょう10

署名用GPGキーの秘密鍵とパスフレーズを取り込む

新しいパッケージマネージャーは暗号化された署名用GPGキーの秘密鍵とパスフレーズを受け取ったら、復号し、署名用GPGキーの秘密鍵を取り込みます。

% gpg --decrypt release-key-secret.asc.gpg.kou | gpg --import

公開鍵も取り込み、自分の秘密鍵で署名しましょう。

% gpg --keyserver keyserver.ubuntu.com --recv-keys 45499429
% gpg --sign-key 45499429
% gpg --keyserver keyserver.ubuntu.com --send-keys 45499429

これで新しいパッケージマネージャーは署名用GPGキーが用意できました。

署名用GPGキーで署名する

いよいよ署名します。実際はシェルスクリプトなどを作って以下のコマンドを打たなくてもすむようにします。(参考: Debianパッケージの作り方と公開方法: groongaを例にして - ククログ(2010-01-18)(情報の一部が古いので注意)、Debian GNU/Linux上でRPMパッケージ・Yumリポジトリを作って公開する方法: milter managerを例にして - ククログ(2010-03-03)

aptリポジトリの場合はReleaseファイルを署名します。

% gpg2 \
    --sign \
    --detach-sign \
    --armor \
    --loca-user 45499429 \
    --output Release.gpg \
    Release

yumリポジトリの場合はrpmファイルを署名します。以下はDebian GNU/Linux sid上でrpmファイルに署名するコマンドです。いろいろマクロを定義しているのは無理やりgpg2を使うためです11

% rpm \
    -D "_gpg_name 45499429" \
    -D "__gpg /usr/bin/gpg2" \
    -D "__gpg_check_password_cmd /bin/true true" \
    -D "__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" \
    --resign *.rpm

これで新しいパッケージマネージャーでもリポジトリに署名できるようになりました。

まとめ

apt/yumリポジトリを更新できる人が複数人いる場合にリポジトリに署名する方法を説明しました。このようにすればセキュアに署名用GPGキーを共有できるのではないかという案とその具体的な実現方法という内容です。複数人でapt/yumリポジトリを更新したいという場合は参考にしてみてはいかがでしょうか。

ただし、セキュリティーの専門家が書いた文章ではないため、セキュリティー面で心配のある方は専門家に相談することをお勧めします。

  1. aptリポジトリではリポジトリのメタデータに署名し、yumリポジトリではrpmファイルに署名する。参考: SecureApt - Debian Wiki第5章 Yum

  2. 改ざんされていないこととそのリポジトリが信用できるものかどうかは別問題なことに注意すること。

  3. gpgコマンドの出力では私有鍵ではなく秘密鍵となっているのでここでも秘密鍵とする。

  4. groongaやmilter managerもgroonga-repositoryやmilter-manager-repositoryではなくgroonga-releaseやmilter-manager-releaseというrpmパッケージ名にすればよかった…

  5. 物理的に盗まれないように注意。

  6. 新しいパッケージマネージャーのGPGキーの公開鍵が既存のパッケージマネージャーのGPGキーの秘密鍵で署名されている。参考: Debian -- 鍵署名 (Keysigning)

  7. RPMのCHANGESには4.4.2でpermit gpg to be used for RSA signatures.やpermit RSA key sizes larger than 1024 bits.と書かれているのでCentOS 5にインストールされているrpm-4.4.2.3ではRSAや1024ビット以上の鍵長も使えそうな気がするが署名を検証できなかった。

  8. 同じファイルに秘密鍵とパスフレーズを入れてしまっているので、このファイルの中身を盗まれたらこの鍵は信用できなくなる。ファイルを分けた方がよりセキュアになりそうな気もするが、どちらかのファイルの中身が盗まれたら復号されたということなので、もう一方のファイルもすぐに復号されるだろう。そのため、分けてもあまりセキュアにならない気がする。

  9. これはapt/yumリポジトリではなくGitやSubversionなどのバージョン管理システムのリポジトリのこと

  10. メールの方が暗号化したファイルにアクセスできる人が少ないのでよりセキュアになりそうだが、公開鍵から秘密鍵とパスフレーズを探すことはこの暗号化したファイルがあるなしに関わらずできるため、リポジトリにコミットしてあってもあまり関係ないのではないかという気がする。そのため、groongaやmilter managerの場合はリポジトリにコミットしている。

  11. gpg-agentを使いたいのでgpg2を使いたい。