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

Validate SslContext thoroughly #2124

Merged
merged 27 commits into from Oct 8, 2019
Merged

Conversation

jyblue
Copy link
Contributor

@jyblue jyblue commented Sep 28, 2019

Motivation:

In some case, a server can start with a misconfigured SslContext and
fail when the serving its first request. If a server refuses to start
in such a case, users could fix their configuration problem at an earlier
stage with much less confusion.

Modifications:

  • Move VirtualHost.validateSslContext() to VirtualHostBuilder
  • Make an actual SslEngine with the given SslContext and perform
    an initial handshake to trigger most configuration issues.
  • Add throws SSLException to tls() builder methods.
  • Remove the deprecated sslContext() methods.

Result:

Motivation:

In some case, servers can start with miss-configured `SslContext` and
fail when users request ssl/tls connections.
If servers refuse to start in that case, users can fix their
configuration before severs start.

Modifications:

Add `VirtualHostBuilder.validateSslContext()`

Result:

Fixes line#1844
@jyblue
Copy link
Contributor Author

jyblue commented Sep 28, 2019

Hello, here is my draft.
And I'm not sure I'm on the right way to go. Please see my questions.

  1. If SSLEngine is created without exceptions, is it guaranteed that SslContext is well-configured?
  2. If not guaranteed, what configurations do I have to validate manually?
    I added some test cases and it seems that most of configurations are already validated by other classes before validateSslContext() method is hit.
  3. I cannot reproduce initial bug report in my test code. Because when I try to load KeyStore without proper key, it throws a exception, which means the server fails to start. How do I create KeyStore with invalid key?

Thank you in advance 🙇‍♂️

@jyblue
Copy link
Contributor Author

jyblue commented Sep 28, 2019

One more question,
I added dummy JKS file to resources directory for test purpose and source code contains its password.
Is it okay with any code security issue?

@trustin
Copy link
Member

trustin commented Sep 29, 2019

@jyblue wrote:

  1. If SSLEngine is created without exceptions, is it guaranteed that SslContext is well-configured?

I think so. If not, we can add more checks later.

  1. I cannot reproduce initial bug report in my test code. Because when I try to load KeyStore without proper key, it throws a exception, which means the server fails to start. How do I create KeyStore with invalid key?

