Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

https SNICallback hangs if optional callback isn't provided on ARM6 (and possibly others) #869

Closed
coolaj86 opened this issue Feb 17, 2015 · 7 comments

Comments

@coolaj86
Copy link
Contributor

Originally I found this bug #867 on ARM6 and while testing further on OS X I found a workaround.

I took my band-aided test case back to my Rapsberry Pi to test and found that the https request still just hangs forever.

I commented out the SNICallback entirely and found that the certificates I'm loading are working by themselves, but the SNICallback is broken.

'use strict';

var https           = require('https');
var fs              = require('fs');
var path            = require('path');
var crypto          = require('crypto');
var connect         = require('connect');

module.exports.create = function (_securePort, _insecurePort) {
    // connect / express app
  var app             = connect();

    // SSL Server
  var secureContexts  = {};
  var dummyCerts;
  var secureOpts;
  var secureServer;
  var securePort      = _securePort || 443;

    // force SSL upgrade server
  var insecureServer;
  var insecurePort    = _insecurePort || 80;

  function loadDummyCerts() {
    var certsPath = path.join(__dirname, 'certs');
    var certs = {
      key:          fs.readFileSync(path.join(certsPath, 'server', 'dummy-server.key.pem'))
    , cert:         fs.readFileSync(path.join(certsPath, 'server', 'dummy-server.crt.pem'))
    , ca:           fs.readdirSync(path.join(certsPath, 'ca')).filter(function (node) {
                       return /crt\.pem$/.test(node);
                     }).map(function (node) {
                       console.log('[Add CA]', node);
                      return fs.readFileSync(path.join(certsPath, 'ca', node));
                    })
    };
    secureContexts.dummy = crypto.createCredentials(certs).context;
    dummyCerts = certs;
  }
  loadDummyCerts();

  app.use(function (req, res, next) {
    console.log('[log] request for ' + req.headers.host + req.url);
    next();
  });

  function runServer() {
    //provide a SNICallback when you create the options for the https server
    secureOpts = {
                    // fallback / default dummy certs
      key:          dummyCerts.key
    , cert:         dummyCerts.cert
    , ca:           dummyCerts.ca
      //SNICallback is passed the domain name, see NodeJS docs on TLS
  /*
    , SNICallback:  function (domainname) {
                      console.log('[log] SNI:', domainname);
                      console.log('[log] SNI:', secureContexts[domainname]);
                      var secureContext = secureContexts[domainname] || secureContexts.dummy;
                      console.log('[log]', secureContext);
                      return secureContext;
                    }
  */
    };

    secureServer = https.createServer(secureOpts);
    secureServer.on('request', function (req, res) {
      console.log('[log] request');
      app(req, res);
    });
    secureServer.listen(securePort, function () {
      console.log("Listening on https://localhost:" + secureServer.address().port);
    });
  }

  runServer();
}
module.exports.create(65443, 65080);

To experience the hang, simply uncomment the SNICallback block.

@coolaj86
Copy link
Contributor Author

(testing from my macbook to my rpi)

It hangs with curl https://10.0.0.20:65443 by itself.

It doesn't hang with curl -k https://10.0.0.20:65443

It does hang in Chrome with https://10.0.0.20:65443

@coolaj86
Copy link
Contributor Author

Testing on localhost

Testing from the Raspberry Pi (localhost -> localhost):

Works or Gives Error

curl https://127.0.0.1:65443 -k
curl https://10.0.0.20:65443 -k
works

curl https://127.0.0.1:65443 --cacert certs/ca/dummy-root-ca.crt
curl https://10.0.0.20:65443 --cacert certs/ca/dummy-root-ca.crt
curl: (51) SSL: certificate subject name 'local.helloworld3000.com' does not match target host name '10.0.0.20'

curl https://127.0.0.1:65443
curl https://10.0.0.20:65443
curl: (60) SSL certificate problem: self signed certificate in certificate chain
More details here: http://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.

Hangs Forever

curl https://localhost:65443

curl https://localhost:65443 --cacert certs/ca/dummy-root-ca.crt.pem

curl https://localhost:65443 -k

curl https://local.helloworld3000.com:65443

curl https://local.helloworld3000.com:65443 --cacert certs/ca/dummy-root-ca.crt.pem

curl https://local.helloworld3000.com:65443 -k

Testing NOT on localhost

curl -vvv https://10.0.0.20:65443
* Rebuilt URL to: https://10.0.0.20:65443/
* Hostname was NOT found in DNS cache
*   Trying 10.0.0.20...
* Connected to 10.0.0.20 (10.0.0.20) port 65443 (#0)
* WARNING: using IP address, SNI is being disabled by the OS.
(hangs forever)
curl -vvv -k https://10.0.0.20:65443
* Rebuilt URL to: https://10.0.0.20:65443/
* Hostname was NOT found in DNS cache
*   Trying 10.0.0.20...
* Connected to 10.0.0.20 (10.0.0.20) port 65443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
* Server certificate: local.helloworld3000.com
* Server certificate: example.com
> GET / HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 10.0.0.20:65443
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 17 Feb 2015 20:00:03 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<
* Connection #0 to host 10.0.0.20 left intact
works
curl -vvv https://10.0.0.20:65443 --cacert certs/ca/dummy-root-ca.crt.pem
* Rebuilt URL to: https://10.0.0.20:65443/
* Hostname was NOT found in DNS cache
*   Trying 10.0.0.20...
* Connected to 10.0.0.20 (10.0.0.20) port 65443 (#0)
* WARNING: using IP address, SNI is being disabled by the OS.
(hangs forever)

@coolaj86 coolaj86 changed the title https SNICallback always hangs on ARM6 (Raspberry Pi) https SNICallback always hangs on ARM6 (and possibly others) Feb 17, 2015
@coolaj86
Copy link
Contributor Author

The Problem and the Workaround

The API for SNICallback has changed since v0.10.36. Now there's an optional callback, but on ARM, for some reason, returning the context without using the callback doesn't work, while on OS X it does work.

P.S. I'm definitely in favor of the callback remaining optional since it's difficult for me, the node user, to feature detect which version of node / io.js my code is running on.

Update: Nevermind, I can check 'function' === typeof cb... DUH

Update 2: it turns out I need the async version anyway because I'll be using let's encrypt and I have to do some lazy loading for performance reasons.

@coolaj86 coolaj86 changed the title https SNICallback always hangs on ARM6 (and possibly others) https SNICallback hangs if optional callback isn't provided on ARM6 (and possibly others) Feb 17, 2015
@indutny
Copy link
Member

indutny commented Feb 19, 2015

See my comment here: nodejs/node-v0.x-archive#9236 (comment)

@coolaj86
Copy link
Contributor Author

This is a DIFFERENT bug. (and #867 is yet another different bug)

The problem here is that on OS X the callback is optional (if you return synchronously, it works) but on ARM returning synchronously is not supported and you MUST use the callback.

@indutny
Copy link
Member

indutny commented Feb 19, 2015

You must use the callback on every platform. I don't think that there is any way to opt-out from it, unless the SNICallback is not called for that connection.

@coolaj86
Copy link
Contributor Author

Oh, I didn't see this comment here before.

I'll close the issue, but I'm interested to retest since I originally claimed to have tested it on 2 devices with 1.2 and 1 with 0.12...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants