RailsのRestClientが利用するTLSのバージョンを確認する

TLSv1.2への移行

利用しているAPIがTLSv1.2へ移行するとのことで、クライアント側がTLSv1.2で通信可能かどうか確認してくださいと連絡をもらいました。

APIにアクセスしているのはrest-client(2.1.0)でした。なのでrest-clientがアクセスする際に利用するTLSのバージョンを確認すればよいことになります。

TLSのバージョン取得方法

ググってみるとopensslのドキュメントが見つかりました

https://www.openssl.org/docs/man1.1.1/man3/SSL_get_version.html

いろんな種類があるようです。

rest-clientの場合

rest-clientはリクエストを作成する場合にNet::HTTPを利用します。

https://github.com/rest-client/rest-client/blob/master/lib/restclient/request.rb#L163
https://github.com/rest-client/rest-client/blob/master/lib/restclient/request.rb#L464

Net::HTTPが利用するTLSのバージョンを知るためにgithubでコードを確認してみます。

https://github.com/ruby/net-http/blob/master/lib/net/http.rb#L975

https://github.com/ruby/net-http/blob/master/lib/net/http.rb#L1030

ここでsocketの生成を行なっています

https://github.com/ruby/net-http/blob/master/lib/net/http.rb#L1044

@socketを作成しています。Net::BufferedIO.newの引数で先ほど作成したsocketを指定しています。

BufferedIOを確認すると、#ioというメソッドがあり、オブジェクトを返してくれるということです。

Module#prependで試してみる

Module#prependを使って実際に出力してみます。

処理を入れるところはsocketをクローズする手前のdo_finishメソッドにします。

https://github.com/ruby/net-http/blob/master/lib/net/http.rb#L1069

まずは@socketを確認してみます。

1
2
3
4
5
6
7
$ rails c
[1] pry(main)> Net::HTTP.prepend Module.new { def do_finish; pp @socket; super; end }
=> Net::HTTP
[2] pry(main)> RestClient.get('https://google.co.jp')
#<Net::BufferedIO io=#<OpenSSL::SSL::SSLSocket:0x00007f81b4001558>>
#<Net::BufferedIO io=#<OpenSSL::SSL::SSLSocket:0x00007f81aedd4be0>>
=> <RestClient::Response 200 "<!doctype h...">

ioというOpenSSL::SSL::SSLSocketクラスのインスタンスがあります。
ioの中身を確認します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[1] pry(main)> Net::HTTP.prepend Module.new { def do_finish; pp @socket.io; super; end }
=> Net::HTTP
[2] pry(main)> RestClient.get('https://google.co.jp')
#<OpenSSL::SSL::SSLSocket:0x00007f8a3a8f9b18
@context=
#<OpenSSL::SSL::SSLContext:0x00007f8a3a8fac48
@cert_store=
#<OpenSSL::X509::Store:0x00007f8a369b2ba8
@chain=nil,
@error=nil,
@error_string=nil,
@time=nil,
@verify_callback=nil>,
@max_proto_version=nil,
@min_proto_version=769,
@session_new_cb=
#<Proc:0x00007f8a3a8f9cf8@/Users/s1180997/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/net/http.rb:986>,
@verify_hostname=true,
@verify_mode=1>,
@eof=false,
@hostname="www.google.co.jp",
@io=#<TCPSocket:fd 26, AF_INET, 10.81.20.99, 55387>,
@rbuffer="",
@sync=true,
@sync_close=true,
@wbuffer="">

OpenSSL::SSL::SSLContextのインスタンスの@contextから取得できそうです。

1
2
3
4
5
6
7
8
[1] pry(main)> Net::HTTP.prepend Module.new { def do_finish; pp @socket.io.context.ssl_version; super; end }
=> Net::HTTP
[2] pry(main)> RestClient.get('https://google.co.jp')
NoMethodError: undefined method `ssl_version' for #<OpenSSL::SSL::SSLContext:0x00007ff13076a998>
from (pry):1:in `do_finish'
Caused by NoMethodError: undefined method `ssl_version' for #<OpenSSL::SSL::SSLContext:0x00007ff1310a2808>
Did you mean? ssl_version=
from (pry):1:in `do_finish'

ん〜ダメでした。setterのみのようです。

ためしにmethodメソッドを利用してみます。

1
2
3
4
5
6
[1] pry(main)> Net::HTTP.prepend Module.new { def do_finish; pp @socket.io.method(:ssl_version); super; end }
=> Net::HTTP
[2] pry(main)> RestClient.get('https://google.co.jp')
#<Method: OpenSSL::SSL::SSLSocket#ssl_version>
#<Method: OpenSSL::SSL::SSLSocket#ssl_version>
=> <RestClient::Response 200 "<!doctype h...">

ssl_versionがかえってきています。ということは呼び出せるということですね。

1
2
3
4
5
6
[1] pry(main)> Net::HTTP.prepend Module.new { def do_finish; pp @socket.io.ssl_version; super; end }
=> Net::HTTP
[2] pry(main)> RestClient.get('https://google.co.jp')
"TLSv1.3"
"TLSv1.3"
=> <RestClient::Response 200 "<!doctype h...">

無事バージョンが取得できました!

まとめ

RailsのRestClientで利用しているTLSのバージョンを確認するにはrails consoleで

1
Net::HTTP.prepend Module.new { def do_finish; pp @socket.io.ssl_version; super; end }

を実行してからRestClient.getでリクエストを送る。