no-image

さくらのレンタルサーバーで pip できない(SSLError)

TL; DR

古い OpenSSL が使われるのが原因です。Python ビルド時に新しい OpenSSL を指定すれば解決します。

$ pyenv uninstall 3.6.5
$ CPPFLAGS="-I/usr/local/ssl/include" LDFLAGS="-L/usr/local/ssl/lib" pyenv install 3.6.5

経緯

さくらのレンタルサーバーはプラン スタンダード以上で SSH が使えるので、pyenv を入れたり pip を入れたりできます。pyenv を使って Python の他のバージョンをインストールするときに注意しないといけないのが、さくらでは Python 導入時のビルドで古い OpenSSL が使われてしまうことです。これの影響で pip したときに次のような SSL のエラーを吐きます。

$ pip install bcrypt
Collecting bcrypt
 Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:833)'),)': /simple/bcrypt/
 Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:833)'),)': /simple/bcrypt/
 Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:833)'),)': /simple/bcrypt/
 Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:833)'),)': /simple/bcrypt/
 Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:833)'),)': /simple/bcrypt/
 Could not fetch URL https://pypi.python.org/simple/bcrypt/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.python.org', port=443): Max retries exceeded with url: /simple/bcrypt/ (Caused by SSLError(SSLError(1, '[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:833)'),)) - skipping
 Could not find a version that satisfies the requirement bcrypt (from versions: )
No matching distribution found for bcrypt

OpenSSL のバージョンが古いのかな?とバージョンを確認しても最新版です。

$ openssl version
OpenSSL 1.0.2o 27 Mar 2018

ですが pyenv install 3.6.5 した Python 上で確認してみると

$ python
Python 3.6.5 (default, Jun 26 2018, 10:35:21)
[GCC 4.2.1 20070831 patched [FreeBSD]] on freebsd9
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 0.9.8zf 19 Mar 2015'

思いっきり 0.9.8zf と出ていますね。「Python - さくらレンタルサーバーでpip installができません(123028)|teratail」によると、普通にビルドすると最新版の OpenSSL ではなくて、古い方の OpenSSL をリンクしてしまうとのこと。

さくらレンタルサーバでは普通にpythonをbuildすると古いopensslにつながってしまうようです。
/usr/local/sslに新しいのが入ってるみたいなので、

./configure CPPFLAGS="-I/usr/local/ssl/include" LDFLAGS="-L/usr/local/ssl/lib"

でpythonを作るとよさそうです。

ということで一旦インストールした Python を uninstall して、OpenSSL のパスを指定して再度インストールし直します。

$ pyenv uninstall 3.6.5
$ CPPFLAGS="-I/usr/local/ssl/include" LDFLAGS="-L/usr/local/ssl/lib" pyenv install 3.6.5
Downloading Python-3.6.5.tgz...
-> https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tgz
Installing Python-3.6.5...
Installed Python-3.6.5 to /home/USERNAME/.pyenv/versions/3.6.5

無事インストールできたようなので、OpenSSL のバージョンを確認してみます。

$ python
Python 3.6.5 (default, Jun 26 2018, 10:35:21)
[GCC 4.2.1 20070831 patched [FreeBSD]] on freebsd9
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 1.0.2o 27 Mar 2018'

大丈夫そうですね。pip の更新を兼ねて pip を試してみます。

$ pip install -U pip
Collecting pip
 Downloading https://files.pythonhosted.org/packages/0f/74/ecd13431bcc456ed390b44c8a6e917c1820365cbebcb6a8974d1cd045ab4/pip-10.0.1-py2.py3-none-any.whl (1.3MB)
 100% |################################| 1.3MB 890kB/s
Installing collected packages: pip
 Found existing installation: pip 9.0.3
 Uninstalling pip-9.0.3:
 Successfully uninstalled pip-9.0.3
Successfully installed pip-10.0.1

ばっちり動きました。

ですが……今回の目的だった bcrypt のインストールは、libffi がなくて結局できず。py-bcrypt なら pip 一発なのでソースコードを py-bcrypt に書き替えた方が早そう。ちょっと不完全燃焼です。

参考