Raspberry PiのPython3でSSLv3 handshake failureを解決した話
あくあたん在室モニターは,Raspberry Piで動いています.
先日らぼのWebサーバがアップデートされたときから,うまく動作しなくなりました.
モニターでは,らぼのWebサーバで公開しているAPIを叩いて在室状況を取得します.
APIはhttpだけでなく,httpsも対応していました.
モニターのログを見ると,データ取得ルーチンが止まっています.
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure
おっ,SSLのエラーか.
新しいサーバではTLS1.0は捨てられました.そこに異議を唱えるつもりはないので,こちらで対応すべきですね.
普通に考えて,何かSSL周りが古いとかそういう状況が考えられます.
これまで,http.client
を使ってアクセスしていたのですが,requests
のほうが良い感じと聞いたので,書き換えてみました.実際,requests
は良い感じに書けます.
テストは次の方法でできます.
$ /usr/bin/python3
Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> import ssl
>>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.1.0j 20 Nov 2018
>>> requests.get('https://se.is.kit.ac.jp/')
Traceback (most recent call last):
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connectionpool.py",
line 600, in urlopen
chunked=chunked)
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connectionpool.py",
line 343, in _make_request
self._validate_conn(conn)
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connectionpool.py",
line 839, in _validate_conn
conn.connect()
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connection.py", line
344, in connect
ssl_context=context)
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/util/ssl_.py", line
347, in ssl_wrap_socket
return context.wrap_socket(sock, server_hostname=server_hostname)
File "/usr/lib/python3.5/ssl.py", line 385, in wrap_socket
_context=self)
File "/usr/lib/python3.5/ssl.py", line 760, in __init__
self.do_handshake()
File "/usr/lib/python3.5/ssl.py", line 996, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.5/ssl.py", line 641, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failur$ (_ssl.c:720)
しかし,エラーは消えませんでした.
pipで関連するモジュールを最新にしてみました.
しかし,エラーは消えませんでした.
試しに,Macで同じコードを実行しました.
~> python3
Python 3.7.3 (default, Mar 27 2019, 09:23:15)
[Clang 10.0.1 (clang-1001.0.46.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> import ssl
>>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.0.2r 26 Feb 2019
>>> requests.get('https://se.is.kit.ac.jp')
<Response [200]>
>>>
エラーは出ません.
たまたまPython3.4が動いているraspberry piが生きていたので,そこから同じコードを実行しました.
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> import ssl
>>> print (ssl.OPENSSL_VERSION)
OpenSSL 1.0.1t 3 May 2016
>>> requests.get('https://se.is.kit.ac.jp')
<Response [200]>
>>>
エラーは出ません.
古いpythonと古いsslでも動くのに,ちょうど今のpython3.5とopenssl 1.1.0jの組み合わせでは動かないの??
問題が切り分けられずに頭が痛くなってきます.こうなったら初期化です.Raspberry Piの公式から最新のイメージをダウンロードして,SDを初期化します.
まっさらなraspbianの上でなら…
$ /usr/bin/python3
Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> import ssl
>>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.1.0j 20 Nov 2018
>>> requests.get('https://se.is.kit.ac.jp/')
Traceback (most recent call last):
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connectionpool.py",
line 600, in urlopen
chunked=chunked)
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connectionpool.py",
line 343, in _make_request
self._validate_conn(conn)
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connectionpool.py",
line 839, in _validate_conn
conn.connect()
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/connection.py", line
344, in connect
ssl_context=context)
File "/home/pi/.local/lib/python3.5/site-packages/urllib3/util/ssl_.py", line
347, in ssl_wrap_socket
return context.wrap_socket(sock, server_hostname=server_hostname)
File "/usr/lib/python3.5/ssl.py", line 385, in wrap_socket
_context=self)
File "/usr/lib/python3.5/ssl.py", line 760, in __init__
self.do_handshake()
File "/usr/lib/python3.5/ssl.py", line 996, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.5/ssl.py", line 641, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failur$ (_ssl.c:720)
こらあかん.
ついに,Raspberry Pi上でPython 3.7をbuildし直すことにしました.
$ sudo apt update
$ sudo apt install -y libffi-dev libbz2-dev liblzma-dev libsqlite3-dev libncurses5-dev libgdbm-dev zlib1g-dev libreadline-dev libssl-dev tk-dev build-essential libncursesw5-dev libc6-dev openssl
$ cd Python-3.7.3
$ ./configure --enable-optimizations
$ make
$ sudo make install
インストールが終わったら,requestsモジュールをインストールします.
$ sudo /usr/local/bin/pip3 install requests
テストします.(ほんま許して)
$ /usr/local/bin/python3
Python 3.7.3 (default, Apr 27 2019, 21:00:58)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> import ssl
>>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.1.0j 20 Nov 2018
>>> requests.get('https://se.is.kit.ac.jp/')
<Response [200]>
>>>
動いた…
なお,この後.新しいPython3.7.3ではもう1つのはまりポイント,pygameにて更に苦労することになります.
それはまた,別の話.