Debianのリポジトリを自前で構築してパッケージを登録し、そのリポジトリからパッケージをインストールする

# 初めに

必要に迫られて自前のDebianリポジトリを構築してみたので、そのための手順と構築したリポジトリからパッケージをインストールする方法をまとめてみました。

# 背景

そもそもきっかけはDebian 11(コードネーム bullseye)でpyspf-milterを利用しようとして異常終了するバグに直面したことでした。この問題を解決するためには、stableのものより新しいtesting/unstableのパッケージをカスタマイズした上でバックポートする必要があります。このあたりの詳細については以下の記事にまとめました。

https://qiita.com/yasuhirokimura/items/9027c3c0a7cfb51f9e63

作成したパッケージを直接インストールしても良かったのですが、どうせなら通常のパッケージと同様に`apt install`でインストールできるようにしたかったので、自前のリポジトリを構築してみることにしました。

# 前提

## リポジトリに関する前提

構築するリポジトリに関する前提は以下の通りになります。

1. リポジトリはパッケージをビルドを行ったシステム上に構築する。
1. リポジトリはWebサーバを用いてHTTPでアクセスできるようにする。

前者に関しては、作成したパッケージとそれに署名する際に用いたGnuPGの鍵を持っていけば、別のシステム上でも構築は可能ですが、話を簡単にするために同じシステム上で構築することとします。

## パッケージに関する前提

パッケージに関しては、前述の記事で作成したパッケージを利用するということになりますが、一応具体的に条件を挙げると以下のようになります。

1. パッケージはstableより新しいバージョンのunstable(sid)のものをカスタマイズしたうえで、stable(bullseye)にバックポートしたものである。
1. パッケージは`debuild`を使ってビルドされたもので、GnuPGの鍵で署名されている。

以下の説明では、パッケージは`~/work/debian/spf-engine/`にあるものとします。

```shell
% ls -1F ~/work/debian/spf-engine
postfix-policyd-spf-python_2.9.3-1.1~bpo11+1_all.deb
pyspf-milter_2.9.3-1.1~bpo11+1_all.deb
python3-spf-engine_2.9.3-1.1~bpo11+1_all.deb
spf-engine/
spf-engine_2.9.3-1.1~bpo11+1.debian.tar.xz
spf-engine_2.9.3-1.1~bpo11+1.dsc
spf-engine_2.9.3-1.1~bpo11+1_amd64.build
spf-engine_2.9.3-1.1~bpo11+1_amd64.buildinfo
spf-engine_2.9.3-1.1~bpo11+1_amd64.changes
spf-engine_2.9.3.orig.tar.gz
```

# 準備

リポジトリの構築には`reprepro`というコマンドを用います。同じ名前のパッケージで提供されているので

```shell
apt install reprepro
```

とすればインストールすることが出来ます。

# リポジトリの構築

## リポジトリのトップディレクトリの作成

前提条件で書いたように、リポジトリはWebサーバを用いてHTTPでアクセスできるようにするので、`/var/www/debian`に構築することとします。リポジトリのトップディレクトリを

```shell
mkdir -p /var/www/debian
```

で作成します。パッケージを追加する際にパッケージへの署名に用いたGnuPG鍵を使用するので、このディレクトリはGnuPG鍵を所有するアカウントが読み書き可能である必要があります。今回はディレクトリの所有者をGnuPG鍵を所有するアカウントに変更します。

```shell
chown taro /var/www/debian
```

## リポジトリの構成ファイルの作成

構成ファイルはリポジトリのトップディレクトリの下に`conf`というディレクトリを作成して、そこに`distributions`という名前で作成します。

```shell
cd /var/www/debian
mkdir conf
cd conf
vi distributions
```

構成ファイルの内容は以下の通りです。

```
Codename: bullseye-backports
Architectures: amd64 source
Components: main
Origin: deb.example.org
Label: deb.example.org
Description: Example.org Debian repository
SignWith: 7F4DBC5AFC3010FF
```

各項目の意味は以下の通りです。

