Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upSupport both OpenSSL 1.1.0 and 1.0.2 #16130
Conversation
nodejs-github-bot
added
C++
lib / src
labels
Oct 10, 2017
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
@shigeki, how does this approach sound to you? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
/cc @nodejs/crypto |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
MylesBorins
Oct 11, 2017
Member
/cc @nodejs/tsc
@jasnell should we deprecate any of the above mentioned bits in 9.x?
|
/cc @nodejs/tsc |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
MylesBorins
Oct 11, 2017
Member
@davidben do you have an insight into if there is planned fips support for openssl 1.1.0? I know that has been a reason for us to not upgrade in the past
|
@davidben do you have an insight into if there is planned fips support for openssl 1.1.0? I know that has been a reason for us to not upgrade in the past |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 11, 2017
Contributor
You'll have to ask OpenSSL folks about that one. But I don't think it makes sense to wait for it before supporting it at all, maybe just before shipping it in the bundled copy. Switching to 1.1.0 is a large change with deprecation consequences. You're better off getting things in sooner rather than later so you can plan for it before 1.0.2 goes EOL. (I intentionally did not switch the bundled copy in this PR.)
Moreover, folks like Linux distributions may switch before you do. They may just pick up the old PR which would be problematic as it didn't work right.
|
You'll have to ask OpenSSL folks about that one. But I don't think it makes sense to wait for it before supporting it at all, maybe just before shipping it in the bundled copy. Switching to 1.1.0 is a large change with deprecation consequences. You're better off getting things in sooner rather than later so you can plan for it before 1.0.2 goes EOL. (I intentionally did not switch the bundled copy in this PR.) Moreover, folks like Linux distributions may switch before you do. They may just pick up the old PR which would be problematic as it didn't work right. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
mhdawson
Oct 11, 2017
Member
@MylesBorins I think we are at the point where regardless of FIPs validation status for 1.1.0 we will need to upgrade. I believe that version 8.x was the last one where openssl 1.0.2 would be supported long enough to use it in an LTS release.
|
@MylesBorins I think we are at the point where regardless of FIPs validation status for 1.1.0 we will need to upgrade. I believe that version 8.x was the last one where openssl 1.0.2 would be supported long enough to use it in an LTS release. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
mhdawson
Oct 11, 2017
Member
@davidben thanks for putting this together. It is important that we have a path to 1.1.0 sooner than later. Based on the size of change, this may take a bit of time for the reviewers to be able to comment.
|
@davidben thanks for putting this together. It is important that we have a path to 1.1.0 sooner than later. Based on the size of change, this may take a bit of time for the reviewers to be able to comment. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
sam-github
Oct 11, 2017
Member
node 8.x and openssl 1.0.2 both go out of support in Dec, 2019
Interestingly, openssl 1.1.0 goes out of support in Aug, 2018, before 1.0.2.
We can use openssl 1.1.0 for node 9.x, because 9.x will be short-lived.
For node 10.x, which is LTS for us, we will want to use an openssl release that will be supported for its lifetime, I assume. They haven't released one yet, and I can't find anywhere where they commit to a release date for the next LTS openssl, though they say this:
We may designate a release as a Long Term Support (LTS) release. LTS releases will be supported for at least five years and we will specify one at least every four years.
Since 1.0.2 was released in jan 2015, if I understand correctly, there should be a LTS release of openssl by jan 2019. But hopefully they won't wait to the last minute, because node 10.x goes LTS in october 2018.
What would be most awkward for us would be if openssl 1.1.0 goes out of support while we are using it for 10.x and getting ready for LTS, and there is no replacement yet. What would we do, go back to openssl 1.0.2?
I strongly suspect that won't happen. That either 1.1.0 will become LTS by then, or that there will be a 1.2.0 by then and it will be called LTS, but I'm speculating.
|
node 8.x and openssl 1.0.2 both go out of support in Dec, 2019 Interestingly, openssl 1.1.0 goes out of support in Aug, 2018, before 1.0.2. We can use openssl 1.1.0 for node 9.x, because 9.x will be short-lived. For node 10.x, which is LTS for us, we will want to use an openssl release that will be supported for its lifetime, I assume. They haven't released one yet, and I can't find anywhere where they commit to a release date for the next LTS openssl, though they say this:
Since 1.0.2 was released in jan 2015, if I understand correctly, there should be a LTS release of openssl by jan 2019. But hopefully they won't wait to the last minute, because node 10.x goes LTS in october 2018. What would be most awkward for us would be if openssl 1.1.0 goes out of support while we are using it for 10.x and getting ready for LTS, and there is no replacement yet. What would we do, go back to openssl 1.0.2? I strongly suspect that won't happen. That either 1.1.0 will become LTS by then, or that there will be a 1.2.0 by then and it will be called LTS, but I'm speculating. |
MylesBorins
added
the
tsc-agenda
label
Oct 11, 2017
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
MylesBorins
Oct 11, 2017
Member
I'm adding the TSC-Agenda label. Specifically I think the TSC should create a interest group specifically focused around coming up with some suggestions around how we can deal with this. We only have 6 months before the 10.x cut
|
I'm adding the TSC-Agenda label. Specifically I think the TSC should create a interest group specifically focused around coming up with some suggestions around how we can deal with this. We only have 6 months before the 10.x cut |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 11, 2017
Contributor
I can't speak for OpenSSL, but I am sure either 1.1.1 will be released before 1.1.0 goes out of support or they will extend support for 1.1.0. It wouldn't make much sense for 1.0.2 to be the only supported OpenSSL release at any point. :-)
|
I can't speak for OpenSSL, but I am sure either 1.1.1 will be released before 1.1.0 goes out of support or they will extend support for 1.1.0. It wouldn't make much sense for 1.0.2 to be the only supported OpenSSL release at any point. :-) |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
kaduk
Oct 12, 2017
OpenSSL 1.1.1's release is essentially waiting on a final version of the TLS 1.3 specification from the IETF. There is no hard deadline on this happening, but I agree with @davidben that it would be silly for 1.0.2 to be the only supported OpenSSL release.
With respect to the FIPS module, I believe the status is that the old FIPS module is not great code and intentionally is not/will not be compatible with OpenSSL 1.1.x, but there is a partially funded endeavor to do a from-scratch rewrite for a new FIPS module that is compatible with the OpenSSL 1.1.x series. See, e.g., https://www.openssl.org/blog/blog/2017/07/25/fips/
kaduk
commented
Oct 12, 2017
|
OpenSSL 1.1.1's release is essentially waiting on a final version of the TLS 1.3 specification from the IETF. There is no hard deadline on this happening, but I agree with @davidben that it would be silly for 1.0.2 to be the only supported OpenSSL release. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
/cc @danbev |
mhdawson
referenced this pull request
Oct 16, 2017
Closed
Node.js Foundation Technical Steering Committee (TSC) Meeting 2017-10-18 #384
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rvagg
Oct 20, 2017
Member
An important issue we need to consider here is how packagers are dynamically linking Node to their own versions of OpenSSL. Maximising the options for them to do that is going to be in our interests because we're not going to stop them from doing this (dynamic linking and separating out dependant packages is strict policy in Debian-land for instance) and they're already shipping Node in ways that are not strictly compatible with core since we do some patching of OpenSSL in our own statically linked version that is never picked up downstream.
So, supporting both 1.0.2 and 1.1.0 as soon as possible makes a lot of sense, so I'd be keen to see this PR move forward even while we're tied primarily to 1.0.2. Packagers can then opt to compile against 1.1.0 if it suits their needs, or stick to 1.0.2 which is still going to be more common for Linux distros for a while yet. i.e. there's going to be an extended period where our downstream packagers are going to be (either forced to, or preferring to) opting for 1.1.x or 1.0.2 in their linking regardless of what we would prefer them to do and the less we help them the more they're going to be doing a hacky job of it.
Given that we're about to hit 9.x, we're unlikely to get any breaking changes in there so the ideal might look something like this:
- Land fully backward-compat 1.1.0 support some time during 9.x (is seems like this PR does that, but would need some careful confirmation of that)
- Move forward with some API adjustments that set us up better for 1.1.0, mostly as suggested in OP (they're all pretty good suggestions, comments & questions on some below)
- Consider a full switch to 1.1.x in 10.x due to LTS and support timeframe
- Consider leaving opt-in support for 1.0.2 for 10.x to leave that optionality for packagers since it's going to cause headaches on older distros to force 1.1.x support
We can fire up at least one dedicated 1.1.0 node in Jenkins like we have for FIPS now, it'd be great to be testing it multiple ways (ideally we'd also have dynamic-linking nodes in there too, we've talked about that for a while but never done it).
wrt to some of the specifics in OP:
- DSS1 - let's deprecate it during 9.x
- "Weak crypto" - we don't actually use any of these anywhere in core right, we just make some of them available to users, I'd be fine with deprecating during 9.x, however I can see a case for defaulting to
enable-weak-ssl-ciphersand caveat emptor to users who wish to use them for whatever reason crypto.createHash('sha')/ SHA-0 - deprecate during 9.x (potentially similar case to weak crypto above tho?)tlsSocket.getCipher().version- interesting to hear that it's basically useless as is now, let's deprecate during 9.x then since it's unlikely that anyone is using it for anything useful and if they are then they're ill-informed about what it does- Error messages - maybe there's a case for combining it with our own new internal error messages stuff. I haven't looked at what 1.1.0 might have done wrt the internals of their error messages and dealing with them in 1.0.x is pretty disgusting, so maybe it'll be too hard to connect the two in a graceful way that lets us not be too worried about future error message breakage. @jasnell it'd be great if you had the headspace to look into this and advise?
ecdhCurve:false- seems like a no-brainer to me to semver-major this asap, so deprecation in 9.x I suppose- Ticket keys - I don't understand the implications of switching ticket key length, is there really going to be a problem going from 48 to 80 bytes? tbh I have no idea if/how that might break things in user-land, it doesn't sound like a problem to me but I'm far from expert on this topic.
- SSL_METHOD - I love the API suggestion here but we're going to need some userland metrics to try and get a handle on how/if it's used in the wild. Perhaps it'll be trivial to do a quick deprecate/semver-major cycle on this but my guess is that we'd want to be slow and therefore support old and new API for a little while.
Huge thanks to @davidben for this work and being so thorough in description and advice here!
|
An important issue we need to consider here is how packagers are dynamically linking Node to their own versions of OpenSSL. Maximising the options for them to do that is going to be in our interests because we're not going to stop them from doing this (dynamic linking and separating out dependant packages is strict policy in Debian-land for instance) and they're already shipping Node in ways that are not strictly compatible with core since we do some patching of OpenSSL in our own statically linked version that is never picked up downstream. So, supporting both 1.0.2 and 1.1.0 as soon as possible makes a lot of sense, so I'd be keen to see this PR move forward even while we're tied primarily to 1.0.2. Packagers can then opt to compile against 1.1.0 if it suits their needs, or stick to 1.0.2 which is still going to be more common for Linux distros for a while yet. i.e. there's going to be an extended period where our downstream packagers are going to be (either forced to, or preferring to) opting for 1.1.x or 1.0.2 in their linking regardless of what we would prefer them to do and the less we help them the more they're going to be doing a hacky job of it. Given that we're about to hit 9.x, we're unlikely to get any breaking changes in there so the ideal might look something like this:
We can fire up at least one dedicated 1.1.0 node in Jenkins like we have for FIPS now, it'd be great to be testing it multiple ways (ideally we'd also have dynamic-linking nodes in there too, we've talked about that for a while but never done it). wrt to some of the specifics in OP:
Huge thanks to @davidben for this work and being so thorough in description and advice here! |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 20, 2017
Contributor
To add to the distro thing, another reason to get 1.1.0 support in sooner rather than later is this way the support won't bitrot and you can test out all the ramifications while you still have time for the 9.x deprecation cycle. Everything I wrote in the OP was stuff I only realized working on this PR.
Land fully backward-compat 1.1.0 support some time during 9.x (is seems like this PR does that, but would need some careful confirmation of that)
Yup, the intent was that this PR has no user-visible changes when building against 1.0.2. (Though certainly this needs careful review to make sure I got it right!)
"Weak crypto" - we don't actually use any of these anywhere in core right, we just make some of them available to users
As far as I can tell, that's correct. The closest you all get to using some of it is in TLS tests used anonymous ciphers to avoid needing to configure certs. (This PR makes them configure certs instead.)
I haven't looked at what 1.1.0 might have done wrt the internals of their error messages
It's mostly that OpenSSL's errors are not very good at being stable. The most obvious change is that the function code switched from all caps to actually being the function code. And function codes existing at all mean renaming an internally OpenSSL function will change that part of the error string!
And then there are some minor differences just because some of the SSL code was reworked and invalid things are detected differently. (Version negotiation in particular goes through a very different codepath. OpenSSL 1.0.x's version negotiation logic was kind of weird. OpenSSL 1.1.0 does it better.)
Ticket key [...] is there really going to be a problem going from 48 to 80 bytes
Not a problem in itself but the server.setTicketKeys API expects the caller just know 48 is the magic number. It's documented as such and whatnot. There's also no other way to find out the expected key length, so I expect anyone using this API is indeed just hardcoding 48. (Unless they're querying some other context's server.getTicketKeys and passing it in, in which case they wouldn't care.)
https://nodejs.org/api/tls.html#tls_server_setticketkeys_keys
It's possible the answer is just exposing some constant[*] which queries the key length and asking callers to use that rather than a hardcoded 48? In that case, you probably could avoid the relevant commit in this patch and do a deprecation cycle? (I can adjust this PR to drop the "compatibility hack" commits you all don't end up needing. I erred on the side on including them here so it'd be easier to see what the options were.)
[*] Or something? The OpenSSL API to query it is per-SSL_CTX so, in theory, they could decide to make ticket key sizes differ based on SSL_METHOD or random configuration??? Though it's just a constant right now.
SSL_METHOD [...] my guess is that we'd want to be slow and therefore support old and new API for a little while.
Yeah, there's no rush here. You get a mess of deprecated API warnings, but it builds fine.
|
To add to the distro thing, another reason to get 1.1.0 support in sooner rather than later is this way the support won't bitrot and you can test out all the ramifications while you still have time for the 9.x deprecation cycle. Everything I wrote in the OP was stuff I only realized working on this PR.
Yup, the intent was that this PR has no user-visible changes when building against 1.0.2. (Though certainly this needs careful review to make sure I got it right!)
As far as I can tell, that's correct. The closest you all get to using some of it is in TLS tests used anonymous ciphers to avoid needing to configure certs. (This PR makes them configure certs instead.)
It's mostly that OpenSSL's errors are not very good at being stable. The most obvious change is that the function code switched from all caps to actually being the function code. And function codes existing at all mean renaming an internally OpenSSL function will change that part of the error string! And then there are some minor differences just because some of the SSL code was reworked and invalid things are detected differently. (Version negotiation in particular goes through a very different codepath. OpenSSL 1.0.x's version negotiation logic was kind of weird. OpenSSL 1.1.0 does it better.)
Not a problem in itself but the It's possible the answer is just exposing some constant[*] which queries the key length and asking callers to use that rather than a hardcoded 48? In that case, you probably could avoid the relevant commit in this patch and do a deprecation cycle? (I can adjust this PR to drop the "compatibility hack" commits you all don't end up needing. I erred on the side on including them here so it'd be easier to see what the options were.) [*] Or something? The OpenSSL API to query it is per-
Yeah, there's no rush here. You get a mess of deprecated API warnings, but it builds fine. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rvagg
Oct 21, 2017
Member
https://nodejs.org/api/tls.html#tls_server_setticketkeys_keys
Note: The key's Buffer should be 48 bytes long. See ticketKeys option in tls.createServer for more information on how it is used.
I see the problem now, we've hardwired it in our API and docs, that's a bit unfortunate. I'm not seeing an obvious best way around this. A constant could be backported to 8.x and maybe even 6.x so users could start querying it. The awkward part is that we throw if ! 48 (env->ThrowTypeError("Ticket keys length must be 48 bytes")) and it seems to me from your patch that it'd be too late to insert the callback at setTicketKeys() only if we get 48 bytes because that'd be the nicer way to approach this and then we could more slowly deprecate 48-byte keys. So, unless we want to be stuck with the old behaviour then we're going to have to force a break here. This is an API we're not going to get good ecosystem usage numbers because it's most likely used in applications rather than libraries. My hunch is that it's got very limited usage. IIRC it was PayPal that drove interest in this and I haven't seen much of anyone else talk about ticket keys than @indutny.
Next step would be some reviews by people with more of a clue. @indutny, @bnoordhuis, @shigeki, maybe @sam-github? Would you mind taking a look over the code? It's a surprisingly small diff and shouldn't take too long. I've had a look over it and will play more before I give a +1.
It'll also need some testing actually compiling against 1.1.0 across our platforms so we'll have to come up with a strategy that's a little more comprehensive than just adding a single 1.1.0 box into the mix.
@davidben would you mind rebasing off master so we can run this in CI? Currently having a problem with that: https://ci.nodejs.org/job/node-test-commit/13351/console
|
https://nodejs.org/api/tls.html#tls_server_setticketkeys_keys
I see the problem now, we've hardwired it in our API and docs, that's a bit unfortunate. I'm not seeing an obvious best way around this. A constant could be backported to 8.x and maybe even 6.x so users could start querying it. The awkward part is that we throw if ! 48 ( Next step would be some reviews by people with more of a clue. @indutny, @bnoordhuis, @shigeki, maybe @sam-github? Would you mind taking a look over the code? It's a surprisingly small diff and shouldn't take too long. I've had a look over it and will play more before I give a +1. It'll also need some testing actually compiling against 1.1.0 across our platforms so we'll have to come up with a strategy that's a little more comprehensive than just adding a single 1.1.0 box into the mix. @davidben would you mind rebasing off master so we can run this in CI? Currently having a problem with that: https://ci.nodejs.org/job/node-test-commit/13351/console |
bnoordhuis
reviewed
Oct 21, 2017
Mostly LGTM. I agree on deprecating ecdhCurve and that there probably isn't much risk in changing the ticket keys API (and defensible for v9.x because it's ultimately openssl that drives this change.)
| X509_STORE* store = SSL_CTX_get_cert_store(ctx); | ||
| X509_STORE_CTX store_ctx; | ||
| ret = X509_STORE_CTX_init(&store_ctx, store, nullptr, nullptr); |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bnoordhuis
Oct 21, 2017
Member
Can you explain in the commit log why this no longer works? X509_STORE_CTX_init() and X509_STORE_CTX_cleanup() still exist in 1.1.0, don't they?
bnoordhuis
Oct 21, 2017
Member
Can you explain in the commit log why this no longer works? X509_STORE_CTX_init() and X509_STORE_CTX_cleanup() still exist in 1.1.0, don't they?
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 21, 2017
Contributor
Yeah, I'm not really sure why they still exist TBH. Some weird form of reset I guess. The reason this is needed is X509_STORE_CTX is opaque and thus cannot be stack-allocated. (I can add a note in the commit message to clarify this.)
davidben
Oct 21, 2017
Contributor
Yeah, I'm not really sure why they still exist TBH. Some weird form of reset I guess. The reason this is needed is X509_STORE_CTX is opaque and thus cannot be stack-allocated. (I can add a note in the commit message to clarify this.)
| assert.ok( | ||
| /SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol/.test(e.message), | ||
| /SSL routines:[^:]*:(unknown protocol|wrong version number)/.test( | ||
| e.message), |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bnoordhuis
Oct 21, 2017
Member
Tiny nit: can you indent by four spaces here and below? (Line continuation.)
bnoordhuis
Oct 21, 2017
Member
Tiny nit: can you indent by four spaces here and below? (Line continuation.)
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
apapirovski
Oct 21, 2017
Member
2 spaces is the standard line continuation for our JS style guide. This seems fine to me but if you really, really wanted to make it a bit less awkward because of the length then maybe just put the RegExp into a variable... ?
apapirovski
Oct 21, 2017
Member
2 spaces is the standard line continuation for our JS style guide. This seems fine to me but if you really, really wanted to make it a bit less awkward because of the length then maybe just put the RegExp into a variable... ?
| @@ -1084,8 +1084,10 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo<Value>& args) { | ||
| node::Utf8Value curve(env->isolate(), args[0]); | ||
| #if OPENSSL_VERSION_NUMBER < 0x10100000L | ||
| SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_ECDH_USE); | ||
| SSL_CTX_set_ecdh_auto(sc->ctx_, 1); |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bnoordhuis
Oct 21, 2017
Member
Somewhat amusingly, we only started doing that last month. Is there a better way of doing that with openssl 1.0.2? I couldn't find one.
bnoordhuis
Oct 21, 2017
Member
Somewhat amusingly, we only started doing that last month. Is there a better way of doing that with openssl 1.0.2? I couldn't find one.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 21, 2017
Contributor
Nah, that's the way to do it. They added and deprecated APIs one release after another. :-) Both of those calls are no-ops in 1.1.0. The ifdef isn't needed, but it silences some build warnings, so I figured I'd clear them.
davidben
Oct 21, 2017
Contributor
Nah, that's the way to do it. They added and deprecated APIs one release after another. :-) Both of those calls are no-ops in 1.1.0. The ifdef isn't needed, but it silences some build warnings, so I figured I'd clear them.
| @@ -7,20 +7,22 @@ if (!common.hasIPv6) | ||
| common.skip('no IPv6 support'); | ||
| const assert = require('assert'); | ||
| const fixtures = require('../common/fixtures'); |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 21, 2017
Contributor
Hrm. That doesn't seem to work. I also don't see common.fixtures in any other test. There's common.fixturesDir, though that just points to the directory. Is that plus fs.readFileSync plus path-munging preferred over fixtures.readKey?
davidben
Oct 21, 2017
Contributor
Hrm. That doesn't seem to work. I also don't see common.fixtures in any other test. There's common.fixturesDir, though that just points to the directory. Is that plus fs.readFileSync plus path-munging preferred over fixtures.readKey?
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
apapirovski
Oct 21, 2017
Member
This seems fine as is. It matches what people were instructed to use in the latest Code & Learn.
apapirovski
Oct 21, 2017
Member
This seems fine as is. It matches what people were instructed to use in the latest Code & Learn.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bnoordhuis
Oct 21, 2017
Member
Sorry, you're both right. I thought (don't know why) that common.js re-exported everything from fixtures.js.
bnoordhuis
Oct 21, 2017
Member
Sorry, you're both right. I thought (don't know why) that common.js re-exported everything from fixtures.js.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 21, 2017
Contributor
I've addressed the comments, rebased to master, and removed the original ecdhCurve commit. Instead, I've added fd6658e to suppress the test in OpenSSL 1.1.0. I'm not sure how to make that as deprecated, so I'll probably need some help from one of you all there. (Or should that be done separately?)
I've left the ticket one alone for the time being, since it sounds like that's still being discussed. I also realize now I missed the "DSS1" one... shall I remove that workaround in favor of a deprecation + test suppression like ecdhCurve?
|
I've addressed the comments, rebased to master, and removed the original I've left the ticket one alone for the time being, since it sounds like that's still being discussed. I also realize now I missed the "DSS1" one... shall I remove that workaround in favor of a deprecation + test suppression like |
bnoordhuis
approved these changes
Oct 21, 2017
Thanks, basically LGTM pending two questions.
I'm not sure how to make that as deprecated, so I'll probably need some help from one of you all there.
Untested but this diff shows the basic principle:
diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md
index 75c5c0feb5..62c0a7a0ce 100644
--- a/doc/api/deprecations.md
+++ b/doc/api/deprecations.md
@@ -737,6 +737,13 @@ Type: Runtime
internal mechanics of the `REPLServer` itself, and is therefore not
necessary in user space.
+<a id="DEP0083"></a>
+### DEP0083: Disabling ECDH with `{ ecdhCurve: false }`
+
+Type: Runtime
+
+Blurb goes here.
+
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
[`Buffer.from(array)`]: buffer.html#buffer_class_method_buffer_from_array
diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
index d7e349b239..ff2fa178d8 100644
--- a/lib/_tls_wrap.js
+++ b/lib/_tls_wrap.js
@@ -919,6 +919,15 @@ Server.prototype.setTicketKeys = function setTicketKeys(keys) {
};
+function ecdhCurveWarning() {
+ if (ecdhCurveWarning.emitted) return;
+ process.emitWarning('{ ecdhCurve: false } is a deprecated no-op.',
+ 'DeprecationWarning',
+ 'DEP0083');
+ ecdhCurveWarning.emitted = true;
+}
+ecdhCurveWarning.emitted = false;
+
Server.prototype.setOptions = function(options) {
this.requestCert = options.requestCert === true;
this.rejectUnauthorized = options.rejectUnauthorized !== false;
@@ -931,8 +940,10 @@ Server.prototype.setOptions = function(options) {
if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
if (options.crl) this.crl = options.crl;
if (options.ciphers) this.ciphers = options.ciphers;
- if (options.ecdhCurve !== undefined)
+ if (options.ecdhCurve !== undefined) {
this.ecdhCurve = options.ecdhCurve;
+ if (this.ecdhCurve === false) ecdhCurveWarning();
+ }
if (options.dhparam) this.dhparam = options.dhparam;
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;I also realize now I missed the "DSS1" one... shall I remove that workaround in favor of a deprecation + test suppression like ecdhCurve?
No strong opinion. It doesn't cost much to keep around and logging deprecation warnings from C++ is kind of cumbersome because you have to thread through the node::Environment. The function you are looking for is ProcessEmitWarning() if you want to pursue that.
| sc->ticket_key_aes_, iv) <= 0 || | ||
| HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_), | ||
| EVP_sha256(), nullptr) <= 0) { | ||
| return -1; |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bnoordhuis
Oct 21, 2017
Member
Doesn't this leak the resources allocated by EVP_EncryptInit_ex() when HMAC_Init_ex() fails?
bnoordhuis
Oct 21, 2017
Member
Doesn't this leak the resources allocated by EVP_EncryptInit_ex() when HMAC_Init_ex() fails?
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Nov 2, 2017
Contributor
It does not. hctx is owned by the caller, so it's meant to get cleaned up after this function returns.
davidben
Nov 2, 2017
Contributor
It does not. hctx is owned by the caller, so it's meant to get cleaned up after this function returns.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bnoordhuis
Nov 3, 2017
Member
Ah, I found the corresponding logic in t1_lib.c and s3_srvr.c. Okay, thanks for clearing that up.
bnoordhuis
Nov 3, 2017
Member
Ah, I found the corresponding logic in t1_lib.c and s3_srvr.c. Okay, thanks for clearing that up.
| if (EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr, sc->ticket_key_aes_, | ||
| iv) <= 0 || | ||
| HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_), | ||
| EVP_sha256(), nullptr) <= 0) { |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
| @@ -7,20 +7,22 @@ if (!common.hasIPv6) | ||
| common.skip('no IPv6 support'); | ||
| const assert = require('assert'); | ||
| const fixtures = require('../common/fixtures'); |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bnoordhuis
Oct 21, 2017
Member
Sorry, you're both right. I thought (don't know why) that common.js re-exported everything from fixtures.js.
bnoordhuis
Oct 21, 2017
Member
Sorry, you're both right. I thought (don't know why) that common.js re-exported everything from fixtures.js.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rvagg
Oct 22, 2017
Member
FYI https://ci.nodejs.org/job/node-test-commit/13361/ CI is happy with this as is sans the linting problem in test/parallel/test-tls-ecdh-disable.js
|
FYI https://ci.nodejs.org/job/node-test-commit/13361/ CI is happy with this as is sans the linting problem in test/parallel/test-tls-ecdh-disable.js |
gibfahn
referenced this pull request
Oct 22, 2017
Closed
Add --without-intl builds to CI matrix #419
rvagg
referenced this pull request
Oct 23, 2017
Closed
test: pass process.env to child processes #16405
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rvagg
Oct 23, 2017
Member
I'm playing with 1.1.0 as a local (non-global) build to test this out and I have a few notes.
First, for @davidben, I'm getting 2 failures, not just parallel/test-http2-create-client-connect but also parallel/test-tls-close-notify on Linux and macOS. This is using the current HEAD of openssl/openssl:
Error: 140737282147264:error:140E0197:SSL routines:SSL_shutdown:shutdown while in init:ssl/ssl_lib.c:1988:
140737282147264:error:140E0197:SSL routines:SSL_shutdown:shutdown while in init:ssl/ssl_lib.c:1988:
Notes, mainly for me and for anyone else wanting to test this out for themselves without having to install OpenSSL globally:
- #16405 is required to get make some of the tests happy (env not passed, so
LD_LIBRARY_PATHdoesn't work) - Need to grab OpenSSL from source because 1.1.0g isn't released yet and this patch set requires it
- Using OpenSSL unpacked inside the Node directory as
openssl-1.1.0g/(cd openssl-1.1.0g && ./config --prefix=$(pwd)/out/ --openssldir=$(pwd)/out/ && make -j4 && make install)./configure --shared-openssl --shared-openssl-includes=$(pwd)/openssl-1.1.0g/out/include/ --shared-openssl-libpath=$(pwd)/openssl-1.1.0g/out/lib/ && make -j4export LD_LIBRARY_PATH=$(pwd)/openssl-1.1.0g/outexport DYLD_LIBRARY_PATH=$(pwd)/openssl-1.1.0g/out(macOS)export PATH=$(pwd)/openssl-1.1.0g/out/bin/:$PATH(for tests using theopensslCLI)make test
My thinking here is that we could test in bulk across most of our CI by either checking in a openssl-1.1.0g/ source directory or just downloading it and unpacking it, then testing with a modified Makefile that does all of these steps. That wouldn't require any CI modification or special machines.
A step beyond that for a more permanent test-dynamically-linked-openssl might be to bake in these steps into CI somewhere so they can be run on any of our hosts without requiring a special host like we do for FIPS. Or perhaps it's sufficient to just have a sub-job that reuses some of the other hosts to do this work across a small subset of our platforms. I'll take this up with the Build WG because it'd be great to start testing dynamic linking in our CI. We could then conceivably extend to some of the other shared libs we support.
|
I'm playing with 1.1.0 as a local (non-global) build to test this out and I have a few notes. First, for @davidben, I'm getting 2 failures, not just
Notes, mainly for me and for anyone else wanting to test this out for themselves without having to install OpenSSL globally:
My thinking here is that we could test in bulk across most of our CI by either checking in a A step beyond that for a more permanent test-dynamically-linked-openssl might be to bake in these steps into CI somewhere so they can be run on any of our hosts without requiring a special host like we do for FIPS. Or perhaps it's sufficient to just have a sub-job that reuses some of the other hosts to do this work across a small subset of our platforms. I'll take this up with the Build WG because it'd be great to start testing dynamic linking in our CI. We could then conceivably extend to some of the other shared libs we support. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
davidben
Oct 23, 2017
Contributor
I'll try to get to the other comments later today or this week, but to answer the immediate test issues real quick:
So, OpenSSL master is slated to be 1.1.1, not 1.1.0g. I've been testing with the OpenSSL_1_1_0-stable branch, on the assumption that 1.1.1 adding TLS 1.3 might throw off others of your tests (though I see it's off by default) and dealing with that later would be simpler. But clearly I should have tested master too, given this new failure!
The immediate cause for this is openssl/openssl#4574, however I think ultimately it's Node that's misbehaving. You all are calling into user code within the OpenSSL info callback, which might re-entrantly call into more code. OpenSSL, 1.1.x or 1.0.x, is unlikely to behave well in all scenarios where SSL_read calls the handshake calls info_callback calls Node calls SSL_read again. TLSWrap too is also unlikely to be happy if random methods are called reentrantly from ClearOut or ClearIn.
Some part of the assembly leaking up to TLSSocket.prototype._finishInit calling this.emit('secure') should probably be deferred an event loop iteration. Though it would also be weird if some read or write callback runs just before the 'secure' event, rather than just after, so we may want to be a bit careful there.
(Yeah, parallel/test-http2-create-client-connect failing is a known issue. Sorry I haven't gotten to writing up the details there yet. Just finished a much-needed vacation the past week, so I've got quite a backlog. :-) )
|
I'll try to get to the other comments later today or this week, but to answer the immediate test issues real quick: So, OpenSSL The immediate cause for this is openssl/openssl#4574, however I think ultimately it's Node that's misbehaving. You all are calling into user code within the OpenSSL info callback, which might re-entrantly call into more code. OpenSSL, 1.1.x or 1.0.x, is unlikely to behave well in all scenarios where Some part of the assembly leaking up to (Yeah, parallel/test-http2-create-client-connect failing is a known issue. Sorry I haven't gotten to writing up the details there yet. Just finished a much-needed vacation the past week, so I've got quite a backlog. :-) ) |
davidben commentedOct 10, 2017
WARNING: This is a rather large change.
In a previous PR, it was suggested that Node was uninterested in incremental changes for OpenSSL 1.1.0 support, with the worry that
OPENSSL_VERSION_NUMBERifdefs in the code will appear as if you support 1.1.0 when you don't. However, doing it all in one commit is also not feasible as one cannot possibly review such a change. I've instead attempted a middle ground where there is one PR, but it is made up of many many individual commits. It's recommended that you review each commit individually and not use Github's combined diff UI.Let me know if you would like the branch presented differently. (Go back to separate PRs, etc.)
This was partially adapted from PR #8491 but fixes some issues with it. This PR does not switch Node to OpenSSL 1.1.0, nor does it break support with OpenSSL 1.0.2. Two notes:
OpenSSL needs openssl/openssl#4384 applied. This means OpenSSL 1.1.0g or later, which is not yet released. Since it would otherwise compile but not work, I've added a
OPENSSL_VERSION_NUMBERcheck to catch this.test-http2-create-client-connect does not work. I've fixed an issue with the test itself, but it still doesn't pass in 1.1.0. The HTTP/2 code bypasses the usual JS afterWrite code which, combined with some weird OpenSSL behaviors, causes the error-handling to be a little off. It actually affects 1.0.2, just not on that particular test's input. I'll file a separate ticket for all that, but since it affects 1.0.2 too, I figure that should be separate to this PR. I'll put together a report with details later.
Additionally, switching to OpenSSL 1.1.0 comes with a number of removals. OpenSSL has documentation here:
https://www.openssl.org/news/openssl-1.1.0-notes.html
https://www.openssl.org/news/changelog.html#x7
I've described what I did notice below. Where it was feasible, I added a change to retain compatibility in Node's API as much as possible. I'll let you all decide exactly which of those you wish to include. For instance, one could imagine adding 1.1.0 support (so it doesn't bitrot), but still shipping with 1.0.2 for a deprecation cycle, and then switching to 1.1.0 with all the breakages that entails. That would let you drop some of the commits in this branch.
DSS1
In OpenSSL 1.0.2, the digest name "DSS1" was a DSA alias for SHA-1, part of the
EVP_Sign*legacy. This has been removed in OpenSSL 1.1.0. In crypto: add compatibility logic for "DSS1" and "dss1", I've added a compatibility knob for it, but it may be better to skip this commit and instead deprecate the name, since there are other unavoidable removals anyway.Weak crypto
OpenSSL 1.1.0 disables a number of weak ciphers like RC4 or anonymous ciphers by default. You can build OpenSSL with
enable-weak-ssl-ciphersif you wish to restore them. If you do, you probably want a deprecation cycle.OpenSSL 1.1.0 also rejects RSA keys smaller than 1024 bits by default and other settings. See https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_security_level.html
SHA-0
SHA-0 was removed in OpenSSL 1.1.0. This is an insecure and virtually unused early variant of SHA-1, but Node did nominally expose it via
crypto.createHash('sha'). Short of adding a compatibility reimplementation of the hash, I think this is an unavoidable removal in OpenSSL 1.1.0.tlsSocket.getCipher().version
The
versionfield returned bytlsSocket.getCipher()did not do what was documented in 1.0.2 and always returned the string "TLSv1/SSLv3". I've retained that behavior in Node and fixed the documentation, but you all may wish to go through a deprecation cycle with that field. It's not useful anyway.See the commit crypto: hard-code tlsSocket.getCipher().version for details.
Error messages
Some of the errors OpenSSL emits are slightly differnent. I had to fix a few strings in tests. Dunno how much you care about this sort of behavior change.
ecdhCurve
The
ecdhCurveserver setting in the TLS module is defined to disable ECDH. This is a remnant of an implementation quirk in OpenSSL where it lacked curve negotiation logic. That meant there exists an ECDH-less state for OpenSSL servers separate from the cipher list. (Note no such mode exists on the client.) They fixed this and added this "ecdh_auto" opt-in knob in 1.0.2. In 1.1.0, this opt-in knob was made always on. Unfortunately, Node exposed this state by settingecdhCurveto false.I've hacked it back in in the comment crypto: fix setting ecdhCurve to false with OpenSSL 1.1.0 by appending to the user's cipher list. Your call whether you wish to keep this. Since everything in TLS without ECDHE is insecure anyway, I would suggest taking a deprecation cycle to remove this instead and dropping the commit.
Ticket keys
OpenSSL 1.0.2 used a 16 byte key name, 16 byte AES-128-CBC key, and 16 byte HMAC-SHA256 key for session tickets. OpenSSL 1.1.0 switches this to a 16 byte key name, 32 byte AES-256-CBC key, and 32 byte HMAC-SHA256 key. One can query the size of the ticket key with SSL_CTX_get_tlsext_ticket_keys to avoid the hard-coded 48, but that doesn't really solve the problem. That ticket keys are 48 bytes are exposed in Node's public APIs.
In crypto: emulate OpenSSL 1.0.x ticket scheme in 1.1.x, I've implemented the callback to use the old scheme in OpenSSL 1.1.0. This works, but you may wish to think about alternate APIs (maybe expose a constant somewhere for the ticket key size) that allow the ticket scheme to change.
SSL_METHOD and protocol versions
OpenSSL's old SSL_METHODs are kind of a mess. You had methods like
TLSv1_1_client_methodwhich locked you to TLS 1.1 and only worked for clients. Then you had methods likeTLSv1_1_methodwhich were exactly the same asTLSv1_1_client_methodexcept you could use them for either clients or server. Then you hadSSLv23_methodwhich was actually version-flexible and misnamed. Then, to configure versions, you had toggles likeSSL_OP_NO_TLSv1_1.This is a mess. In OpenSSL 1.1.0, things are much better.
SSLv23_methodis renamed toTLS_methodand then you have APIs likeSSL_CTX_set_min_proto_versionandSSL_CTX_set_max_proto_version.TLS_client_methodandTLS_server_methodsadly still exist and are approximately as pointless as they always were. :-)Unfortunately, Node's APIs thoroughly expose this and has the user specify the string name of the method to use, rather than min/max version configurations. OpenSSL 1.1.0 retains the old APIs but marks them as deprecated, so this still works but you have a lot oc compile warnings. I have not attempted to remove the deprecated APIs in this branch as I think you all need a better API first.
My suggestion:
secureProtocoloption.minVersionandmaxVersion.secureProtocolis completely removed, stop using the version-locked methods and tripping the deprecation warnings.Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passesAffected core subsystem(s)
crypto