まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2024/01

我ながら分かりにくいタイトルになってしまいましたが、やりたいことはこういうことです:

データベースに Postgres を使っているケースで、テーブルのある bigint 列にそのレコードの作成日時が記録されているものとします(これ自体はそこまで珍しくないと思っています):
create table items( id varchar(50) primary key, name varchar(100), created bigint );

上の例では items テーブルの "created" 列が bigint 型で定義されていて、このレコードが作成された日時の(ミリ秒単位の)タイムスタンプ値が格納されているものとします。

そしてこの items テーブルに格納されているレコードから、created の値が(例えば)1月17日のものだけを取り出す、というクエリーを実行するにはどのような SQL にすればよいか、という命題です。

要は facebook の思い出(過去のこの日)機能のような「何年か前の今日と同じ日付に作成したデータ」を取り出したくなることがあるのです。年や時分秒は違ってもいいので、月と日だけが一致している過去のデータを取り出したい、というケースです。日付が文字列で記録されていれば部分一致検索すればいいだけなので難しくはないと思いますが、これを bigint 型のタイムスタンプで格納されている中で実現するにはどのような SQL を実行すればよいか? というのがやりたいことでした。

で、その方法です。PostgreSQL には to_char() という組み込み関数が存在していて、この関数を使うと(PostgreSQL の)タイムスタンプ値をフォーマットを指定した文字列に変換することができます。また to_timestamp() という組み込み関数もあり、こちらは bigint などの値を(PostgreSQL の)timestamp 型に変換してくれます。

この2つの関数を併用して、例えば、
select id, name, created from items where to_char( to_timestamp( created / 1000 ), 'MM-DD' ) = '01-17' order by created desc

のように実行すると、
  • まずミリ秒単位の created が created / 1000 によって( timestamp 型と同じ)秒単位になり、
  •  to_timestamp( created / 1000 ) によってタイプスタンプ型に変換され、更に、
  •  to_char( to_timestamp( created / 1000 ), 'MM-DD' ) によってタイムスタンプ値が '月-日' というフォーマットの文字列に変換される
といった一連の処理が実行されます(上の SQL 例では、その結果が '01-17' となるレコードの id と name と created を、 created の新しい順に取り出す、という処理をしています)。


さらにおまけを。上の SQL は正しく実行できるのですが、タイムスタンプ値は UTC 時間で計算されるので、このままだと「UTC 時間で1月17日」のデータを取り出すことになります。

これを「日本時間で1月17日」のデータを取り出す場合は9時間のオフセットを考慮して、
select id, name, created from items where to_char( to_timestamp( created / 1000 ) + 9 * 3600, 'MM-DD' ) = '01-17' order by created desc

のように実行することで実現できます。後はこの "9" の部分をプログラムで動的に取得するとか、外部からパラメータ指定できるようにすれば色々なロケールでの過去の同じ月日のデータを取り出すことができる、ということになります。


自作アプリに思い出機能を実装しようとする時に役立つ情報・・・だと思ってます。


RHEL(RedHat Enterprise Linux) を「インターネットから隔離された状態で」使う、というのが本ブログエントリのテーマです。セキュリティ要件など何らかの事情があってインターネットに接続することが許されていない環境下で RHEL を使う、という場合の設定手順を紹介します。

これ、CentOS だと特に意識することもなく、インストール用の DVD などを用意して普通にインストールできるし、(インターネットに接続できない不便さはあっても)そのまま使い続けることもできます。ただ同じことを RHEL でやろうとするとうまくいかないことがあって、それを回避するにはどうすればよいか、を自分なりに調べた内容のアウトプットです。


【RHEL と CentOS との違い】
まず RHEL は CentOS と同じように使えないのでしょうか? その違いは何で、どのような影響があるのでしょうか?

今回のテーマとしてとりあげる問題はサブスクリプションマネージャー(subscription-manager)にあります。RHEL は CentOS とは異なり、サブスクリプションがないと使えません。サブスクリプションは有償のものであったり、開発者向けに無償で提供されているものもありますが、なんらかの登録を行い、その登録情報をインストール中に入力することで使えるようになります(下画面ではインストール中に表示される「Red Hat に接続」という部分で正しいサブスクリプション情報を入力しないと先に進めません。つまりこの時点ではインターネット接続が必要です):
2024010701

2024010702


なお一部のクラウドベンダーでは初めから RHEL がインストールされたイメージを使うことができますが、この場合もサブスクプションを無視して使えているわけではなく、特殊なサブスクリプションが適用された状態になっています(RHEL のサブスクリプション料金がクラウドの料金に含まれているはずです)。サブスクリプションマネージャーはこのサブスクリプション情報を管理するシステムを提供しています。


【サブスクリプションマネージャーが管理しているもの】
このサブスクリプションマネージャーが管理しているものの1つが「yum(dnf) のリポジトリ」です。例えば RHEL の場合、RedHat が提供しているツール類に加えて ansible など RHEL のサブスクリプションを所有している前提で使うことのできるツールが提供されていて、その利用のためのリポジトリ情報(yum などでインストールするためにサーバー情報や鍵情報)がサブスクリプションマネージャーによって管理されています。サブスクリプションマネージャーが yum リポジトリを管理していることで(インターネットに繋がっている RHEL や、特定のクラウド環境内で使っている RHEL は)RHEL 向けに提供されているツールを使うことができる、という仕組みになっています。そしてその仕組みを管理しているのがサブスクリプションマネージャーということになります。


