Manage multiple distributions with reprepro

本日Trustyがリリースされるということで、職場で使っているrepreproにTrusty用のローカルアーカイブを追加しました。 Preciseの設定をコピーして列挙すればよいだけですね。

設定内容

conf/distributions

preciseの部分をtrustyに変えて追記するだけ。

Origin: myrepo
Label: myrepo
Suite: precise
Codename: precise
Architectures: amd64 i386 source
Components: custom
UDebComponents: custom
Description: my repository for Ubuntu precise
SignWith: yes

Origin: myrepo
Label: myrepo
Suite: trusty
Codename: trusty
Architectures: amd64 i386 source
Components: custom
UDebComponents: custom
Description: my repository for Ubuntu trusty
SignWith: yes

conf/incoming

distributionsと同じ。

Name: precise
IncomingDir: incoming
TempDir: tmp
LogDir: log
Allow: precise
Default: precise

Name: trusty
IncomingDir: incoming
TempDir: tmp
LogDir: log
Allow: trusty
Default: trusty

conf/options

これは変更なし。

verbose
basedir /var/lib/debpkg-custom/ubuntu
ask-passphrase

DB生成

PreciseやWheezy用のローカルアーカイブには、私のメンテナンスしているパッケージやその依存パッケージをバックポートしたり、 社内で開発&利用しているツールをDebianパッケージにして管理しています。 私のメンテナンスしているパッケージはひと通りTrustyに入っているので、とりあえず必要ないので空のDBを生成します。

$ pwd
/var/lib/debpkg-custom/ubuntu
$ reprepro export trusty

余談。

Wheezy用には、パス自体分けて(/var/lib/debpkg-custom/debian)運用していたのですが、 なんか分けないで混ぜていても良かったなぁと思いました。が、まぁ特に困らないのでいいや。

Retrieve and generate debian package of Oracle JDK

I have written script for retrieving Oracle JDK tarball automatically, why I wanted to retrieve and make debian package with make-jpkg command on Jenkins.

  1. Retrieve JDK 7 and JDK 8 download page urls from Oracle JDK Download site.

  2. Retrieve JDK tarball URL.

  3. Check the save file local directory.

  4. Download with “wget” command.

Requirements

  • python-requests

  • python-query

  • wget

  • java-package

Prepare as following script for Jenkins.

#!/bin/sh

python retrieve_jdk.py -j $1
echo -e '\n\n' | make-jpkg $(ls -1 jdk-*-linux-x64.tar.gz | tail -1)

See also

How to build custom Debian package automatically by Jenkins

以前、OpenSSH LDAP Public key パッチ(openssh-lpk)を、Ubuntu 12.04 LTS(Precise)とDebian GNU/Linux WheezyのそれぞれのOpenSSHに適用したカスタムビルドパッケージを作りました。 1 2 OpenSSHのパッケージが更新される度に、

  1. このパッチを適用し

  2. pbuilderを使ってPrecise, Wheezyでビルド

  3. 実環境にインストールしてテスト

  4. GPG key sign

  5. reprepro管理下のローカルアーカイブに登録

するという作業を行っています。といっても、PreciseやWheezyでもOpenSSHのパッケージ自体のアップデートが最近までほとんどなかったので、手動でメンテナンスを行っていたのですが、この1ヶ月くらいで何回かアップデートがあったことや、先日Jenkinsを作ったこと (Issue deploying Jenkins to Tomcat7 Debian package in Wheezy)もあったこと、職場で私以外に作業できる人がいない、という問題があるので、Jenkinsでビルドするようにしました。

OpenSSHパッケージのDebianバージョンとOpenSSH LPKパッチの関係

これを図にすると下記のようになります。 Debianのパッケージが更新されるたびに、”+cust1”を付加したバージョンをリリースするわけです。

digraph changelog { node [fontsize="10"]; edge [fontsize="10"];  w2c[label="1:6.0p1-4+deb7u2+cust1", style=dotted, fontcolor="#888888"]; w2[label="1:6.0p1-4+deb7u2", style=dotted, fontcolor="#888888"]; w1c[label="1:6.0p1-4+deb7u1+cust1"]; w1[label="1:6.0p1-4+deb7u1"]; w0c[label="1:6.0p1-4+cust1"]; w0[label="1:6.0p1-4"]; w2 -> w2c [style=dotted, label="apply openssh-lpk"]; w1 -> w1c [label="applied openssh-lpk"]; w0 -> w0c [label="applied openssh-lpk"]; w2 -> w1 [style=dotted, dir=back]; w1 -> w0 [dir=back]; {rank = same; w0; w0c} {rank = same; w1; w1c} {rank = same; w2; w2c} }