<dl>
<dt>Codename</dt>
<dd>配布されるパッケージのコードネームの一覧(必須項目)。パッケージに関する前提のところで説明した通り、今回用いるパッケージはunstableのバージョンをstable(bullseye)にバックポートしたものなので、<code>bullseye-backports</code>になります。</dd>
<dt>Architectures</dt>
<dd>配布するパッケージのアーキテクチャの一覧(必須項目)。<code>source</code>はパッケージのソースを配布することを意味します。</dd>
<dt>Components</dt>
<dd>リポジトリが提供するコンポーネントの一覧(必須項目)。Debianの公式リポジトリだと<code>main,contrib,non-free</code>などがありますが、それ以外にも任意の値を指定できます。</dd>
<dt>Origin</dt>
<dd>このリポジトリの配布元を表す文字列。配布元のホスト名やURLなどを用いるのが良いでしょう。<code>apt</code>の設定ファイルでここで指定された値を条件として指定する場合があります。</dd>
<dt>Label</dt>
<dd>このリポジトリに対するラベル。<code>Origin</code>と同様に、<code>apt</code>の設定ファイルでここで指定された値を条件として指定する場合があります。</dd>
<dt>Description</dt>
<dd>このリポジトリの説明文。</dd>
<dt>SignWith</dt>
パッケージに署名するために用いたGnuPG鍵の鍵識別子の値。鍵識別子の値は<code>gpg --list-keys --keyid-format long</code>で確認できます。</dd>

## パッケージの追加

以下のコマンドを実行してリポジトリにパッケージを追加します。

```shell
cd /var/www/debian
reprepro include bullseye-backports ~/work/debian/spf-engine/spf-engine_2.9.3-1.1\~bpo11
+1_amd64.changes
```

パッケージを追加する際に署名に用いたGnuPG鍵を利用するため、このコマンドはGnuPG鍵を所有するアカウントで実行する必要があります。

無事パッケージを追加出来たら、`reprepro list`コマンドでリポジトリに追加されたパッケージの一覧を表示することが出来ます。

```shell-session
% cd /var/www/debian
% reprepro list bullseye-backports
bullseye-backports|main|amd64: postfix-policyd-spf-python 2.9.3-1.1~bpo11+1
bullseye-backports|main|amd64: pyspf-milter 2.9.3-1.1~bpo11+1
bullseye-backports|main|amd64: python3-spf-engine 2.9.3-1.1~bpo11+1
bullseye-backports|main|source: spf-engine 2.9.3-1.1~bpo11+1
% 
```

## 公開鍵の配布

このリポジトリで配布するパッケージはGnuPG鍵で署名されているので、このリポジトリからパッケージをインストールするユーザが署名を検証できるように、署名に用いたGnuPG鍵の公開鍵を取り出してリポジトリのトップに置きます。公開鍵の取り出しは以下のコマンドで行います。

```shell
gpg --export --armor --output "出力先のファイルのパス" "取り出す公開鍵の鍵識別子"
```

今回署名に用いたGnuPG鍵は以下の通りです。

```shell-session
% gpg --list-keys --keyid-format long taro@example.org
pub   rsa3072/7F4DBC5AFC3010FF 2022-11-16 [SC] [expires: 2024-11-15]
      F954C7608427047664E784FC7F4DBC5AFC3010FF
uid                 [ultimate] Taro Yamada <taro@example.org>
sub   rsa3072/49AB5A578599DCA8 2022-11-16 [E] [expires: 2024-11-15]

%
```

従って以下のコマンドを実行します。

```shell
gpg --export --armor --output /var/www/debian/gpg.key.asc 7F4DBC5AFC3010FF
```

これで公開鍵がリポジトリのトップディレクトリの下に`gpg.key.asc`という名前で作成されます。

# Webサーバを用いたリポジトリの公開

構築したリポジトリをWebサーバを用いてネットワーク経由でアクセス出来るように公開します。今回は以下のように設定します。

1. Webサーバはapache2パッケージを利用。
1. リポジトリのトップを`http://deb.example.org/`としてアクセスできるようにする。
1. 設定はApacheの`VirtualHost`の機能を利用する。

ここではApacheの基本的な設定は省略して、リポジトリのトップを`http://deb.example.org/`としてアクセスできるようにするための`VirtualHost`の設定だけを記述します。

`/etc/apache2/sites-available/deb.example.org.conf`というファイルを以下のような内容で作成します。

```apache
<VirtualHost *:80>
    ServerName deb.example.org
    DocumentRoot /var/www/debian/
    <Directory "/var/www/debian">
        Options FollowSymLinks Indexes
        Require all granted
    </Directory>
    <Directory "/var/www/debian/conf">
        Require all denied
    </Directory>
    <Directory "/var/www/debian/db">
        Require all denied
    </Directory>
    <Directory "/var/www/debian/incoming">
        Require all denied
    </Directory>
</VirtualHost>
```

`DocumentRoot`の指定とリポジトリのサブディレクトリのうち`conf`、`db`及び`incoming`へのアクセスを拒否するのが主な設定内容です。

設定ファイルを作成した後に以下のコマンドを実行すれば、リポジトリのトップディレクトリが`http://deb.example.org/`でアクセス可能になります。

```shell
a2ensite deb.example.org
systemctl reload apache2.service
```

# 構築したリポジトリからのパッケージのインストール

構築したリポジトリがネットワーク経由でアクセスすることが出来るようになったので、そこから`apt`を使ってパッケージをインストールできるようにシステムの設定を行います。

## 公開鍵のダウンロード

`http://deb.example.org/`からインストールされるパッケージにはGnuPGの鍵で署名がされているため、パッケージのインストールには署名に用いた鍵の公開鍵が必要になります。公開鍵は`http://deb.example.org/gpg.key.asc`でアクセスできるので、以下のようにしてダウンロードします。

```shell
mkdir -p /etc/apt/keyrings
wget -O - http://deb.example.org/gpg.key.asc | gpg --dearmor --output /etc/apt/keyrings/deb.example.org.gpg
```

## aptのsources.listの設定

`apt`でパッケージをインストールする出来るようにするにはリポジトリの情報を`sources.list`に追加する必要があります。今回の場合は`/etc/apt/sources.list.d/deb.example.org.list`を以下のような内容で作成します。

```
deb [arch=amd64 signed-by=/etc/apt/keyrings/deb.example.org.gpg] http://deb.example.org/ bullseye-backports main
deb-src [arch=amd64 signed-by=/etc/apt/keyrings/deb.example.org.gpg] http://deb.example.org/ bullseye-backports main
```

## 構築したリポジトリからのpyspf-milterパッケージのインストール

パッケージをインストールするための準備が完了したので、あとは`apt update`でデータベースを更新した後に`apt install pyspf-milter`を実行すれば、構築したリポジトリからpyspf-milterパッケージがインストールされます。インストール完了後に`dpkg -s pyspf-milter`を実行すると以下のようになります。

```shell-session
# dpkg -s pyspf-milter
Package: pyspf-milter
Status: install ok installed
Priority: optional
Section: mail
Installed-Size: 48
Maintainer: Scott Kitterman <scott@kitterman.com>
Architecture: all
Source: spf-engine
Version: 2.9.3-1.1~bpo11+1
Depends: python3:any, python3-spf (>= 2.0.9), postfix | sendmail, adduser, python3-authres, python3-milter, python3-spf-engine (= 2.9.3-1.1~bpo11+1), python3-pkg-resources
Pre-Depends: init-system-helpers (>= 1.54~)
Conffiles:
 /etc/init.d/pyspf-milter 56ada07e525548f3743697cb805837e3
 /etc/pyspf-milter/pyspf-milter.conf 1619f766b0f8c2f4f652d6834695fe4a
Description: Modern milter for SPF checking
 pyspf-milter is a full featured milter for SPF checking. It includes a
 variety of whitelisting mechanisms and policy options to enable it to
 support the widest variety of system requirements.  It is implemented in pure
 Python and uses the python-spf module.  The SPF web site is
 http://www.openspf.net/.  It has been updated to support RFC 7208.
Homepage: https://launchpad.net/spf-engine
#
```

`Version`の行を見ると`2.9.3-1.1~bpo11+1`となっているので、構築したリポジトリにあるバージョンがインストールされたことがわかります。

# まとめ

Debianのリポジトリを自前で作成してパッケージを追加し、作成したリポジトリからパッケージをインストールする方法についてまとめました。これから同様のことを行おうとする方の参考になれば幸いです。

# 参考文献

1. [reprepro(1)のmanページ](https://manpages.debian.org/bullseye-backports/reprepro/reprepro.1.en.html "reprepro(1)のマニュアル")
1. 「reprepro manual」(上のmanページとは別のもので、repreproパッケージをインストールすると`/usr/share/doc/reprepro/manual.html`としてインストールされる)
1. [HOWTO: Create debian repositories with reprepro | Packagecloud Blog](https://blog.packagecloud.io/how-to-create-debian-repository-with-reprepro/ "HOWTO: Create debian repositories with reprepro | Packagecloud Blog")