【rpmbuild】RHEL8系でのopenssl-3.0.3のRPMビルド【openssl3.0】

# openssl-3.0.3のRPMビルド

opensslは1系と3系があるようで、今まで1.1.1k とか 1.1.1oを使ってたけど、
3.0をいつでも導入できるようにしておきたいと思ったので、RPMビルドしてdnfでインストールできるようにした。

かなりハマりどころが多かったのでメモしておく。

# ビルド環境

- OS : Rocky Linux 8

# うまくいったときの手順

## Specファイル作成

下記の通りspecファイルを作成

```
$ rpmdev-newspec openssl3
```

重要な点をコメントで記載している。

```spec:openssl3.spec
Summary:        Utilities from the general purpose cryptography library with TLS implementation
Name:           openssl3
Version:        3.0.3
Release:        1%{?dist}

License:        Apache License v2.0
URL:            https://www.openssl.org/
Source:         https://www.openssl.org/source/%{name}-%{version}.tar.gz

BuildRequires:  perl-IPC-Cmd, perl-Pod-Html
Requires:       %{name}-libs

%description
The OpenSSL Project develops and maintains the OpenSSL software - a robust, commercial-grade, full-featured toolkit 
for general-purpose cryptography and secure communication.
The project's technical decision making is managed by the OpenSSL Technical Committee (OTC)
and the project governance is managed by the OpenSSL Management Committee (OMC).
The project operates under formal Bylaws.

%package devel
Summary:        A general purpose cryptography library with TLS implementation
Group:          Development

%description devel
Files for development of applications which will use OpenSSL

%package libs
Summary:        A general purpose cryptography library with TLS implementation
Group:          Development/Libraries

%description libs
Libraries for openssl 3.0.3

%global debug_package %{nil}

# /usr/bin/openssl3 となるように prefixを設定
%global prefix /usr

# opensslのインストールディレクトリは/usr/local/sslとする
%global openssldir /usr/local/ssl

# そのままだとディレクトリが存在しないといわれるので、-n で名前指定
%prep
%setup -n openssl-%{version}

%build
# NOTES-UNIX.md の内容に沿った configure オプションは↓の通り
./Configure --prefix=%{prefix} --openssldir=%{openssldir} '-Wl,-rpath,$(LIBRPATH)'
%make_build

%install
%make_install

# /usr/bin , /usr/lib64 を配置
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_libdir}
# opensslの実行バイナリをリネーム (そのままだとデフォルトのopenssl(1.1~)と競合するため)
mv %{buildroot}%{_bindir}/openssl %{buildroot}%{_bindir}/%{name}

%clean
rm -rf $RPM_BUILD_ROOT

# libssl.so.1 や libcrypto.so.1 などはいらないため、パッケージに含めなくてもエラーが出ないようにする。
%define _unpackaged_files_terminate_build 0

%files
%doc doc/
%{_bindir}/openssl3
%{prefix}/share/man/man1
%{prefix}/share/man/man5
%{prefix}/share/man/man7
%{prefix}/share/doc/openssl/html/man1/*
%{prefix}/share/doc/openssl/html/man5/*
%{prefix}/share/doc/openssl/html/man7/*
%{openssldir}/openssl.cnf*
%{openssldir}/ct*
%{openssldir}/misc/*

%files devel
%{prefix}/include
%{prefix}/share/man/man3
%{prefix}/share/doc/openssl/html/man3/*

%files libs
%{_libdir}/engines-3
%{_libdir}/libssl.so.3
%{_libdir}/libcrypto.so.3
%{_libdir}/ossl-modules

%post
%{_sysbindir}/ldconfig

%postun
%{_sysbindir}/ldconfig

%changelog
* Mon Jun 13 2022 cokemaniaIIDX
- Version3.0 の最初のリリース
```

## rpmbuild実行

```
# rpmbuild -ba openssl3.spec
```

完了までだいたい8分くらいかかる(man3 のページ数が異常に多い)。

## インストールできるか確認