Preciseの場合も基本的に同じです。

digraph changelog { node [fontsize="10"]; edge [fontsize="10"];  u4c[label="1:5.9p1-5ubuntu1.4+cust1", style=dotted]; u4[label="1:5.9p1-5ubuntu1.4", style=dotted]; u3c[label="1:5.9p1-5ubuntu1.3+cust1"]; u3[label="1:5.9p1-5ubuntu1.3"]; u2c[label="1:5.9p1-5ubuntu1.2+cust1"]; u2[label="1:5.9p1-5ubuntu1.2"]; u1c[label="1:5.9p1-5ubuntu1.1+cust1"]; u1[label="1:5.9p1-5ubuntu1.1"]; u0c[label="1:5.9p1-5ubuntu1+cust1"]; u0[label="1:5.9p1-5ubuntu1"]; u4 -> u4c [label="apply openssh-lpk", style=dotted]; u3 -> u3c [label="applied openssh-lpk"]; u2 -> u2c [label="applied openssh-lpk"]; u1 -> u1c [label="applied openssh-lpk"]; u0 -> u0c [label="applied openssh-lpk"]; u4 -> u3 [style=dotted, dir=back]; u3 -> u2 -> u1 -> u0[dir=back]; {rank = same; u0; u0c} {rank = same; u1; u1c} {rank = same; u2; u2c} {rank = same; u3; u3c} {rank = same; u4; u4c} }

pbuilderのイメージ準備

Jenkinsを動かしているのは前回の通りWheezyなので、Wheezy用のbase.tgzは次のように作成します。

$ sudo pbuilder --create --distribution wheezy --basetgz /var/cache/pbuilder/wheezy-base.tgz

一方、Precise用のbase.tgzは、別途Preciseをインストールしたサーバ上で同様に作成し、そのtarballを/Jenkins用のサーバに転送&配置します。 3

WheezyやPrecise用のbase.tgzのAPT Lineにはupdatesやsecurityのものは含まれていないので、

$ sudo pbuilder --login --save-after-login --basetgz /vat/cache/pbuilder/wheezy-base.tgz

でchroot環境にログインし、変更しておきます。 4 また、合わせて、repreproで作成したローカルアーカイブ用のGPG公開鍵をapt-key addコマンドで追加しておきます。

パッチ置き場と一次ビルドの出力先の作成

openssh-lpkパッチと、debian/rulesのパッチをを任意のWebサーバにWheezy用、Precise用とそれぞれ用意しました。下記のようなURLです。

また、一次ビルドで出力するソースパッケージは、/var/tmp/resultディレクトリを作成し、後述のスクリプトでそこに出力するようにします。

$ sudo install -d -o tomcat7 -g tomcat7 --mode 0700 /var/tmp/result

一次ビルド、といっているのはパッチを適用した状態で、一度debuildを実行して出力されるソースパッケージのことです。その出力されたソースパッケージを用いて、pbudilerでクリーンビルドする、という流れです。

ソースツリーからpbuilderを実行するpdebuildコマンドを使えば、そんな面倒なことをする必要はありません。が、Wheezy上でPreciseの公開鍵をインポートしなくてはいけない点と、PreciseとWheezyとで基本的に同じ手順で行いたい、という二点から、pbuilderを2回実行する形にしました。

ジョブ用のスクリプト

Wheezy, preciseとで基本同じなので、先頭の2行のみを環境に合わせて変更します。Wheezyの場合は、codenameをwheezy, distroにはstableにしますが、Preciseの場合には両方共preciseです。 5

codename=wheezy
distro=stable
bin_package=openssh-server
src_package=openssh
arch=$(dpkg-architecture -qDEB_HOST_ARCH)

# check version
cat << EOF > check_version.sh
apt-cache show $bin_package | grep Version: | sort | tail -1 | grep -v +cust > /var/tmp/result/${BUILD_ID}.txt
exit 0
EOF
sudo pbuilder --update --basetgz /var/cache/pbuilder/${codename}-base.tgz
sudo pbuilder --execute --basetgz /var/cache/pbuilder/${codename}-base.tgz --bindmounts "/var/tmp/result" -- check_version.sh
test -s /var/tmp/result/${BUILD_ID}.txt || exit