【サブスクリプションマネージャーが有効な場合に不都合となるケース】
そしてこの適用されているサブスクリプション情報は、インストール後の利用時にも影響を与え続けることになります。たとえネットワークインターフェースを無効にするなどしてインターネットに(物理的/論理的に)繋がっていない状況を作り出したとしても、サブスクリプション情報はシステムに残り続けます。

これが「インターネットに接続していない環境」だと不都合な問題が発生します。以下、具体的な画面(VirtualBox 内にインストールした RHEL 9.3 の画面)を交えて紹介します。

まず RHEL 9.3 をインストールします。上述のようにサブスクリプション情報を入力/認証する必要があるため、初期セットアップ時にはインターネット接続が必要です。この時点では VirtualBox 側のネットワーク設定でアダプター割り当てを「NAT」にしていました:
2024010801


この状態で(インターネット接続がある状態で) "yum repolist" を実行して現在有効な yum のリポジトリを確認すると2つのリポジトリが有効に登録されていました。そして "yum install tmux"(tmux というツールをインストールする際のコマンド)を実行すると登録されているリポジトリから必要なモジュールを探し、「インストールしますか?」と聞かれるところまで実行できました("n" を押してインストールをキャンセルします)。期待通りの挙動になっています:
2024010802


この状態からネットワークを無効にします。今回は VirtualBox 側の割り当て設定を「未割当」にしました。RHEL 側からはネットワークアダプターそのものは認識できるが、DHCP などで IP アドレスが割り当てられることもなく、実質的に使えない状態になるはずです。こうすることで物理的なサーバーをインターネットが使えない環境に移動させた時と近い状況が作り出せているはずです:
2024010803


この状態で先ほどと同じコマンドを実行してみます。"yum repolist" を実行すると先ほどと同じ結果になり、リポジトリそのものは2つ登録されたままになっていることがわかります。しかし "yum install tmux" を実行して tmux をインストールしようとすると、今度は(ネットワークが使えず)リポジトリ先にアクセスすることができないため失敗してしまいます。これもここまでは期待通りの挙動と言えます:
2024010804


ただ問題は「使えないリポジトリが登録されたままになっている」点です。この状態から tmux などのツールを追加インストールするにはローカルリポジトリを作成するなどすることで可能ではあるのですが、この使えないリポジトリは邪魔なので消す必要があります。

リポジトリの情報は /etc/yum.repos.d/ フォルダ以下に .repo という拡張子を持ったファイルを用意しておくことでシステムに認識させることができます。RHEL の場合も /etc/yum.repos.d/redhat.repo というファイルが用意されており、ここでサブスクリプション情報に合わせたリポジトリが使えるようになっています。 というわけでいったんこのファイルを(redhat_repo などに)リネームして、RHEL のリポジトリが無効になるよう更新してみます。 本来ならばこれで登録リポジトリは空になるはずだと期待していたのですが、実際にやっていると登録リポジトリは変わらず2つ存在したままで、しかもリネームしたはずの /etc/yum.repos.d/redhat.repo ファイルがいつの間にか復活(!?)していました:
2024010805


これがサブスクリプションマネージャーの挙動です。ansible など RHEL のサブスクリプションがないと使えないリポジトリも含めて管理されており、何かの手違いでサブスクリプション情報が消えてしまわないよう(ansible などがインストールできなくなってしまわないよう)リポジトリ情報はリポジトリファイル以外でも管理されていて、変更が加わってもリポジトリの更新時に再度自動で復活させるような挙動を見せます。

この挙動により RHEL をインターネット接続がない環境で使おうとした際の、上述の「使えないリポジトリが登録されたままになっている」点を解決しようとしてもすぐに元に戻ってしまう、という問題が残ってしまうのでした。


(↓以下補足)
自分だけのケースかもしれませんが少し補足します。上述のようなインターネット接続が使えない環境下で RedHat OCP(OpenShift Container Platform) をローカルインストールしようと試みた際に RedHat が提供している ocp4-helpernode というツールを使おうとしました。 その時も上述のように RedHat のリポジトリが自動で復活する問題を抱えていたのですが、「ローカルリポジトリを作って、自動復活する前に各種ツールをインストールして準備」したつもりでした。 が、それだと最終的に ocp4-helpernode を使おうとした段階になって RedHat のリポジトリが復活してしまい、「準備ツールをインストールした時と環境が異なっている」ことが検知されたようなエラーメッセージ(↓)が表示されて止まってしまいました:

  :
  :
TASK [set_fact] ****************************************************************
ok: [localhost]

TASK [Install needed packages] *************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failures": [], "msg": "Unknown Error occurred: Some packages from local repository have incorrect checksum", "rc": 1, "results": []}

自分の場合はもともとこの環境を作りたくて調べていたのですが、CentOS とは異なり、RHEL だとこのリポジトリを管理するサブスクリプションマネージャーをどうにかしないといけない、ということがわかったのでした。

なお、同じこと(OCP をネットワーク接続のない環境で物理サーバーにインストール)を RHEL ではなく CentOS を使って行うことも検討したのですが、こちらの場合は RHEL のサブスクリプションには含まれている ansible や ansible の関連ツールのインストールが別途必要になったり、上で紹介した ocp4-helpernode 自体が RHEL 向けだけに提供されていたりして、別の部分で余計にややこしそうだったのであきらめました。
(↑以上補足)


