Edited at

MacにTLS1.3とHTTP/2で通信可能なcurlをインストールした手順

Mac標準のcurlはLibreSSLとリンクしているが、現在手元にインストールされているバージョンだとTLS1.3を指定して通信することはできない。

$ /usr/bin/curl --version

curl 7.54.0 (x86_64-apple-darwin18.0) libcurl/7.54.0 LibreSSL/2.6.5 zlib/1.2.11 nghttp2/1.24.1
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets HTTPS-proxy

$ /usr/bin/curl https://www.instart.com --tlsv1.3 --http2
curl: (4) LibreSSL was built without TLS 1.3 support

TLS1.3に対応しているOpenSSL1.1を使用したcurlをインストールすることで、TLS1.3通信が可能になるのだけど、インストールに苦戦したのでメモしておく。

Macの場合、curlはbrew install curlまたはbrew install curl-opensslでインストールできるが、これでインストールしたcurlはTLS1.3に未対応のOpenSSL1.0系にリンクしてしまう。この時--tlsv1.3オプションを指定してもエラーにならないが無視されてしまい、TLS1.2などで接続されてしまう。

OpenSSL 1.1はbrew install opensslではなくbrew install openssl@1.1でインストールできるが、/usr/local/opt/openssl@1.1という別ディレクトリーにインストールされることもあり、この後curlをインストールしても1.0の方が使用されてしまう。また、他のbrewでインストールしたライブラリーが1.0系を参照していることもあり、1.0をアンインストールしてもうまくいかなかった。openssl@1.11.0と同一ディレクトリーに上書きできるなら解決しそうな気もするのだけど、できるのだろうか?

仕方ないのでcurlのソースをダウンロードしてビルドするが、下記のように、1.1側を参照させるような環境変数設定をがんばって行っても、なぜか1.0とリンクしてしまう。

$ export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"

$ export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
$ export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"
$ ./configure --with-nghttp2=/usr/local/opt/nghttp2 --with-brotli --without-darwinssl --with-ssl="/usr/local/opt/openssl@1.1" CPPFLAGS="-I/usr/local/opt/openssl@1.1/include" CFLAGS="-I/usr/local/opt/openssl@1.1/include"

config.logを見ると、checking for OpenSSL headers versionの結果が1.0.2になってしまうことがわかった。バージョン確認がどのように行われているかを調べたところ、#include <openssl/crypto.h>して定数OPENSSL_VERSION_NUMBERの値をチェックしている。

ここで、なぜかCPPFLAGSで指定したディレクトリーではなく、-I/opt/local/includeが追加されており、-isystemより-Iが優先されるため、1.0系が入っている/opt/local/include/openssl/cypto.hの方が優先で参照されてしまっていた。

gcc -E -isystem /usr/local/opt/openssl@1.1/include -I /opt/local/include  -I/usr/local/Cellar/openssl@1.1/1.1.1c/include    conftest.c

configureの20703行目あたりでzlibライブラリーの場所を確認するために実行したpkg-configコマンドの戻り値が-Iを使っていることが判明したため、-isystemに置き換えたところ、無事1.1.1cとリンクしてTLS1.3通信が可能になった。

#      CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags-only-I zlib`"

CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"

$ /usr/local/bin/curl --version

curl 7.65.3 (x86_64-apple-darwin18.7.0) libcurl/7.65.3 OpenSSL/1.1.1c zlib/1.2.11 brotli/1.0.7 libidn2/2.2.0 nghttp2/1.39.1 librtmp/2.3
Release-Date: 2019-07-19
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS brotli HTTP2 HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB SSL TLS-SRP UnixSockets

$ /usr/local/bin/curl -v https://www.instart.com -o/dev/null --tlsv1.3 --http2
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 159.180.93.29:443...
* TCP_NODELAY set
* Connected to www.instart.com (159.180.93.29) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [122 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [19 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [2820 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [264 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=US; ST=California; L=Palo Alto; O=Instart Logic, Inc.; OU=IT Department; CN=*.instart.com
* start date: Jan 21 00:00:00 2019 GMT
* expire date: Jan 29 12:00:00 2020 GMT
* subjectAltName: host "www.instart.com" matched cert's "*.instart.com"
* issuer: C=US; O=DigiCert Inc; CN=DigiCert SHA2 Secure Server CA
* SSL certificate verify ok.
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
} [5 bytes data]
* Using Stream ID: 1 (easy handle 0x7fea5500b600)
} [5 bytes data]
> GET / HTTP/2
> Host: www.instart.com
> User-Agent: curl/7.65.3
> Accept: */*
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [289 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [273 bytes data]
* old SSL session ID is stale, removing
{ [5 bytes data]
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
} [5 bytes data]
0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0< HTTP/2 200
< content-type: text/html; charset=UTF-8
< content-length: 77762
< x-type: default
< accept-ranges: bytes
< x-pingback: https://instart.wpengine.com/xmlrpc.php
< cache-control: max-age=600, must-revalidate
< x-pass-why:
< vary: Accept-Encoding
< vary: Accept-Encoding
< vary: Accept-Encoding,Cookie
< x-cacheable: SHORT
< x-cache-group: normal
< link: <https://instart.wpengine.com/wp-json/>; rel="https://api.w.org/"
< server: nginx
< wpe-backend: apache
< date: Wed, 24 Jul 2019 01:30:21 GMT
< x-cache: HIT: 26
< x-instart-request-id: 5606122923514061747:UXT01-CPVNPPRY16:1563931821:0
<
{ [15820 bytes data]
100 77762 100 77762 0 0 36714 0 0:00:02 0:00:02 --:--:-- 36714
* Connection #0 to host www.instart.com left intact

疲れすぎたのでissueを書こうとしたけどKNOWN BUSに似たようなのがあった。

https://curl.haxx.se/docs/knownbugs.html#configure_finding_libs_in_wrong