deb_version=$(awk -F: '{print $3}' /var/tmp/result/${BUILD_ID}.txt)
orig_version=$(echo $deb_version | awk -F"-" '{print $1}')

# retrieve source package and patches
cat << EOF > build.sh
apt-get -qq -y install curl devscripts quilt patch libdistro-info-perl fakeroot libldap2-dev
apt-get -qq -y build-dep $bin_package

apt-get source $src_package

curl -O http://repo.example.org/${codename}/openssh-lpk.patch
curl -O http://repo.example.org/${codename}/rules.patch
(
export DEBFULLNAME="Auto Build"
export DEBEMAIL=autobuild@example.org

cd ${src_package}-${orig_version}
echo "### applying patch ###"
patch -p1 --dry-run < ../openssh-lpk.patch || exit 1
patch -p1 < ../openssh-lpk.patch

echo "### commiting patch ###"
echo | dpkg-source --commit . openssh-lpk.patch ../openssh-lpk.patch

echo "### update Build-Depends ###"
sed -i 's/^\(Build-Depends: .*\)$/\1, libldap2-dev/' debian/control

echo "### update compile options ###"
patch -p1 --dry-run < ../rules.patch
patch -p1 < ../rules.patch

echo "### update changelog ###"
dch -l+cust -D${distro} "Applied OpenSSH LPK patch."

echo "### build package ###"
debuild -us -uc
)

cp -f ${src_package}_${deb_version}+cust1.debian.tar.gz ${src_package}_${orig_version}.orig.tar.gz ${src_package}_${deb_version}+cust1.dsc /var/tmp/result/
EOF

sudo pbuilder --execute --basetgz /var/cache/pbuilder/${codename}-base.tgz --bindmounts "/var/tmp/result" -- build.sh

# clean build
sudo pbuilder --build --basetgz /var/cache/pbuilder/${codename}-base.tgz /var/tmp/result/${src_package}_${deb_version}+cust1.dsc

# installing test
sudo piuparts -b /var/cache/pbuilder/${codename}-base.tgz -d $codename --keep-sources-list /var/cache/pbuilder/result/${src_package}_${deb_version}+cust1_${arch}.changes

大まかな流れとしては次のとおりです。

  1. カスタムビルドパッケージよりも更新されたバージョンがリリースされていないかのチェック

  2. pbuilderを使い、chroot環境内でのソースパッケージとパッチの取得・適用及びビルド

  3. ビルドして生成されたソースパッケージを用い、pbuilderでクリーンビルド

  4. クリーンビルドしてできたパッケージを、piupartsでインストールテスト

公式パッケージで更新されてパッチ適用したバージョンよりも新しいバージョンがリリースされている場合、このジョブを実行すると、パッチ適用してビルドされたパッケージが、Jenkinsサーバの/var/cache/pbuilder/resultディレクトリ下に作成されます。

後は別のジョブでdebsignでGPG key signし、repreproのincomingディレクトリにpushすれば良いわけです。 6

この記事のオチ

さて、これで自動ビルドできるようになったので楽できるわー、と思ったら、このジョブを仕込んた翌日に、 OpenSSH 6.2 以降で

  • AuthorizedKeysCommand

  • AuthorizedKeysCommandUser

というオプションが使えるようになったことを同僚に教えてもらいました。つまり、「もはやOpenSSH LPKなんて不要になった!」ワケです。素晴らしい。

来週リリース予定のUbuntu 14.04 LTS(Trusty)ではOpenSSH 6.6ですし、WheezyでもBackportsに6.5にあるのでわざわざこんな手の混んだことをしなくても、ミラーをすればよいのです。 なので、このジョブそのものはたった一日で不要になってしまいました。

ただし、Precise用にはWheezyのBackportsのソースパッケージをリビルドする必要があるので、今回やった事自体は役立ちそうです。このジョブに比べたら(リビルドさえちゃんと確認できれば)なんてこと無いですね。

もっと簡単にマルチディストリビューション向けに自動(カスタム)クリーンビルドする方法があれば、どなたか教えて下さい。

追記

  • check_version.shに exit 0 を忘れていたので、ローカルアーカイブの方にパッチ適用済みのパッケージが存在すると、ジョブがコケてしまう、ので修正しました。

  • @mizuno_as さんに複数環境向けにビルドするならpbuilder-distの方が便利ですよ、と教えてもらったので、後日検証しようと思います。

追記2

  • –bindmounts オプションを /var/tmp/result /tmp としていたのですが、pbuilderのbindmountsオプションの挙動を勘違いしていました。こうするとホストの/var/tmp/resultとchroot内の/var/tmp/result、そしてホストの/tmpとchroot内の/tmpがbind-mountされるので、今回であれば/var/tmp/resultだけを記述すれば良かった、ということでした。(修正済み)

Footnotes

1

OpenSSH LDAP public key

2

Applying openssh-lpk to Wheezy

3

cowbuildreを使うことも検討したのですが、Precise上で cowbuilder –create すると失敗するためpbuilderを使うことにしました。

4

exitコマンドではなく、Ctrl + dでログアウトすると、”–save-after-login” オプションをつけていても変更が保存されないようです。

5

Wheezyでは、dchコマンドの”-D”オプションでのディストリビューションの指定には、Ubuntuと違ってコードネームの”wheezy”ではなく、”stable”でないとエラーになります。

6

今回はdebsign, repreproへのpushについては書きませんが。

Issue deploying Jenkins to Tomcat7 Debian package in Wheezy

travis-ciはよく使っているのですが、JenkinsはあのWeb UIに馴染めなくて今まで使っていませんでした。が、この度仕事でJenkinsを用意する必要が出てきたの 1 で、Debian GNU/Linux Wheezyでサーバを用意することにしました。

用意するにあたり、upstreamからwarファイルをダウンロードし、直接実行する方法で、まず全体設定やセキュリティ関連の設定を試しました。そのあとで、DebianパッケージのTomcat7上で動かそうとした際にちょっとハマりました。これはそのメモです。ググった限りでは同様の問題にハマっている人は多分いないのでしょう。

直接実行の場合

OpenJDKを使うため、default-jre-headless パッケージをインストールします。あとは、warファイルをダウンロードして直接実行するだけです。

$ sudo apt-get intall default-jre-headless
$ wget http://mirrors.jenkins-ci.org/war/latest/jenkins.war
$ java -jar jenkins.war

検証途中で、権限周りの設定をしている時に間違えてEnterを打ってしまい、何もできなくなるというエライことに。きっと これよくある問題 なんでしょうね。

DebianパッケージのTomcat上で動かす場合

本題です。Tomcat7をまずDebianパッケージでインストールします。

$ sudo apt-get install tomcat7 tomcat7-admin

インストールしたら自動的にTomcatが起動しますが、何の権限も無くてtomcatのweb管理画面にもアクセスできないので、/etc/tomcat7/tomcat-users.xmlを変更し、下記を<tomcat-users>の子要素として追加します。ユーザ名、パスワードは適当に設定して下さい。

<tomcat-users>
    (snip)
        <role rolename="manager"/>
        <role rolename="manager-gui"/>
        <role rolename="admin-gui"/>
        <user username="tomcat" password="passw0rd" roles="manager,manager-gui,admin-gui"/>
</tomcat-users>

設定したら、tomcat7を再起動します。

$ sudo service tomcat7 restart

これで、設定したユーザ、パスワードを使って、 http://tomcat.example.org:8080/manager/ からTomcat管理画面にアクセスできます。あとは、先ほどダウンロードしたwarファイルを使ってデプロイしてやれば、 Jenkinsが起動します。

と思ったら大間違いで、デプロイできても起動できません。

原因は、tomcat7パッケージで作られるtomcat7ユーザのホームディレクトリの位置です。 tomcat7ユーザのホームディレクトリは、/usr/share/tomcat7になっているのですが、/usr以下のディレクトリはパッケージのインストール以外で置かれる静的ファイル以外は基本置かれません。しかし、Jenkinsは実行ユーザのホームディレクトリの直下に、.jenkinsディレクトリを作成し、そこに各種出力をします。/usr/share/tomcat7の所有権はrootユーザおよびrootグループであり、自由に書き込みはできません。

なので、次のようにしてやることで、この問題を回避できます。 2

$ sudo install -d -o tomcat7 -g tomcat7 -m 700 /var/lib/jenkins
$ sudo ln -s /var/lib/jenkins /usr/share/tomcat7/.jenkins

あとは、tomcatを再起動すれば、Jenkinsが起動します。

この問題、TomcatもUpstreamのwarファイルを使うか、あるいはJenkinsも公式のDebianパッケージ 3 を使えば発生しない現象ですね、おそらく。しかし、Wheezyでは残念ながら公式パッケージにJenkinsは含まれていないので、Debianパッケージを用意するならSidからのバックポートが必要になります。が、ビルドの依存関係多いので、jenkins以外にもバックポートが多数必要という罠。今回バックポートせずにJenkinsだけをwarファイル使ったのは、たんに私の甘え&ゆとりです。

Footnotes

1

本来のきっかけとは別に、SidからWheezyやPrecise向けにDebianパッケージをバックポートしたり、自前のツールをDebianパッケージにする際のオートビルド環境として、jenkins-debian-glue が良いよ、と @lurdan に教えて頂いたので、それも動機となっています。

2

/usr/share/tomcat7/.jenkinsディレクトリを作って、所有権をtomcat7:tomcat7にする、というのでも動きますが、お行儀悪いのでやめましょう。

3

Upstreamが配布しているDebianパッケージもあります が、これはDebianの公式パッケージではないので、私は使いません。

How to encrypt ansible-vault in the TLS certificate

先週、Sidのansibleパッケージが1.4.5+dfsg-1から1.5.3+dfsg-1に上がったので、待望のansible-vaultを使ってみました。パスワードなどを暗号化するために使うのが主でしょうが、今回の私の目的は、TLS証明書を及びその秘密鍵の暗号化です。仕事で使っているplaybookに、PEM形式の証明書や秘密鍵を含めるのがいやだったのが動機です。

IMAPサーバ用のplaybookを例にします。

.
|-- development
|-- group_vars
|   |-- all
|   |-- development
|   `-- production
|-- imapd.yml
|-- production
|-- roles
(snip)
|   |-- dovecot
|   |   |-- handlers
|   |   |   `-- main.yml
|   |   |-- tasks
|   |   |   `-- main.yml
|   |   |-- templates
(snip)
|   |   |   |-- 10-ssl.conf.j2
    (snip)
|   |   |   |-- key.j2
|   |   |   `-- cert.j2
|   |   `-- vars
|   |       `-- main.yml
(snip)
`-- site.yml

ansible-vaultで暗号化する対象は、roles/dovecot/templatesディレクトリの下にある、key.j2とcert.j2の2つのテンプレートの中に出力する文字列です。 証明書とprivate keyを配布するだけなら、filesディレクトリの下に、証明書およびprivate keyファイル自体を配置し、copyモジュールでコピーすれば良いだけです。 しかし、 ドキュメントにもあるとおり ansible-vaultで対象に暗号化の対象にできるのは各種の変数だけなので、filesディレクトリ下の添付ファイルは復号できません。そこで、templateモジュールを使う必要があります。暗号化する文字列自体は、roles/dovecot/vars/main.ymlの中に記述します。

PEM形式の文字列は、

-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE
AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x
CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW
(snip)
-----END CERTIFICATE-----

のような形式になっているので、これをtemplateモジュールを使って、ファイルを配置するには、templates/cert.j2などのテンプレートに、

{{ cert_pem }}

とだけ記述し、vars/main.ymlには、

---
- cert_pem: "-----BEGIN CERTIFICATE-----\nMIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE\nAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x\nCzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW\n...(snip)...\n-----END CERTIFICATE-----"
- key_pem: "..."

のように、改行を”n”に変更して一行の文字列とし、それをダブルクォートかシングルクォートで括って指定します。 これで動作確認ができたら、vars/main.ymlを暗号化します。

$ ansible-vault encrypt roles/dovecot/vars/main.yml

暗号化されたファイルは下記のようになります。

$ANSIBLE_VAULT;1.1;AES256
39613864343833373066386636333338653831383630623938643138366230323336656234326234
3833663562396237393333363665643631653331386566660a653530326636666137633731373361
34653163626664376432393433303435333433303535626437623935313962626430643038623464
(snip)

暗号化された変数を使って、playbookを実行するには、 --ask-vault-pass オプションをつけてansible-playbookコマンドを実行すれば良いだけです。 このオプションをつけると、実行時にだけメモリ上に復号されます。

まとめ

今まで、パスワードなどはvars_promptで都度入力し、TLS証明書&秘密鍵などは別途配布する、という方法でやっていました。 これでplaybook自体にansible-vaultで暗号化して管理できるので、vautl passwordのみ、keepassなどで管理すれば良さそうです。