`RPMS/x86_64`に`openssl3-3.0.3-1.el8.x86_64.rpm`ができているので、これを指定してdnf installを試みる。
openssl3-3.0.3をインストールする際に、libssl.so.3 , libcrypto.so.3がないとインストールできないと言われるので、
先に`openssl3-libs-3.0.3`をインストールする。

```
# dnf install openssl3-libs-3.0.3-1.el8.x86_64.rpm -y
# dnf install openssl3-3.0.3-1.el8.x86_64.rpm 
Last metadata expiration check: 1:12:43 ago on Wed Jun 15 05:10:47 2022.
Dependencies resolved.
============================================================================================
 Package        Architecture     Version              Repository          Size
============================================================================================
Installing:
 openssl3       x86_64           3.0.3-1.el8          @commandline       5.0 M

Transaction Summary
============================================================================================
Install  1 Package

Total size: 5.0 M
Installed size: 22 M
Is this ok [y/N]: y
Downloading Packages:
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :     1/1 
  Installing       : openssl3-3.0.3-1.el8.x86_641/1 
  Running scriptlet: openssl3-3.0.3-1.el8.x86_641/1 
/var/tmp/rpm-tmp.u49moA: line 1: fg: no job control
warning: %post(openssl3-3.0.3-1.el8.x86_64) scriptlet failed, exit status 1

Error in POSTIN scriptlet in rpm package openssl3
  Verifying        : openssl3-3.0.3-1.el8.x86_641/1 

Installed:
  openssl3-3.0.3-1.el8.x86_64

Complete!
```

無事インストールできた！

## 確認

- version確認

```
[root@7e73e7d8486c x86_64]# openssl3 version
OpenSSL 3.0.3 3 May 2022 (Library: OpenSSL 3.0.3 3 May 2022)
[root@7e73e7d8486c x86_64]# openssl version
OpenSSL 1.1.1k  FIPS 25 Mar 2021
```

3と1がちゃんと共存している。

- opensslコマンド確認

LetsEncryptで発行しているTLS証明書をopensslコマンドを使って情報表示できるか確認

```
# openssl3 x509 -noout -dates -in CERTS/fullchain2.pem 
notBefore=May 21 10:19:47 2022 GMT
notAfter=Aug 19 10:19:46 2022 GMT
```

ちゃんと確認できた！

# ハマったところ

## インストール時にコンフリクトが発生してしまう

現環境(RockyLinux8)ではopenssl-1.1.1kがデフォルトでインストールされていて、
共有ライブラリのopenssl-libs-1.1.1kも同様にインストールされている。

opensslという名前でビルドすると、
dnfでインストールするときに、既にインストールされている1.1.1kのものを上書きしようとして(? 挙動の詳細はよくわかってない)、
インストールできなくなってしまう。

解決策としては、パッケージの名前をopensslではなく、`openssl3`としてビルドする。
opensslはopensslだけど別物としてインストールすればコンフリクトは回避できるよねという理屈

実際、dnf search opensslすると、
Rocky8ではepelで提供されているopensslとopenssl3の2つがヒットする。
dnf install openssl3 で、openssl-1.1.1がインストールされている状態でも、
openssl3がインストールできる。

epelを見習って、openssl3という名前で再ビルドしたところ、うまくいった。

### fedoraの場合

fedoraはLinuxディストリビューションの中でも「最新性」を追及しているみたい。
実際、fedoraの最新版(37)コンテナをpullしてみて、opensslのインストールを試みると、
デフォルトでopenssl3.0.3がインストールできることが分かった！

```
$ docker run -it fedora:37
[root@7b36009b31bc /]#

[root@7b36009b31bc /]# dnf install openssl
Last metadata expiration check: 0:00:36 ago on Wed Jun 15 05:45:11 2022.
Dependencies resolved.
=============================================================================
 Package          Architecture        Version             Repository   Size
=============================================================================
Installing:
 openssl          x86_64              1:3.0.3-1.fc37      rawhide     1.1 M
Upgrading:
 openssl-libs     x86_64              1:3.0.3-1.fc37      rawhide     2.1 M

Transaction Summary
=============================================================================
Install  1 Package
Upgrade  1 Package

Total download size: 3.3 M
Is this ok [y/N]: 
```