というわけで、RHEL のサブスクリプションマネージャーは便利な反面、利用形態によっては邪魔になってしまうケースもあることがわかったのでした。


【サブスクリプションマネージャーを無効にする方法】
前置きの長いブログになってしまいましたが、このような背景からサブスクリプションマネージャーを無効にしたい、という需要も少なからずあるように感じています。もちろん無効にすることで RHEL が管理している最新のリポジトリは使えなくなってしまうし、ansible など RHEL のサブスクリプションと合わせて提供されるツールもインストールできなくなってしまうというリスクがあります。そのリスクを理解した上で「それでも無効にしたい」場合だけ実施するようにしてください(私は責任取れません)

RHEL のサブスクリプションマネージャーは以下のコマンドで無効にできます:
# subscription-manager config --rhsm.manage_repos=0

上述のような「インターネット接続がない環境下でいろいろなツールをセットアップ」しないといけないようなケースでは、まず上のコマンドを実行してサブスクリプションマネージャーを無効化した上で、DVD などから必要なモジュールを取り出し、あるいはファイルのみダウンロードしたりした上で、必要に応じてローカルリポジトリを用意してインストール/セットアップを行う、という手順が必要になると思います。

自分のように OCP をネットワーク接続のない環境でセットアップ、、なんて無茶なことをやろうとする場合に自分が調べてこのブログにまとめた関連情報(躓きそうな箇所に関する情報)を以下に記載しておくので参考にしていただけると嬉しいです:




(参照)
ネットワークが制限された環境でのクラスターのベアメタルへのインストール
OpenShift on Power 完全オフライン環境でのインストール

 

クラウドインスタンスだったり、VM だったりで RHEL9.x を使う機会が増えてきました。個人的にはどちらかというと最近は Debian/Ubuntu 派で、Ubuntu の選択肢がある場合は Ubuntu を使うことが多かったと思っています。CentOS 6 はヘビーユーザーでしたが、しばらく CentOS/RHEL を使っていなかったこともあり、久しぶりに使って戸惑うこともでてきています。まあググればなんとかなることが多いんですけど、

そんな中でも解決までに特に時間を要したというか、"RHEL9 からの変更点" の影響を受けて戸惑ったのが SSH 接続でした。SFTP も含めて RHEL9 に SSH 接続したり、RHEL9 から SSH 接続する際にうまく動かない(接続できない)ことが頻発して対処に手間取ったことを、その原因や対処法含めて以下にメモしておきました。


【そもそも何が起こったのか】
IBM Cloud を使って RHEL9.x(正確には 9.2)の VM を作りました。RHEL 8.8 を使うこともできて、8.8 では SSH 秘密鍵ファイルを指定して TeraTerm で接続していました(何も問題ありませんでした)。

そして RHEL 8.8 で使っていた時と全く同じ SSH 鍵ペアを使って RHEL 9.x の VM インスタンスを構成し、TeraTerm を使って SSH で VM に接続しようとして・・・ 接続できませんでした??
2024010401


あれ? 鍵ペアファイルは全く同じなのに、なんで RHEL8.x だと接続できて、RHEL9.x だと接続できないの?? ・・・という点に気付いたのが事の起こりでした。


【RHEL9.x で何が変わったのか】
挙動から考えると「RHEL8.x と RHEL9.x で、SSH 接続に関する何らかの仕様変更があった」と推測するのが自然だと思いました。そういうキーワードで調べているうちに以下の記事を見つけました:
Tera Term で Rocky9/RHEL9 にログインできない(2022/12/7)


キーワード的には自分の手元で起こっている現象に近いことが書かれているような気がして調べてみました。記事内の情報をまとめると、このようなことが書かれていました:
  • RHEL8.x までは OpenSSH の認証方式に RSA/SHA-1 をデフォルトとして使っていた
  • TeraTerm 4.x 以前も OpenSSH の認証方式は RSA/SHA-1 だった
  • RHEL9.x からは RSA/SHA-256 などのより強い認証方式がデフォルトとして採用された
  • TeraTerm 4.x 以前のものでは接続できないので、TeraTerm の対応を待つか、RSA/SHA-256 をサポートした別の SSH クライアントを使う必要がある
なるほど、とりあえず RHEL9.x で認証方式の変更があって、それが原因らしい、ということまではわかりました。

なお同じ理由で RHEL9.x から古い認証方式だけをサポートしたシステムへの SSH 接続もできなくなっています:
2024010402


【RHEL9.x への SSH ログイン】
さて原因が分かったところで、改めてどうすればよいか? を考えます。まずは「RHEL9.x への SSH ログイン」です。

こちらは実は上の記事の最後に追記があり、
  • TeraTerm 5.x では対応済み
になっています。したがって TeraTerm 5.x をダウンロード/インストールして使うことで RHEL9.x へも SSH ログインができるようになります。

TeraTerm のダウンロードはこちらから:
https://github.com/TeraTermProject/osdn-download/releases


【RHEL9.x からの SSH ログイン】
もう1つ、反対方向の SSH ログインについて、こちらは少し厄介な問題です。接続先からすると全く知らない(未対応の)認証方式でアクセスされるので接続先を RSA/SHA-256 対応するよう、アップグレードするしかないような気もします(それが可能な場合はその方法でも対応できます)。

ただそうはいかないケースが多いと思うので、対応策の1つとして「一時的に RSA/SHA-1 をデフォルトにするよう暗号化ポリシーを変更」する方法を紹介します。