You have to try to reproduce the problem with 1) a PEM format private key and 2) OpenSSL enabled (which is enabled by default, but you'll have to check using Flags.useOpenSsl()).

I added dummy JKS file to resources directory for test purpose and source code contains its password. Is it okay with any code security issue?

I don't think it has any security implication if you used a self-signed certificate and if it's not encrypted with your personal password. 😄

@trustin trustin added the defect label Sep 29, 2019
@anuraaga
Copy link
Collaborator

We have validation of created contexts here

https://github.com/line/armeria/blob/master/core/src/main/java/com/linecorp/armeria/internal/SslContextUtil.java

I think the new checks here look the same as what we do so maybe they don't help with the issue. I guess we need to reproduce the error case we first to know for sure.

@jyblue
Copy link
Contributor Author

jyblue commented Sep 29, 2019

@anuraaga Thanks for good hint.

I think I found the reason.
In this case, null was given to KeyStore.load() as a password parameter and KeyStore.load() didn't perform password integrity check.

store.load(url.openStream(), password != null ? password.toCharArray()

https://docs.oracle.com/javase/7/docs/api/java/security/KeyStore.html
A password may be given to unlock the keystore , or to check the integrity of the keystore data. If a password is not given for integrity checking, then integrity checking is not performed.

If steps reproduced,

KeyStore.load(keyStore, null);  // not performed password check
...
// server starts
// wait for ssl/tls request
...
KeyStore.getEntry(password); // password check, exception occurs and server fails

I will write a test case to reproduce it.

@trustin
Copy link
Member

trustin commented Sep 30, 2019

Thanks @jyblue! Looks like we are going into the right direction. Please keep us posted.

@codecov
Copy link

codecov bot commented Sep 30, 2019

Codecov Report

Merging #2124 into master will decrease coverage by 0.02%.
The diff coverage is 91.66%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #2124      +/-   ##
============================================
- Coverage     73.59%   73.56%   -0.03%     
+ Complexity     9571     9567       -4     
============================================
  Files           837      837              
  Lines         36836    36863      +27     
  Branches       4543     4547       +4     
============================================
+ Hits          27108    27119      +11     
- Misses         7402     7421      +19     
+ Partials       2326     2323       -3
Impacted Files Coverage Δ Complexity Δ
...ava/com/linecorp/armeria/server/ServerBuilder.java 74.53% <ø> (+1.97%) 104 <0> (+1) ⬆️
.../java/com/linecorp/armeria/server/VirtualHost.java 56% <100%> (+0.53%) 32 <0> (-2) ⬇️
...om/linecorp/armeria/server/VirtualHostBuilder.java 61.15% <91.3%> (+4.51%) 64 <2> (+2) ⬆️
...rp/armeria/server/tomcat/ManagedTomcatService.java 50.84% <0%> (-22.04%) 6% <0%> (-1%)
...armeria/server/tomcat/Tomcat90ProtocolHandler.java 48.14% <0%> (-14.82%) 11% <0%> (-4%)
...corp/armeria/client/DefaultEventLoopScheduler.java 79.27% <0%> (-9.91%) 30% <0%> (-5%)
...inecorp/armeria/client/AbstractEventLoopState.java 92.3% <0%> (-7.7%) 6% <0%> (-1%)
...com/linecorp/armeria/client/OneEventLoopState.java 66.66% <0%> (-4.77%) 6% <0%> (-1%)
...om/linecorp/armeria/server/jetty/JettyService.java 63.05% <0%> (-2.96%) 23% <0%> (-1%)
.../linecorp/armeria/server/tomcat/TomcatService.java 63.82% <0%> (-2.66%) 24% <0%> (-1%)
... and 17 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 326877a...09c488f. Read the comment docs.

@jyblue
Copy link
Contributor Author

jyblue commented Oct 2, 2019

Hello, here is my research and code updated. PTAL 😀

Test conclusion
If key store password is not given to PKCS12 key store,
a server is built, running without exceptions, but fails when ssl/tls request come.

Internal cause
When netty.SslHandler is trying to decode client's first packet, a exception occurs.
I think SslHandler doesn't have a valid access to key store (not for sure).
https://github.com/netty/netty/blob/d8b1a2d93f556a08270e6549bf7f91b3b09f24bb/handler/src/main/java/io/netty/handler/ssl/SslHandler.java#L1329

Validation
I added validation method which tries dummy ssl/tls request,
and it produces same exception as original error.

failed to validate SSL/TLS configuration : javax.net.ssl.SSLHandshakeException: error:100000ae:SSL routines:OPENSSL_internal:NO_CERTIFICATE_SET
java.lang.RuntimeException: failed to validate SSL/TLS configuration : javax.net.ssl.SSLHandshakeException: error:100000ae:SSL routines:OPENSSL_internal:NO_CERTIFICATE_SET
	at com.linecorp.armeria.server.VirtualHostBuilder.build(VirtualHostBuilder.java:843)
	at com.linecorp.armeria.server.ServerBuilder.build(ServerBuilder.java:1432)

Note
JKS key store doesn't produce any exception when key store password is not given.

FYI
Bug reproduction and detail report
https://github.com/jyblue/ArmeriaIssueReproduction1844

@trustin
Copy link
Member

trustin commented Oct 3, 2019

Please remove the WIP status if it's ready for reviews. 😉

@jyblue jyblue marked this pull request as ready for review October 3, 2019 10:47
@jyblue jyblue changed the title [WIP] Add SslContext validation Add SslContext validation Oct 3, 2019
@trustin
Copy link
Member

trustin commented Oct 4, 2019

You also have to release the two SSLEngines you created during validation:

try {
    ...
} finally {
    ReferenceCountUtil.release(serverEngine);
    ReferenceCountUtil.release(clientEngine);
}

@jyblue
Copy link
Contributor Author

jyblue commented Oct 5, 2019

Here is my update.

  • Rename validateSslContext to validateKeyStoreNullPassword
    Because it is confused with VirtualHost.validateSslContext
  • Make validateKeyStoreNullPassword to be called inside of tls(KeyManagerFactory, TlsCoustomizer)
    Making the validation more close to user-side, so users can easily find their miss configured key store password

@jyblue
Copy link
Contributor Author

jyblue commented Oct 5, 2019

It seems @Ignore annotation on testJksKeyStoreWithNullPassword() doesn't work.
I will take a look and fix it.

`VirtualHost.validateSslContext()`

* Remove the `validateSslContext()` call from VirtualHost's constructor.
* Add `SSLException` exception to method signature
  `ServerBuilder.tls(SslContext)`
  `VirtualHostBuilder.tls(SslContext)`
* Update javadoc on `testJksKeyStoreWithNullPassword()` test case
* Remove deprecated method
  `ServerBuilder.sslContext(sslContext)`
  `ServerBuilder.sslContext(protocol, keyCertChainFile, keyFile)`
  `ServerBuilder.sslContext(protocol, keyCertChainFile, keyFile,
                            keyPassword)`
  `VirtualHostBuilder.sslContext(sslContext)`
  `VirtualHostBuilder.sslContext(protocol, keyCertChainFile, keyFile)`
  `VirtualHostBuilder.sslContext(protocol, keyCertChainFile, keyFile,
                                 keyPassword)`
@jyblue
Copy link
Contributor Author

jyblue commented Oct 7, 2019

Thank you for review, again. Here is my update.

I fixed items from previous review
and removed 6 deprecated method (had a chat with @trustin for this)
Please see below removed method list.

  • ServerBuilder.sslContext(sslContext)
  • ServerBuilder.sslContext(protocol, keyCertChainFile, keyFile)
  • ServerBuilder.sslContext(protocol, keyCertChainFile, keyFile, keyPassword)
  • VirtualHostBuilder.sslContext(sslContext)
  • VirtualHostBuilder.sslContext(protocol, keyCertChainFile, keyFile)
  • VirtualHostBuilder.sslContext(protocol, keyCertChainFile, keyFile, keyPassword)

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your work! 🙇

@ikhoon ikhoon added this to the 0.94.1 milestone Oct 8, 2019
Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one nit. Nice work, @jyblue! 👍

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Great job!

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot, @jyblue!

@trustin trustin merged commit 26c8fae into line:master Oct 8, 2019
@trustin
Copy link
Member

trustin commented Oct 8, 2019

Nice work, @jyblue! I've updated the PR description and commit message for you. 😉

@trustin trustin changed the title Add SslContext validation Validate SslContext thoroughly Oct 8, 2019
@jyblue jyblue deleted the add_sslcontext_validation branch October 8, 2019 14:20
eugene70 pushed a commit to eugene70/armeria that referenced this pull request Oct 16, 2019
Motivation:

In some case, a server can start with a misconfigured `SslContext` and
fail when the serving its first request. If a server refuses to start
in such a case, users could fix their configuration problem at an earlier
stage with much less confusion.

Modifications:

- Move `VirtualHost.validateSslContext()` to `VirtualHostBuilder`
- Make an actual `SslEngine` with the given `SslContext` and perform
  an initial handshake to trigger most configuration issues.
- Add `throws SSLException` to `tls()` builder methods.
- Remove the deprecated `sslContext()` methods.

Result:

- Fixes line#1844
- (Breaking) `tls()` now throws a checked `SSLException`.
- (Breaking) `sslContext()` methods, previously deprecated, have been
  removed.
fmguerreiro pushed a commit to fmguerreiro/armeria that referenced this pull request Sep 19, 2020
Motivation:

In some case, a server can start with a misconfigured `SslContext` and
fail when the serving its first request. If a server refuses to start
in such a case, users could fix their configuration problem at an earlier
stage with much less confusion.

Modifications:

- Move `VirtualHost.validateSslContext()` to `VirtualHostBuilder`
- Make an actual `SslEngine` with the given `SslContext` and perform
  an initial handshake to trigger most configuration issues.
- Add `throws SSLException` to `tls()` builder methods.
- Remove the deprecated `sslContext()` methods.

Result:

- Fixes line#1844
- (Breaking) `tls()` now throws a checked `SSLException`.
- (Breaking) `sslContext()` methods, previously deprecated, have been
  removed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cryptic error message when TLS key store password is incorrect.
5 participants