この場合、opensslがデフォルトで3.0以上なので、
opensslの名前でビルドしたパッケージでも問題なくインストールできた。

## libssl.so.1.1 を必要とされてしまう

openssl-libsは、様々なパッケージに参照されているようで、
このパッケージは容易にアップデートや変更、ましてや削除なんかはできないものである。

mysqlやpython、nginxなどもソースからビルドする際は`--openssl=`でopensslを指定して
./Configureを実行することからも、この事実は明白。

先述のハマりポイントと関連して、CentOS7やRockyLinux8など、
openssl-1.1 がデフォルトのディストリビューションの場合、
openssl-libs-1.1がデフォルトで入っているため、
共有ライブラリにはopenssl-libs-1.1の中身が格納されている。

多くのパッケージはこのopenssl-libs-1.1のライブラリを参照しているため、
ビルドしたopenssl(3.0.3)をインストールしようとすると、
`libssl.so.1 が必要とされていますが、どのパッケージからもインストールできません。`
と言われてしまう。

こちらの問題も、やはりパッケージ名がopensslのため、
共有ライブラリのlibssl.so等が置き換わってしまって、
インストール済みのほかのパッケージに影響を及ぼしてしまうから、
インストールができなくなっていると推察できる。

解決策としては、やはり名前をopenssl3としてビルドを実行する。
これでopensslのライブラリ群も、openssl3-libsとしてインストールされるため、
競合が発生しなくなる。

rpmビルド実行時に、openssl-1.1と同じファイルをインストールできるけど、
これらも競合の発生を防ぐため、パッケージは含めないようにした。

```
↓このへん
warning: Installed (but unpackaged) file(s) found:
   /usr/bin/c_rehash
   /usr/lib64/libcrypto.a
   /usr/lib64/libcrypto.so
   /usr/lib64/libssl.a
   /usr/lib64/libssl.so
   /usr/lib64/pkgconfig/libcrypto.pc
   /usr/lib64/pkgconfig/libssl.pc
   /usr/lib64/pkgconfig/openssl.pc
```

これに関しては、epelのリポジトリのopenssl3のrpmパッケージの中身を参考にした。

```
# rpm -ql openssl3-libs-3.0.1-1.el8.x86_64.rpm
/usr/lib/.build-id
/usr/lib64/engines-3
/usr/lib64/libcrypto.so.3
/usr/lib64/libssl.so.3
/usr/lib64/ossl-modules
/usr/lib64/ossl-modules/legacy.so
```

↑こんなかに入ってないし、OKでしょう。

# おわりに

rpmbuild初心者ながら、opensslの最新版rpmパッケージの作成に成功した。
いろいろなパッケージがopensslのライブラリを利用しているため、競合が多く
ビルドの難易度は非常に高かったが、何とか完成まで持っていけて良かった。

課題点:
実は、epelのrpmのように`dnf install openssl3`を実行した際に依存関係としてopenssl3-libsもインストールされるようにしたかったが、
やり方がわからず、、
SPECファイルの書き方が悪く、Requireでは無理なのか？
もしくは、yumリポジトリに両方入れておけば、リポジトリ参照してインストールする際に一緒にインストールしてくれるのだろうか、、
この辺りは、この後yumリポジトリサーバを自作するので、そこに格納してみて挙動を確かめてみる。

# 参考資料

- [Vine Linux | RPMパッケージの作成方法](https://vinelinux.org/docs/vine6/making-rpm/vine-making-rpm.html)
  - RPMパッケージ作成のノウハウはここにすべて書いてある。

- [philyuchkoff/openssl-RPM-Builder](https://github.com/philyuchkoff/openssl-RPM-Builder/blob/master/openssl3.spec)
  - philyuchkoffさんのRPMビルダーのリポジトリ。非常に参考になりました。

- [RPM build errors: Empty %files file debugsourcefiles.list への対応](https://www.openrtm.org/redmine/issues/4555)
  - debuginfoがバグって作成できなかった際の治し方。