Closed Napzu closed 4 years ago
Do you know the corresponding OpenSSL functionality?
Not really but I know something.
Multiple certification is implemented via Server Name Indication (SNI).
Express uses callback something like this:
SNICallback: function(domain, cb) {
cb(null, tls.createCredentials({ key: fs.readFileSync('app1.key').toString(), cert: fs.readFileSync('app1.crt').toString() }).context);
}
But in uWebsocket.js context I think this would be the easies from user perspective:
require('uWebSockets.js').SSLApp({
certs: {
'domain1.com' : {
key_file_name : 'domain1_com_key.pem',
cert_file_name: 'domain1_com_cert.pem',
},
'domain2.com' : {
key_file_name : 'domain2_com_key.pem',
cert_file_name: 'domain2_com_cert.pem',
},
},
key_file_name : 'fallback_com_key.pem',
cert_file_name: 'fallback_com_cert.pem',
})
https://en.m.wikipedia.org/wiki/Server_Name_Indication
Okay okay, that sounds like it could be implemented. But this is a typical thing I won't do unless many people want it or someone pays for it
I implemented it for v0.14 as follows:
auto ctx = uS::TLS::createContext(cert, key);
SSL_CTX_set_tlsext_servername_callback(ctx.getNativeContext(), [](SSL *ssl, int *, void *) -> int {
if (!ssl) return SSL_TLSEXT_ERR_NOACK;
const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (!hostname || hostname[0] == '\0') return SSL_TLSEXT_ERR_NOACK;
if (strstr(hostname, "default.com")) return SSL_TLSEXT_ERR_OK;
std::string stdHost(hostname);
SSL_CTX *native = nullptr;
static thread_local std::unordered_map<std::string, SSL_CTX *> nativeCtxCache;
auto cachedNativeCtx = nativeCtxCache.find(stdHost);
if (cachedNativeCtx != nativeCtxCache.end()) {
native = cachedNativeCtx->second;
} else {
std::string cert = stdHost + ".crt";
std::string key = stdHost + ".key";
auto ctx = uS::TLS::createContext(cert, key);
native = ctx.getNativeContext();
if (!native) return SSL_TLSEXT_ERR_NOACK;
nativeCtxCache[stdHost] = native;
}
if (SSL_set_SSL_CTX(ssl, native) != native)
return SSL_TLSEXT_ERR_NOACK;
return SSL_TLSEXT_ERR_OK;
});
I didn't use v0.15 yet, but I guess it's a good starting point
That's good as a reference but still, this is low priority until more than two wants it
@alexhultman my company is releasing an open source framework/CMS for Node.js shortly and we've love to make use of this feature.
Background details:
I've written an efficient HTTP/2 router for our framework that allows serving multiple apps/domains off of one server instance.
Of course I know that websockets + HTTP/2 aren't currently feasible because there isn't even a spec for it yet.
My current plan with our framework is to allow users to deploy an HTTP/2 server that accommodates multiple domains for their front-end app. For their data/API, I'll be using uWS.js to serve GraphQL requests and Websockets via HTTP/1.1 as a separate app/deployment/server on a different domain/subdomain from their front-end apps. It would be phenomenal if users could also serve multiple domains for their data app the same as they can for their front end app.
Then they'd be able to have something like this:
This approach of the front-end and data apps being separate is also has the advantage of not having to disconnect and reconnect websocket connected users when a UI-only change is deployed.
Do you think it would be incredibly difficult to make this change? Our team is bootstrapped with no VC cash, but we may be willing to scramble together funds to sponsor the development of this feature.
Yes this could be added but is currently not prioritized
How is this exposed in Express.js? The same as above?
Why would you need to state the domain? It should say in the certificate. You should only need to specify an array of certificates and keys
Basically you want to put all SSL options in an array, so that you can add multiple entire contexts.
@alexhultman well, express still requires you to create your own server. Express is more of a route dispatcher that you supply to a Node.js server.
With that in mind, the best syntax example I can think of how it's done in the Node.js https2
server, which extends from the tls
module server:
https://nodejs.org/api/tls.html#tls_server_addcontext_hostname_context
const http2 = require('http2');
const server = http2.createSecureServer({});
const contexts = {
'foobar.com': {
cert: '...cert...',
key: '...key...'
},
'example.com': {
cert: '...cert...',
key: '...key...'
}
};
Object.entries(contexts).forEach(([hostname, secureContext]) => {
server.addContext(hostname, secureContext);
});
server.on('request', (req, res) => {
res.statusCode = 200;
res.write('Hello world');
res.end();
});
I would assume that the idea of having the hostname is so that internally, a keyed index could be used to speed up lookups for serving the correct certificate.
Also worth noting that the Node.js TLS server addContext
function does allow for wildcards such as *.example.com
. That would also be incredibly handy for using the uWebsockets ecosystem to build out multi-tenant apps.
This should be possible with the added us_socket_context_get_native_handle that returns SSL_CTX
This is implemented now
This is implemented now
I'm using uWebsockets.js and wondering if there is now the ability to have multiple certificates based on which domain they are accessing from?
We have one device using a self signed which can't be updated which needs to connect and then we want to setup a real SSL for the other people connecting on a separate domain.
I have NGINX with SNI setup to serve the two SSL certificates based on the domain connecting. Just looking for the correct example/structure for SNI when it was added in v18.5.0 to uwebsockets.js, I can't find any docs.
Yes look at ServerName example
It would be nice to have option to serve multiple certs depending on which domain the request came from.
Maybe something along the lines: