Решил давеча обновиться с Node.js v0.12.7 на Node.js v4.1.0. После обновления проблема — при запросе к серверу через curl (из командной строки) этот самый curl отказывается делать запрос с ошибкой:
* About to connect() to test.monopoly-one.com port 443 (#0)
* Trying 51.254.61.44... connected
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS alert, Server hello (2):
* SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
* Closing connection #0
curl: (60) SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
More details here: curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
Этот же запрос curl с ключом `-k`:
* About to connect() to test.monopoly-one.com port 443 (#0)
* Trying 51.254.61.44... connected
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using ECDHE-RSA-AES128-GCM-SHA256
* Server certificate:
* subject: C=RU; CN=test.monopoly-one.com; emailAddress=webmaster@monopoly-one.com
* start date: 2015-06-28 08:38:09 GMT
* expire date: 2016-06-28 18:38:55 GMT
* issuer: C=IL; O=StartCom Ltd.; OU=Secure Digital Certificate Signing; CN=StartCom Class 1 Primary Intermediate Server CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
При этом, если откатиться на Node.js v0.12.7, то абсолютно такой же серверный код с абсолютно теми же сертификатом и ключом не вызывает у curl на том же самом устройстве никакого дискомфорта:
* About to connect() to test.monopoly-one.com port 443 (#0)
* Trying 51.254.61.44... connected
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
* subject: C=RU; CN=test.monopoly-one.com; emailAddress=webmaster@monopoly-one.com
* start date: 2015-06-28 08:38:09 GMT
* expire date: 2016-06-28 18:38:55 GMT
* issuer: C=IL; O=StartCom Ltd.; OU=Secure Digital Certificate Signing; CN=StartCom Class 1 Primary Intermediate Server CA
* SSL certificate verify ok.
То есть проблема явно не в том, где выполняется curl, а именно в версии Node.js, однако я никак не смог найти в сети чейнджлог, который внятно описывает, что в Node.js поменялось в плане создания https сервера.
Кусочек кода, который создаёт https сервер:
var __fs = require('fs'),
__https = require('https');
self.__server = __https.createServer({
cert: __fs.readFileSync('/valid/path/to.crt'),
key: __fs.readFileSync('/valid/path/to_decrypted.key')
}, self.preparer);
self.__server.addContext('test.monopoly-one.com', {
cert: __fs.readFileSync('/valid/path/to.crt'),
key: __fs.readFileSync('/valid/path/to_decrypted.key'),
ca: [
__fs.readFileSync('/valid/path/to/startssl/ca.pem')
__fs.readFileSync('/valid/path/to/startssl/sub.class1.server.ca.pem')
]
});
self.__server.listen(443);
Та же версия, но с использованием tls.createSecureContext() и SNICallback вместо self.__server.addContext ведёт себя абсолютно так же.
UPD: забыл упомянуть, что все современные браузеры ведут себя абсолютно одинаково и не выказывают беспокойств.
UPD 2: изменение порядка chiphers помогает заставить curl сменить ECDHE-RSA-AES256-GCM-SHA384 на ECDHE-RSA-AES128-GCM-SHA256 (как раньше), но сертификату он по-прежнему не доверяет.