RHEL9.x では "update-crypto-policies" という CLI コマンドで暗号化ポリシーの確認や変更ができます。まず root でログインしている状態から、以下のコマンドでデフォルトの暗号化ポリシーを確認してみます(青字が出力結果):
# update-crypto-policies --show
DEFAULT

"DEFAULT" とだけ表示されました。これは特に何も設定されていない(つまり初期値の暗号化ポリシーが使われている)ことを示しています。この状態から変更する必要があります。

では一時的に "SHA1" に変更してみます:
# update-crypto-policies --set DEFAULT:SHA1
Setting system policy to DEFAULT:SHA1
Note: System-wide crypto policies are applied on application start-up.
It is recommended to restart the system for the change of policies
to fully take place.

この状態で再度デフォルトの暗号化ポリシーを確認してみます:
# update-crypto-policies --show
DEFAULT:SHA1

今度は "DEFAULT:SHA1" と表示されました。これでデフォルトの暗号化ポリシーが "SHA1" に変更できました。この状態であれば古い Linux へも SSH 接続が可能になっています。

目的の SSH 接続が完了してログアウトまでしたら、最後にデフォルト暗号化ポリシーも元に戻しておくことにします:
# update-crypto-policies --set DEFAULT
Setting system policy to DEFAULT:SHA1
Note: System-wide crypto policies are applied on application start-up.
It is recommended to restart the system for the change of policies
to fully take place.

# update-crypto-policies --show
DEFAULT

これで元の暗号化ポリシーに戻すこともできました。

 

2024 年になってから yum(dnf) 関連のブログエントリばかり書いてる気がしますが、今回のテーマも yum(dnf) 関連です。

前回のブログでも書いたのですが、yum は指定したモジュールを「ダウンロードだけ実行する(インストールは行わない)」ことを指定するパラメータがあります:
"yum install xxxxx" でインストールされるモジュールをダウンロードだけする

yum はダウンロードした .rpm ファイルを一時フォルダに格納して、その .rpm ファイルを使ってローカルインストールを実行します。インストールは普通に rpm ツールを使って実行するだけなのですが、今回のブログテーマはその手前、「ダウンロード」する部分に着目します。

例えば以下のような場面に遭遇しているとします(あまり関係ないですが RHEL 8.8 で実行した内容です):
# yum install ansible
Updating Subscription Management repositories.
Last metadata expiration check: 2:22:50 ago on Tue 02 Jan 2024 05:03:04 AM UTC.
Dependencies resolved.
================================================================================
 Package  Arch    Version           Repository                             Size
================================================================================
Installing:
 ansible  noarch  2.9.27-1.el8ae    ansible-2-for-rhel-8-x86_64-rpms       17 M
Installing dependencies:
 sshpass  x86_64  1.09-4.el8        rhel-8-for-x86_64-appstream-eus-rpms   30 k

Transaction Summary
================================================================================
Install  2 Packages

Total download size: 17 M
Installed size: 96 M
Is this ok [y/N]:

コマンドラインからは "yum install ansible" と入力しました。"ansible" というモジュールをインストールしようとしています。 色々と調べた結果、"ansible" というツールに加えて、その実行条件となる "sshpass" というライブラリモジュールも(現在の環境には足りてないと判断されて)インストールしようとしている、という場面です。通常はここで "y" と入力してインストール開始、、という流れになると思っています。普通はこの画面にこれ以上着目する必要もないのですが、今回は(たまにはいい機会だと思うので)この画面をもう少し詳しく見てみます。

このピンク色の部分をよく見ると、以下のような挙動を行おうとしていることが分かります:
ansible-2-for-rhel-8-x86_64-rpms リポジトリから ansible-2.9.27-1.el8ae.noarch.rpm をダウンロード(約 17MB
rhel-8-for-x86_64-appstream-eus-rpms リポジトリから sshpass-1.09-4.el8.x86_64.rpm をダウンロード(約 30kB


ではこれらの2つのファイルは具体的にはどこからダウンロードされるのでしょう? その答を調べるにはリポジトリに登録されている情報を調べる必要があります。CentOS や RHEL の場合、"/etc/yum.repos.d/" 以下に ".repo" という拡張子を持つファイルが1つ以上あり、その中にリポジトリの情報が記述されています。

私の環境では "/etc/yum.repos.d/redhat.repo" というファイルが1つだけ存在していて、このファイルの中に全てのリポジトリの情報が含まれていました。その一部だけを紹介しますが、以下のような内容になっていました(一部伏字にしています):
  :
  :
[rhel-8-for-x86_64-baseos-rpms]
name = Red Hat Enterprise Linux 8 for x86_64 - BaseOS (RPMs)
baseurl = https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/rhel8/8.8/x86_64/baseos/os
enabled = 1
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
sslverify = 1
sslcacert = /etc/rhsm/ca/katello-server-ca.pem
sslclientkey = /etc/pki/entitlement/xxxxxxxxxxxx-key.pem
sslclientcert = /etc/pki/entitlement/xxxxxxxxxxxx.pem
metadata_expire = 86400 enabled_metadata = 1 : : [rhel-8-for-x86_64-appstream-eus-rpms] name = Red Hat Enterprise Linux 8 for x86_64 - AppStream - Extended Update Support (RPMs) baseurl = https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/eus/rhel8/8.8/x86_64/appstream/os
enabled = 1 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/katello-server-ca.pem sslclientkey = /etc/pki/entitlement/xxxxxxxxxxxx-key.pem
sslclientcert = /etc/pki/entitlement/xxxxxxxxxxxx.pem
metadata_expire = 86400 enabled_metadata = 1 : : [ansible-2-for-rhel-8-x86_64-rpms] name = Red Hat Ansible Engine 2 for RHEL 8 x86_64 (RPMs) baseurl = https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os
enabled = 1 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/katello-server-ca.pem sslclientkey = /etc/pki/entitlement/xxxxxxxxxxxx-key.pem
sslclientcert = /etc/pki/entitlement/xxxxxxxxxxxx.pem
metadata_expire = 86400 enabled_metadata = 1 : :

ピンク色で記載しているのがリポジトリ名です。今回の処理でダウンロード元に指定されている2つのリポジトリがともに含まれていることがわかります(今回はリポジトリファイルが1つだけなので、1つのファイルを確認するだけで分かったのですが、複数のリポジトリファイルが存在している場合は全て調べないと分からない可能性があります)。

ではリポジトリファイルに記載されている情報を使って、目的の2つのファイルをダウンロードしてみます(単にダウンロードするだけなら前回のブログの応用で可能なのですが、今回は yum の仕組みを理解することを目的として wget を使ってダウンロードすることにします)。

では wget を使って2つのファイルをダウンロードしてみます。まずは ansible のファイルをダウンロードしてみます。

yum の実行結果やリポジトリファイルに書かれている情報から、ansible のファイルは ansible-2.9.27-1.el8ae.noarch.rpm というファイル名であることや、このファイルは(リポジトリの baseurl である)以下の文字列を含む URL からダウンロードされることがこの時点で推測できます:
https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/eus/rhel8/8.8/x86_64/appstream/os

では単純に https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/eus/rhel8/8.8/x86_64/appstream/os/ansible-2.9.27-1.el8ae.noarch.rpm という URL からダウンロードできるのでは? という予想もあると思いますが(現にそういう単純なパターンのケースもありますが)、ここはちゃんと調べてみましょう。ここからは wget を併用して調べてみます(手元の PC から直接接続できる URL であれば wget ではなくブラウザを使ってもいいのですが、以下の手順ではネットワークが繋がっていない可能性があることと、鍵ファイルも必要になることから wget を使うことにします。wget の方法で覚えておけば色んなケースに対応できてある意味安心です)。

まずはシンプルに baseurl の値から wget を実行してみます。一般的には baseurl の値に HTTP クライアントでアクセスするとファイルやフォルダの一覧(の HTML)が表示されるので、その画面から次にどの URL にアクセスすればよいか、を推測することができます。

というわけで wget で baseurl の値をヒントにアクセスしてみます:
# wget https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os
--2024-01-02 09:59:30--  https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os
Resolving rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)... 161.xx.xx.xx
Connecting to rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)|161.xx.xx.xx|:443... connected.
HTTP request sent, awaiting response... 403 [('PEM routines', 'PEM_read_bio', 'no start line')]
2024-01-02 09:59:30 ERROR 403: [('PEM routines', 'PEM_read_bio', 'no start line')].

おっと、接続はできているのですが 403 エラーが出てしまいました。「権限がない」という意味ですが、これは??

この 403 エラーの原因はリポジトリに記述されていました。改めて ansible-2-for-rhel-8-x86_64-rpms リポジトリの内容を確認してみます:
  :
  :
[ansible-2-for-rhel-8-x86_64-rpms]
name = Red Hat Ansible Engine 2 for RHEL 8 x86_64 (RPMs)
baseurl = https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os
enabled = 1 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/katello-server-ca.pem sslclientkey = /etc/pki/entitlement/xxxxxxxxxxxx-key.pem
sslclientcert = /etc/pki/entitlement/xxxxxxxxxxxx.pem

metadata_expire = 86400 enabled_metadata = 1 : :

よく見ると、この URL にアクセスするには SSL の証明書が必要という情報が書かれていました。この情報が抜けていてアクセスに失敗していたようです。sslcacert は CA 証明書、sslclientkey は秘密鍵、sslclientcert はクライアント証明書で、それぞれのファイルの位置がフルパスで記述されています。

そして wget では以下のオプションを指定することができます:
 ・CA証明書: "--ca-certificate=(ファイルパス)"
 ・秘密鍵: "--private-key=(ファイルパス)"
 ・クライアント証明書: "--certificate=(ファイルパス)"

これらの情報を使い、CA 証明書、秘密鍵、クライアント証明書をそれぞれ指定するオプションを追加して再度先ほどのコマンドを実行してみます:
# wget --ca-certificate=/etc/rhsm/ca/katello-server-ca.pem --private-key=/etc/pki/entitlement/xxxxxxxxxxxx-key.pem --certificate=/etc/pki/entitlement/xxxxxxxxxxxx.pem https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os
--2024-01-02 10:19:58--  https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os/
Loaded CA certificate '/etc/rhsm/ca/katello-server-ca.pem'
Resolving rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)... 161.xx.xx.xx
Connecting to rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)|161.xx.xx.xx|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 407 [text/html]
Saving to: ‘index.html’

index.html          100%[===================>]     407  --.-KB/s    in 0s

2024-01-02 10:19:58 (29.8 MB/s) - ‘index.html’ saved [407/407]

今度は成功したようです。wget は text/html のウェブページに対して(ファイル名がないような URL で)実行すると、結果を "index.html" というファイルに保存してくれます。今回も成功して "index.html" というファイルができていたので、その内容を確認すると以下のように記載されていました:
        <!DOCTYPE html>
        <html>
            <body>
                <ul>

                    <li><a href="Packages/">Packages/</a></li>

                    <li><a href="config.repo">config.repo</a></li>

                    <li><a href="repodata/">repodata/</a></li>

                </ul>
            </body>
        </html>

3つのパスへのリンクが含まれる HTML になっていました。一般的にインストールファイルは "Packages/" フォルダ以下にあるので、今度はさっきの URL の後ろに "Packages/" を足して実行してみます:
# wget --ca-certificate=/etc/rhsm/ca/katello-server-ca.pem --private-key=/etc/pki/entitlement/xxxxxxxxxxxx-key.pem --certificate=/etc/pki/entitlement/xxxxxxxxxxxx.pem https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os/Packages/
--2024-01-02 10:19:58--  https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os/Packages/
Loaded CA certificate '/etc/rhsm/ca/katello-server-ca.pem'
Resolving rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)... 161.xx.xx.xx
Connecting to rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)|161.xx.xx.xx|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 295 [text/html]
Saving to: ‘index.html.1’

index.html.1        100%[===================>]     295  --.-KB/s    in 0s

2024-01-02 10:34:15 (8.24 MB/s) - ‘index.html.1’ saved [295/295]

今度は "index.html.1" というファイルに結果が保存されたようです。このファイルを確認します:
        <!DOCTYPE html>
        <html>
            <body>
                <ul>

                    <li><a href="a/">a/</a></li>

                    <li><a href="s/">s/</a></li>

                </ul>
            </body>
        </html>

ダウンロードされる .rpm ファイルは "Packages/" フォルダ直下に用意されているケースもありますが、多くの場合(ファイルが多いこともあってか)ファイル名の頭文字がついたサブフォルダの下に用意されているケースが多いです。今回のそのようでこの "Packages/" フォルダには "a/" と "s/" というサブフォルダが用意されていました。今回の目的のファイル名は "ansible-2.9.27-1.el8ae.noarch.rpm" だったので、おそらく "a/" サブフォルダの中に保存されている可能性が高いと推測できます。

というわけで再度、今度は URL の最後に更に "a/" を付けて wget を再実行してみます:
# wget --ca-certificate=/etc/rhsm/ca/katello-server-ca.pem --private-key=/etc/pki/entitlement/xxxxxxxxxxxx-key.pem --certificate=/etc/pki/entitlement/xxxxxxxxxxxx.pem https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os/Packages/a/
--2024-01-02 10:19:58--  https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os/Packages/a/
Loaded CA certificate '/etc/rhsm/ca/katello-server-ca.pem'
Resolving rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)... 161.xx.xx.xx
Connecting to rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)|161.xx.xx.xx|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 7647 (7.5K) [text/html]
Saving to: ‘index.html.2’

index.html.2        100%[===================>]   7.47K  --.-KB/s    in 0s

2024-01-02 10:43:45 (293 MB/s) - ‘index.html.2’ saved [7647/7647]

今度は "index.html.2" というファイルに結果が保存されたようです。このファイルを確認します:
        <!DOCTYPE html>
        <html>
            <body>
                <ul>

                    <li><a href="ansible-2.8.0-1.el8ae.noarch.rpm">ansible-2.8.0-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-2.8.1-1.el8ae.noarch.rpm">ansible-2.8.1-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-2.8.2-1.el8ae.noarch.rpm">ansible-2.8.2-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-2.8.3-1.el8ae.noarch.rpm">ansible-2.8.3-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-2.8.4-1.el8ae.noarch.rpm">ansible-2.8.4-1.el8ae.noarch.rpm</a></li>

                        :
                        :

                    <li><a href="ansible-2.9.26-1.el8ae.noarch.rpm">ansible-2.9.26-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-2.9.27-1.el8ae.noarch.rpm">ansible-2.9.27-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-2.9.4-1.el8ae.noarch.rpm">ansible-2.9.4-1.el8ae.noarch.rpm</a></li>

                        :
                        :

                    <li><a href="ansible-test-2.9.5-1.el8ae.noarch.rpm">ansible-test-2.9.5-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-test-2.9.6-1.el8ae.noarch.rpm">ansible-test-2.9.6-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-test-2.9.7-1.el8ae.noarch.rpm">ansible-test-2.9.7-1.el8ae.noarch.rpm</a></li>

                    <li><a href="ansible-test-2.9.9-1.el8ae.noarch.rpm">ansible-test-2.9.9-1.el8ae.noarch.rpm</a></li>

                </ul>
            </body>
        </html>

"ansible-2.9.27-1.el8ae.noarch.rpm" 、やっと見つかりました!このフォルダの下にあったんですね。

というわけで最後に "ansible-2.9.27-1.el8ae.noarch.rpm" を URL の最後に追加して wget を実行します:
# wget --ca-certificate=/etc/rhsm/ca/katello-server-ca.pem --private-key=/etc/pki/entitlement/xxxxxxxxxxxx-key.pem --certificate=/etc/pki/entitlement/xxxxxxxxxxxx.pem https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os/Packages/a/ansible-2.9.27-1.el8ae.noarch.rpm
--2024-01-02 10:19:58--  https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/dist/layered/rhel8/x86_64/ansible/2/os/Packages/a/ansible-2.9.27-1.el8ae.noarch.rpm
Loaded CA certificate '/etc/rhsm/ca/katello-server-ca.pem'
Resolving rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)... 161.xx.xx.xx
Connecting to rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)|161.xx.xx.xx|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 17705052 (17M) [application/octet-stream]
Saving to: ‘ansible-2.9.27-1.el8ae.noarch.rpm’

