経緯
うるさいですね・・・。解決法だけ見せてくださいココアさん!という方はこちら。Ubuntu18.04 on WSL (Windows10)でrbenvをgitレポジトリからインストールしたが、SSL関連のエラーが散見された。例えばgem installができなかったり、open-uriでのhtml取得の際にrequire certifiedしないと証明書エラーになったり。(なぜか1度はnokogiriなどもインストールできて使っていたのだが、それは忘れておく。)
そこで、rubyが見ているOpenSSLのバージョンを調べてみる。
irb(main):001:0> require "openssl"
=> true
irb(main):002:0> OpenSSL::OPENSSL_VERSION
=> "OpenSSL 1.1.1 11 Sep 2018"
おそらくほとんどの状況であればこれで何の問題もないはずだろうが、私の場合は違った。というのも、以前Python3をローカルインストールした際にOpenSSL1.1.1hを手動インストールしており、システムで動いているのはそちらなのだが、rubyがそれを見ていないということを意味していたからである。
環境
Ubuntu18.04 on WSL (Windows10) rbenv 1.1.2-40-g62d7798解決の流れ
OpenSSL::OPENSSL_VERSIONが正しく設定されるようにする
どのようにrubyが参照するOpenSSLが特定されているのか調べるために、それっぽいファイルを探してみる。$ grep -r OPENSSL ./*
とすると、まともでないものも含めかなりの数がヒットするが、その中で、
./plugins/ruby-build/bin/ruby-build: OPENSSL_PREFIX_PATH="${PREFIX_PATH}/openssl"
./plugins/ruby-build/bin/ruby-build: OPENSSLDIR="${OPENSSLDIR:-$OPENSSL_PREFIX_PATH/ssl}"
に注目。どうやらopensslディレクトリ$PREFIX_PATH直下にないと認識されないようだ。そしてこのとき$PREFIX_PATHは設定されていなかった。
(余談)
ちなみにmacの場合は同様にOpenSSLを認識させる方法も見つかった(記事)のだが、参照する環境変数が違うのかこれでは上手くいかなかったためこんなことをする羽目になった。
そこで、$PREFIX_PATHを設定。
$ export PREFIX_PATH=$PREFIX_PATH:/usr/local/bin
$ source ~/.bash_profile
この状態で
$ rbenv install 2.7.2
などすると上手くいくはず。
$ rbenv local 2.7.2
$ rbenv versions
system
2.6.5 (set by /home/rubyer/.ruby-version)
* 2.7.2
rubyのバージョンを変更するのを忘れずに、
irb(main):001:0> require "openssl"
=> true
irb(main):002:0> OpenSSL::OPENSSL_VERSION
=> "OpenSSL 1.1.1h 22 Sep 2020"
ちなみに、rbenvを経由しないsystemのrubyについてはこれでは解決しない。同じようなことをすれば解決するのだろうが、必要ないのでここでは扱わない。
SSL証明書を更新する
上記を経ても、open-uriでhtmlを取得する際にはrequire "certified"しないとエラーが出た。これはメッセージを読めば原因が証明書にあることは分かる。試しにrubyが参照する証明書の場所を確認し、内容を見てみよう。irb(main):001:0> require "openssl"
=> true
irb(main):002:0> OpenSSL::X509::DEFAULT_CERT_FILE
=> "/usr/local/ssl/cert.pem"
$ cat /usr/local/ssl/cert.pem
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4
GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbF
...
-----END CERTIFICATE-----
一応証明書は存在している。ということは、内容が不足していることが考えられる。したがって接続しようとするサイトとの通信に必要な証明書を置けばよいのだが、証明書もサイトごとにいくつか種類があるので、必要になるごとにこういう所からコピペやダウンロードをして追記することになるというのは避けたい。
ということで、私の場合はSSL通信が上手くいっている、Python3から網羅的な証明書群を拝借することにした。
$ find /usr/lib/python3 -name "*cert*"
/usr/lib/python3/dist-packages/certifi
/usr/lib/python3/dist-packages/certifi/cacert.pem
(以下略)
$ cp /usr/local/ssl/cert.pem /usr/local/ssl/cert.pem_copy
$ cat /usr/lib/python3/dist-packages/certifi/cacert.pem > /usr/local/ssl/cert.pem
検索は適当に。cacert.pemには複数の証明書が入っていた。念のため元の証明書はコピーして残しておき、置換。これで証明書エラーもなくなった。
(余談)
これまでこういったエラーには数々出会って解消してきたが、原因の特定からまともに「おまじない」もなく解決したのは初めてかもしれない。