Dockerで自作RPMパッケージを自前のYumリポジトリで公開する環境を作った

大学の授業で、「暇だったらCTFの問題作って」と言われました。そこで、```yum info パッケージ名```などと入力したらパッケージの情報とともにフラッグが表示されるという問題はどうかな？と思いましたので、それを作ってみました。

コードは下記でアップしてますので、ご確認ください。
[https://github.com/fum1h1to/ctfYumRPM](https://github.com/fum1h1to/ctfYumRPM)

# 構成
![ctfYumRPM.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1681329/948de352-296f-7e42-632c-0a0b06956fd8.png)
今回は画像のように、RPMパッケージをインストールするクライアントコンテナと、Yumリポジトリを持ちRPMパッケージを提供するサーバコンテナをDockerで構築し、クライアント側で```yum info -y ctfYumRPM```と入力するとサーバ側からctfYumRPMをインストールできるようにします。

ちなみにパッケージの名前は、```ctfYumRPM```となっています。特に理由はありません。

# 手順
## 1. RPMパッケージを作成する
RPMパッケージの作成は、後に作るクライアントコンテナ、サーバコンテナとは別の環境で作成しても問題ありません。
自分は、新たにDocker上にcentosの環境を準備しそこでRPMパッケージを作成しました。

```bash
$ docker pull centos:centos7
$ docker run -it -d -name centos7 centos:centos7
$ docker exec -it centos7 /bin/bash
```

なお、RPMパッケージを作成する手順については、下記を参考にさせていただきました。
> [自分で作ったRPMパッケージを自前のyumリポジトリで公開してみた](https://note.com/ictlink/n/n74f9e3c13a6e)

### 1. RPMパッケージの作成に必要なツールをインストール
```bash
$ yum groupinstall -y 'Development tools'
$ yum install -y rpmdevtools yum-utils
```

### 2. RPM作成用の環境を生成
```bash
$ rpmdev-setuptree
```
ホームディレクトリに```rpmbuild```というディレクトリができます。

### 3. ```SOURCES```ディレクトリにソースコードを入れる
```ctfYumRPM.c
#include <stdio.h>

int main() {
  printf("flag{This_is_Fake}\n");
  printf("sorry:(\n");
  return 0;
}
```
```ctfYumRPM.c```などとして```~/rpmbuild/SOURCES```に入れます。
これは、クライアント側で自作パッケージをインストールした後に実行できるようになるプログラムです。
今回は簡単な内容で、fakeのフラッグを出力するようにします。

### 4. ```SPECS```フォルダにspecファイルを入れる
specファイルについては詳しく説明しませんが、ここに書く情報が後に```yum info ctfYumRPM```とした際に表示されるため、ここに本当のフラッグを書いておきます。
```ctfYumRPM.spec
%define name ctfYumRPM
%define version 1.0
%define release 1

Name: %{name}
Version: %{version}	
Release: %{release}
Source0: ctfYumRPM.c
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
Summary: Where is flag???
License: flag{This_is_real_flag_Congrats}

%description
Where is flag?????????????????????????????????????

%prep
rm -rf %{buildroot}

%build
gcc /root/rpmbuild/SOURCES/ctfYumRPM.c -o /root/rpmbuild/BUILD/ctfYumRPM

%install
mkdir -p %{buildroot}/usr/bin/
install -m 755 /root/rpmbuild/BUILD/ctfYumRPM %{buildroot}/usr/bin/ctfYumRPM

%clean
rm -rf %{buildroot}

%files
/usr/bin/ctfYumRPM
```
```ctfYumRPM.spec```などとして```~/rpmbuild/SPECS```に入れます。

### 5. パッケージをビルドする
```bash
$ rpmbuild -bb ~/rpmbuild/SPECS/ctfYumRPM.spec
```

### 6. パッケージに署名する
1. 必要なパッケージをインストール
    ```bash
    $ yum install -y gpg
    ```

1. 鍵の生成
    ```bash
    $ gpg --gen-key
    ```
    この際、様々なことを聞かれますが、エンターで大丈夫です。
    パスワードについては、拒否するような選択をしても何度も入力しろと言われますが、そのうち諦めてくれます。
    
    生成した鍵を確認するときは下記コマンドです。
    ```bash
    $ gpg --list-key
    ```

1. 鍵のexport
    ```bash
    $ gpg -export -a '鍵の作成時に指定した本名' > '鍵ファイル名' 
    ```
    自分は、
    ```bash
    $ gpg -export -a 'fumihito' > 'ctfYumRPM.pgp' 
    ```
    としました。

1. 鍵ファイルをRPMのデータベースに登録する
    ```bash
    $ rpm -import '鍵ファイル名'
    ```

1. ```~/.rpmmacros```に内容を追加
    ```.rpmmacros
    %_signature gpg
    %_gpg_name <鍵の所有者名>
    ```
    鍵の所有者名は、鍵の作成時に指定した本名の事です。つまり自分の場合、```fumihito```です。

1. RPMファイルに署名をする
    ```bash
    $ rpm -addsign ~/rpmbuild/RPMS/x86_64/ctfYumRPM-1.-1.x86_64.rpm
    ```
    path内の```x86_64```については環境によっては異なる可能性があります。
    
    RPMファイルを検証してみましょう。
    ```bash
    $ rpm -checksig ~/rpmbuild/RPMS/x86_64/ctfYumRPM-1.0-1.x86_64.rpm
    /root/rpmbuild/RPMS/x86_64/ctfYumRPM-1.0-1.x86_64.rpm: rsa sha1 (md5) pgp md5 OK
    ```
    OKと出れば成功です。

以上で、RPMパッケージの作成は終了です。
作成した```ctfYumRPM-1.0-1.x86_64.rpm```と```ctfYumRPM.gpg```は後に必要となります。

## 2. Yumのリポジトリを作成する
作成にあたって、ディレクトリ構造は下記のような形になっています。
```
│  docker-compose.yml
│
├─client
│  │  dockerfile
│  │
│  └─file
│          ctfYumRPM.gpg
│          fumihito.repo
│
└─server
    │  dockerfile
    │
    └─file
            ctfYumRPM-1.0-1.x86_64.rpm
```

### サーバコンテナを用意する
Dockerでサーバを構築します。
今回RPMファイルを転送するためにHTTPを使うため、ApacheでHTTPサーバを用意します。

```dockerfile
FROM centos:centos7

RUN yum install -y httpd
RUN yum install -y createrepo
RUN mkdir -p /var/www/html/repos/RPMS
ADD ./server/file/ctfYumRPM-1.0-1.x86_64.rpm /var/www/html/repos/RPMS
RUN createrepo /var/www/html/repos
EXPOSE 80
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
```
dockerfileでは、

httpdとcreaterepoをインストール
↓
RPMを格納するディレクトリを作成
↓
ホスト側から用意したRPMパッケージをコンテナ内に取り込む
↓
リポジトリを作成
↓
httpdを起動

という流れを行っています。
これで、自分の作成したRPMパッケージを提供するYumリポジトリを持ったサーバの完成です。

## 3. クライアント側でインストールできるようにする

クライアントコンテナを作成するわけですが、クライアント側で自作のRPMパッケージをyumでインストールするためには、以下2つを行う必要があります。

- RPMパッケージを作成する際に作った鍵ファイルの登録
- ```/etc/yum.repos.d/```配下に自前のYumリポジトリの情報を追加

dockerfileはこのようになりました。
```dockerfile
FROM centos:centos7

ADD ./client/file/ctfYumRPM.gpg /root/
ADD ./client/file/fumihito.repo /etc/yum.repos.d/
RUN rpm -import /root/ctfYumRPM.gpg
```

```fumihito.repo```は下記です。
```fumihito.repo
#fumihito's yum repository
[fumihito]
name=fumihito RPM packages
baseurl=http://web:80/repos/
```
```baseurl=http://web:80/repos/```の箇所はDockerでなければ、YumリポジトリがあるサーバのIPアドレスを記入するのですが、今回はDocker上に構成するので、serviceの名前を書いています。

## 4. ```docker-compose.yml```の作成
最後に```docker-compose.ymel```を作成してクライアントコンテナとサーバコンテナの起動を楽にします。

```docker-compose.yml
version: "3"

services:
  web:
    tty: true
    build: 
      context: .
      dockerfile: ./server/dockerfile
    container_name: ctfYumRPM_repo
    ports: 
     - 8080:80
  
  client:
    tty: true
    build:
      context: .
      dockerfile: ./client/dockerfile
    container_name: ctfYumRPM_client
```

これで、```docker-compose up -d```とコマンドで入力することで、コンテナが起動するようになります。
そして、クライアント側に入り、```ctfYumRPM```をインストール出来たら成功です。
```bash
$ docker exec -it ctfYumRPM_client
$ yum install -y ctfYumRPM
```
確認で下記コマンドを打つと、しっかりとfakeのフラッグが出てくるはずです。
```bash
$ ctfYumRPM
flag{This_is_Fake}
sorry:(
```
CTFの答えとなってしまいますが、パッケージの情報を出すと本当のフラッグも出てくるはずです。
```bash
$ yum info ctfYumRPM
```

# 最後に
CTFの問題としてはすごく簡単な問題ですが、問題の環境を構築するのは大変でした。でも、Dockerってすごく便利ですね。今後も何かCTFの問題を作ってみたいです。
また、RPMやYum、Dockerについて自分の知識が浅い中で作ったので、何かおかしな点がありましたらご指摘していただきたいです。

# 参考文献
- [自分で作ったRPMパッケージを自前のyumリポジトリで公開してみた](https://note.com/ictlink/n/n74f9e3c13a6e)