ansible-2.9.27-1.el 100%[===================>]  16.88M  --.-KB/s    in 0.06s

2024-01-02 10:53:08 (270 MB/s) - ‘ansible-2.9.27-1.el8ae.noarch.rpm’ saved [17705052/17705052]

長い道のりを経て、やっと "ansible-2.9.27-1.el8ae.noarch.rpm" がダウンロードできました!

もう1つのファイル "sshpass-1.09-4.el8.x86_64.rpm" も同様にして "rhel-8-for-x86_64-appstream-eus-rpms" リポジトリからダウンロードできます:
# wget --ca-certificate=/etc/rhsm/ca/katello-server-ca.pem --private-key=/etc/pki/entitlement/xxxxxxxxxxxx-key.pem --certificate=/etc/pki/entitlement/xxxxxxxxxxxx.pem https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/eus/rhel8/8.8/x86_64/appstream/os/Packages/s/sshpass-1.09-4.el8.x86_64.rpm
--2024-01-02 10:19:58--  https://rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com/pulp/repos/customer/Library/content/eus/rhel8/8.8/x86_64/appstream/os/Packages/s/sshpass-1.09-4.el8.x86_64.rpm
Loaded CA certificate '/etc/rhsm/ca/katello-server-ca.pem'
Resolving rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)... 161.xx.xx.xx
Connecting to rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com (rhha02.updates.jp-tok.iaas.service.xxxxxxxxxx.com)|161.xx.xx.xx|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 30568 (30K) [application/octet-stream]
Saving to: ‘sshpass-1.09-4.el8.x86_64.rpm’

sshpass-1.09-4.el8. 100%[===================>]  29.85K  --.-KB/s    in 0s

2024-01-02 11:07:37 (185 MB/s) - ‘sshpass-1.09-4.el8.x86_64.rpm’ saved [30568/30568]

まとめると、、
・yum コマンドの実行結果にリポジトリの情報が含まれている
・/etc/yum.repos.d/ 以下にリポジトリの URL や鍵に関する情報が含まれている
・これらの情報を組み合わせつつ、wget でフォルダ階層を調べながら目的ファイルの URL を調べていくことでファイルのダウンロードも可能

となります。

前回に引き続き yum(dnf) 関連の小ネタです。


前回のおさらいも含めますが、yum を使って、例えば "wget" をインストールしようとする場合、以下のような "yum install" コマンドを実行することになります:
# yum install wget
Updating Subscription Management repositories.
Last metadata expiration check: 1:19:58 ago on Mon Jan  1 09:30:11 2024.
Dependencies resolved.
================================================================================
 Package Arch      Version            Repository                           Size
================================================================================
Installing:
 wget    x86_64    1.21.1-7.el9       rhel-9-for-x86_64-appstream-rpms    794 k

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

Total download size: 794 k
Installed size: 3.1 M
Is this ok [y/N]:

普通にインターネット環境が整っていれば、インターネット上のリポジトリを参照し、必要な依存ライブラリと現在の OS にインストール済みのライブラリとを比較し、足りない(追加でインストールが必要な)ライブラリとまとめてインストールしてくれます。一般的な利用であればこれだけ覚えておけば充分なケースも少なくありません。

ただ少し特殊なケース、例えば「インターネット接続環境がない」 CentOS/RHEL 環境の場合にどうやってインストールするか、というのは少し工夫が必要になります。今回のブログのテーマがこれです。

これはケース・バイ・ケースなんですが、おそらく以下(1)~(3)のいずれかの方法で解決できると思っています。実現が容易な順に(1)から(3)と記載しているので、数字が小さい方法で解決できるものかどうかを調べて、できない場合は次の数字へ、、という順に調べていただくのがおススめです。なおインストールするパッケージのライセンスに関わる問題は解決済みである(正式なライセンスを持っていて、インターネット接続できている場合はライセンス含めて問題なくインストールできるもの)と仮定します。

(1)DVD や ISO などのインストールメディアを持っている場合、

インターネット環境はないけれど、OS のインストール時に使った DVD メディアや、その ISO ファイルを持っているようなケースです(本来なら初期インストール時に一緒に入れておけばよかったのに入れ忘れたケースも含みます)。目的のファイルの rpm パッケージが含まれたメディアが手元にある場合は、そのメディアを使ってインストールすることが可能です。

具体的にはそのメディアをマウントした上で、ローカル環境に新しいリポジトリを1つ追加することで、インターネット上ではなく、ローカルファイルシステムに展開されたリポジトリが使えるようになり、そのローカルリポジトリを使ってインストールする、という方法です。少し古い環境で書かれていますが、以前に書いたこちらのブログエントリを参照するとより具体的な手順を説明しています:
ローカル環境内に yum リポジトリを作成する


(2)DVD や ISO などのインストールメディアを持っていない場合、

問題はこちらです。インターネット上に目的のパッケージファイルがあり、インストール用 DVD や他の DVD などでは提供されていないようなケースです(ansible などはこのパターンです)。インターネットに接続されている状況下であれば普通に "yum install" でインストールできるけど、接続がないとモジュールにアクセスできないのでインストールできない、ということになります。

これも考え方としては(1)と同様で「なんとかしてインストールに必要なファイルを入手して、ローカルファイルシステム上に用意」して、「ローカルリポジトリを作ってインストールする」ことになります。問題は「なんとかしてインストールに必要なファイルを入手」する部分です。

ここから先は実際にインターネットに接続されていない環境(目的のインストールを行う環境)とは別にインターネットに接続された環境を用意するか、その環境を一時的でいいのでインターネットに接続して実施する必要があります。インターネット接続のある環境で準備して、準備したファイルを目的の環境にコピー(転送)すれば、後は(1)と同様にできる、というやり方です。この前提が必要となる点に注意してください。

さて、インターネット接続のある環境で目的のファイルを(インストールではなく)ダウンロードだけ行うにはどのようにすればいいでしょうか? 例えば上述の "wget" の例で考えると、「"yum install wget" の結果としてダウンロードされてインストールされるファイルをダウンロードだけしたい」ことになります。

これは yum のオプション指定で可能でした。具体的には以下2つのオプションを指定します:
 ・"--downloadonly"
 ・"--destdir=(保存先ディレクトリ)"

"wget" の例だと以下のように実行します(この例だと保存先に /tmp を指定し、また確認プロンプトで止めないように "-y" オプションも指定しています):
# yum install --downloadonly --destdir=/tmp -y wget
Updating Subscription Management repositories.
Last metadata expiration check: 0:59:59 ago on Tue Jan  2 05:56:23 2024.
Dependencies resolved.
================================================================================
 Package       Architecture    Version                  Repository         Size
================================================================================
Installing:
 wget          x86_64          1.21.1-7.el9             myrhel92          794 k

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

Total size: 794 k
Installed size: 3.1 M
YUM will only download packages for the transaction.
Downloading Packages:
Complete!

成功すると(私の環境だと) "/tmp/wget-1.21.1-7.el9.x86_64.rpm" というファイルがダウンロードできていました。具体的なファイル名は実行環境やタイミングによって(バージョンなどが)少し変わる可能性もありますが、目的のファイルがダウンロードできたことになります。 ちなみにこうしてダウンロードできた rpm ファイルをスタンドアロン環境でインストールするには、上述のようなローカルリポジトリを作って行う場合以外にも、
# yum install /tmp/wget-1.21.1-7.el9.x86_64.rpm

のようにフルパス指定することでも可能になります。

ちなみに、上の wget のケースでは rpm ファイル1つだけが必要で、ダウンロードファイルも1つだけでした。これが、
# yum install --downloadonly --destdir=/tmp jq
Updating Subscription Management repositories.
Last metadata expiration check: 1:06:58 ago on Tue Jan  2 05:56:23 2024.
Dependencies resolved.
================================================================================
 Package           Architecture   Version                Repository        Size
================================================================================
Installing:
 jq                x86_64         1.6-14.el9             myrhel92         190 k
Installing dependencies:
 oniguruma         x86_64         6.9.6-1.el9.5          myrhel92         221 k

Transaction Summary
================================================================================
Install  2 Packages

Total size: 411 k
Installed size: 1.1 M
Is this ok [y/N]:

のように依存ライブラリが存在しているような場合はどうなるのでしょうか? 答はシンプルで「依存ファイルもまとめてダウンロード」されることになります。上の例だと、
 ・/tmp/jq-1.6-14.el9.x86_64.rpm
 ・/tmp/oniguruma-6.9.6-1.el9.5.x86_64.rpm

という2つのファイルがダウンロードされます。ダウンロード後のインストールは2つのファイルをそれぞれフルパス指定してローカルインストールしてもいいですし、2つのファイルを含むローカルリポジトリを作ってから "yum install jq" を実行する、でもセットアップできます。


(3)既にインストール済みの場合、

最後はちょっとややこしいケースです。例えば "wget" のモジュールをダウンロードしようとして、以下のようなメッセージがでることがあります:
# yum install --downloadonly --destdir=/tmp -y wget
Updating Subscription Management repositories.
Last metadata expiration check: 1:14:12 ago on Tue Jan  2 05:56:23 2024.
Package wget-1.21.1-7.el9.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!

「(この環境には)既にインストール済みなので何もすることはない」というメッセージが表示されています。ある程度環境が整っているマシンで実行すると、このようなケースは珍しくないと思っています。が、元々このマシンでインストールしたくてダウンロードしているのではなく、別のインターネットにつながっていないマシンでローカルインストールできるようにするためのダウンロードをしているので、今のマシンにインストールされているかどうかは関係なくダウンロードしたい、という背景があります。

これは「1度アンインストールする」ことで話が単純になります。つまり、
 ・一度アンインストール
 ・ローカルにダウンロード(上の(2)の手順)
 ・再度インストール(環境を元に戻す)
を順に実施することで目的のファイルのダウンロードができます。


っていうか、インターネットのない環境でのセットアップ、って今はもうかなり面倒なことになってきているんですね。まあ確かに「内側からインターネットに出ていくだけ」すら許されない環境って、「そのせいでここまでセットアップにコストがかかる」デメリットを凌ぐだけの理由があるんだろうか?? という気がして・・・ あ、いえいえ、仕事の愚痴ではありませんよ。


このページのトップヘ