Uploaded image for project: 'ZooKeeper'
  1. ZooKeeper
  2. ZOOKEEPER-236

SSL Support for Atomic Broadcast protocol

    Details

    • Type: New Feature
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: quorum, security, server
    • Labels:

      Description

      We should have the ability to use SSL to authenticate and encrypt the traffic between ZooKeeper servers. For the most part this is a very easy change. We would probably only want to support this for TCP based leader elections.

        Issue Links

          Activity

          Hide
          githubbot ASF GitHub Bot added a comment -

          GitHub user afine opened a pull request:

          https://github.com/apache/zookeeper/pull/184

          ZOOKEEPER-236: SSL Support for Atomic Broadcast protocol [DO NOT MERGE]

          This is a work in progress, I wanted to get some feedback from the community while I worked on this. Please do not merge yet. Tests, documentation, and some cleanup still coming.

          This is a first pass at ssl support for the zookeeper quorum. It supports encrypting both leader election and normal operation.

          Rolling upgrades are supported via port unification (`portUnification=true`). This should only be used while performing a rolling upgrade.

          Some open questions:

          Thanks,
          Abe

          You can merge this pull request into a Git repository by running:

          $ git pull https://github.com/afine/zookeeper ZOOKEEPER-236

          Alternatively you can review and apply these changes as the patch at:

          https://github.com/apache/zookeeper/pull/184.patch

          To close this pull request, make a commit to your master/trunk branch
          with (at least) the following in the commit message:

          This closes #184


          commit db33552046dea7e8e850945da4f18d18644d8ee5
          Author: Abraham Fine <afine@apache.org>
          Date: 2017-03-06T23:12:59Z

          ZOOKEEPER-236: SSL Support for Atomic Broadcast protocol


          Show
          githubbot ASF GitHub Bot added a comment - GitHub user afine opened a pull request: https://github.com/apache/zookeeper/pull/184 ZOOKEEPER-236 : SSL Support for Atomic Broadcast protocol [DO NOT MERGE] This is a work in progress, I wanted to get some feedback from the community while I worked on this. Please do not merge yet. Tests, documentation, and some cleanup still coming. This is a first pass at ssl support for the zookeeper quorum. It supports encrypting both leader election and normal operation. Rolling upgrades are supported via port unification (`portUnification=true`). This should only be used while performing a rolling upgrade. Some open questions: Anyone have any ideas for better names for the configuration options (`sslQuorum` and `portUnification` currently). I am using the same configuration that points to the truststore/keystore used for server <-> client ssl. Do they need to be separate? Is port unification the correct approach for rolling upgrades? Is the impact from the use of `BufferedSocket`s during the upgrade acceptable? See: http://stackoverflow.com/questions/25637039/detecting-ssl-connection-and-converting-socket-to-sslsocket http://stackoverflow.com/questions/6559859/is-it-possible-to-change-plain-socket-to-sslsocket server < > client ssl is implemented with netty. I did not feel that rewriting our server < > server logic with netty was necessary given how easy ssl was to implement with standard java `SSLSocket`s. Any arguments to the contrary? Thanks, Abe You can merge this pull request into a Git repository by running: $ git pull https://github.com/afine/zookeeper ZOOKEEPER-236 Alternatively you can review and apply these changes as the patch at: https://github.com/apache/zookeeper/pull/184.patch To close this pull request, make a commit to your master/trunk branch with (at least) the following in the commit message: This closes #184 commit db33552046dea7e8e850945da4f18d18644d8ee5 Author: Abraham Fine <afine@apache.org> Date: 2017-03-06T23:12:59Z ZOOKEEPER-236 : SSL Support for Atomic Broadcast protocol
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,

          I have attempted to work on providing the same support last year for branch-3.5 and I have created the pull request for what I have so far for you to look at: https://github.com/apache/zookeeper/pull/185
          Readme: https://github.com/geek101/zookeeper/blob/branch-3.4/README_SSL.md

          I definitely did not consider upgrading non SSL cluster to an SSL one for ZAB and FLE, glad that you brought that up.

          I am attempting to combine these changes and Netty changes I have for Trunk. You can find them here:
          https://github.com/geek101/zookeeper/pull/6

          Let me know what you think.
          thanks
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, I have attempted to work on providing the same support last year for branch-3.5 and I have created the pull request for what I have so far for you to look at: https://github.com/apache/zookeeper/pull/185 Readme: https://github.com/geek101/zookeeper/blob/branch-3.4/README_SSL.md I definitely did not consider upgrading non SSL cluster to an SSL one for ZAB and FLE, glad that you brought that up. I am attempting to combine these changes and Netty changes I have for Trunk. You can find them here: https://github.com/geek101/zookeeper/pull/6 Let me know what you think. thanks Powell.
          Hide
          abrahamfine Abraham Fine added a comment -

          Hi Powell Molleti-

          Thanks for letting me know about the work that you have done. I apologize if I have missed it somewhere else in JIRA. Hopefully we can combine efforts here.

          I wanted to discuss about the way that certificates are being handled in your patch, which I think is a fundamental difference in our two approaches (which I think is based on different operational assumptions).

          Your patch, and please correct me if I am wrong, appears to use self signed certs on each node and a fingerprint (passed through the configuration system) as a mechanism of verification. This makes zookeeper self contained and easy to manage.

          My patch assumes certificates are likely not self signed and some public key (or possibly keys) are available in the trust store that would be able to authenticate all zk servers. I think this has the advantage of making it much more difficult for unauthorized servers to join a quorum as they would need to have access to the CA that was used to generate the keys in the truststore. In addition, I needed to make minimal changes to the config system.

          What do you think is the best path forward?

          Abe

          Show
          abrahamfine Abraham Fine added a comment - Hi Powell Molleti - Thanks for letting me know about the work that you have done. I apologize if I have missed it somewhere else in JIRA. Hopefully we can combine efforts here. I wanted to discuss about the way that certificates are being handled in your patch, which I think is a fundamental difference in our two approaches (which I think is based on different operational assumptions). Your patch, and please correct me if I am wrong, appears to use self signed certs on each node and a fingerprint (passed through the configuration system) as a mechanism of verification. This makes zookeeper self contained and easy to manage. My patch assumes certificates are likely not self signed and some public key (or possibly keys) are available in the trust store that would be able to authenticate all zk servers. I think this has the advantage of making it much more difficult for unauthorized servers to join a quorum as they would need to have access to the CA that was used to generate the keys in the truststore. In addition, I needed to make minimal changes to the config system. What do you think is the best path forward? Abe
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,

          I am more then happy to combine efforts and collaborate. I think the significant amount of work is testing which is what I am attempting to get it right in the Trunk patch which also has Netty support when compared to work on branch-3.5 (this patch).

          This patch has support for configuration of for Truststore. Hence if the admin provides a cert chain that contains the CA(s) or for that matter if the Truststore contains all the self signed certs it should also work. I did not do extensive testing for this in this branch (I did this for branch-3.4, hence I think it should work).

          Let me point you to the code and let me know if this can work or is what you are expecting:

          createSSLContext() calls always chain Trustmanagers via X509ChainedTrustManager class with Truststore manager first i.e ZKX509TrustManager class and dynamic trust verification via ZKPeerX509TrustManager class.

          Client side SSL socket creation:
          https://github.com/apache/zookeeper/pull/185/files#diff-74d86ae5e83698ad62aada32dcbe615eR53

          Quorum server side SSL socket creation:
          https://github.com/apache/zookeeper/pull/185/files#diff-eb0052bc7ed160b8dee226f2ed1bdad2R51

          I do think we need consensus here if there is need or want to support dynamic reconfiguration with SSL in a fault-tolerant way. By which I mean, if I understand correctly, both the operations of managing the certs (add/remove of certs) and reconfig() API to change members of a quorum have to be fault-tolerant. reconfig() is atomic hence no issue there. But then that leaves managing the certs for the Quorum which is a non trivial problem and there exists a solution I think that does need support in the guts of ZK. But I think we can simplify the orchestration of Zookeeper with SSL if it is SSL aware.

          CA seems like the best way and simple way to go till such a time arrises when the CA has to be changed. Also prevalent security considerations recommend revoking the cert of the Quorum member that is removed. Again how do we accomplish this in a fault-tolerant way. Hence there is some work still left to do in CA case.

          If we do not consider fault-tolerance for SSL config management or take a stance that ZK does not have to solve this for the admin then just providing a Truststore would suffice.

          Also regarding the path forward do you think we should aim for a Trunk patch or patch to 3.5?. Also should we split the effort two phases, first without Netty and then perhaps bring in Netty etc.

          Let me know if I got something wrong and what do you think about bringing in Netty support for Quorum communication.
          Cheers
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, I am more then happy to combine efforts and collaborate. I think the significant amount of work is testing which is what I am attempting to get it right in the Trunk patch which also has Netty support when compared to work on branch-3.5 (this patch). This patch has support for configuration of for Truststore. Hence if the admin provides a cert chain that contains the CA(s) or for that matter if the Truststore contains all the self signed certs it should also work. I did not do extensive testing for this in this branch (I did this for branch-3.4, hence I think it should work). Let me point you to the code and let me know if this can work or is what you are expecting: createSSLContext() calls always chain Trustmanagers via X509ChainedTrustManager class with Truststore manager first i.e ZKX509TrustManager class and dynamic trust verification via ZKPeerX509TrustManager class. Client side SSL socket creation: https://github.com/apache/zookeeper/pull/185/files#diff-74d86ae5e83698ad62aada32dcbe615eR53 Quorum server side SSL socket creation: https://github.com/apache/zookeeper/pull/185/files#diff-eb0052bc7ed160b8dee226f2ed1bdad2R51 I do think we need consensus here if there is need or want to support dynamic reconfiguration with SSL in a fault-tolerant way. By which I mean, if I understand correctly, both the operations of managing the certs (add/remove of certs) and reconfig() API to change members of a quorum have to be fault-tolerant. reconfig() is atomic hence no issue there. But then that leaves managing the certs for the Quorum which is a non trivial problem and there exists a solution I think that does need support in the guts of ZK. But I think we can simplify the orchestration of Zookeeper with SSL if it is SSL aware. CA seems like the best way and simple way to go till such a time arrises when the CA has to be changed. Also prevalent security considerations recommend revoking the cert of the Quorum member that is removed. Again how do we accomplish this in a fault-tolerant way. Hence there is some work still left to do in CA case. If we do not consider fault-tolerance for SSL config management or take a stance that ZK does not have to solve this for the admin then just providing a Truststore would suffice. Also regarding the path forward do you think we should aim for a Trunk patch or patch to 3.5?. Also should we split the effort two phases, first without Netty and then perhaps bring in Netty etc. Let me know if I got something wrong and what do you think about bringing in Netty support for Quorum communication. Cheers Powell.
          Hide
          abrahamfine Abraham Fine added a comment -

          Hi Powell Molleti-

          if I understand correctly, both the operations of managing the certs (add/remove of certs) and reconfig() API to change members of a quorum have to be fault-tolerant.

          Would you mind clarifying what you mean by "fault-tolerant" here? Can you give an example of how a fault would break my patch?

          CA seems like the best way and simple way to go till such a time arrises when the CA has to be changed.

          You are correct in that there is certainly additional complexity involved here. But these are "solved" problems that I don't feel we need to write a custom solution for.

          Also prevalent security considerations recommend revoking the cert of the Quorum member that is removed. Again how do we accomplish this in a fault-tolerant way. Hence there is some work still left to do in CA case.

          Good point. I forgot to include a note on this with my patch. I was thinking supporting OCSP (or even CRL's) would be a fine solution to that. What do you think?

          Also regarding the path forward do you think we should aim for a Trunk patch or patch to 3.5?

          I think we should aim for both.

          Let me know if I got something wrong and what do you think about bringing in Netty support for Quorum communication.

          What are your motivations for bringing in Netty?

          Thanks,
          Abe

          Show
          abrahamfine Abraham Fine added a comment - Hi Powell Molleti - if I understand correctly, both the operations of managing the certs (add/remove of certs) and reconfig() API to change members of a quorum have to be fault-tolerant. Would you mind clarifying what you mean by "fault-tolerant" here? Can you give an example of how a fault would break my patch? CA seems like the best way and simple way to go till such a time arrises when the CA has to be changed. You are correct in that there is certainly additional complexity involved here. But these are "solved" problems that I don't feel we need to write a custom solution for. Also prevalent security considerations recommend revoking the cert of the Quorum member that is removed. Again how do we accomplish this in a fault-tolerant way. Hence there is some work still left to do in CA case. Good point. I forgot to include a note on this with my patch. I was thinking supporting OCSP (or even CRL's) would be a fine solution to that. What do you think? Also regarding the path forward do you think we should aim for a Trunk patch or patch to 3.5? I think we should aim for both. Let me know if I got something wrong and what do you think about bringing in Netty support for Quorum communication. What are your motivations for bringing in Netty? Thanks, Abe
          Hide
          hanm Michael Han added a comment -

          Thanks Abe for driving this. I have some comments for your questions in pull request.

          I am using the same configuration that points to the truststore/keystore used for server <-> client ssl. Do they need to be separate?

          Separate configuration option provides better flexibility and is also consistent with SASL / Kerberos configurations for client-server and server-server. For example we have separate server login context for server credential in client-server and server-server cases (ZOOKEEPER-1045), the keytab might be the same but the configuration options are separate.

          Is port unification the correct approach for rolling upgrades?

          I think it might be helpful to do some scoping and decide if we want to support rolling upgrade - or supporting rolling upgrade in first release of this patch. When it comes to security usually customers are OK to do a cold restart. Not saying that we should not support rolling upgrade someday but you might also want consider the amount of work involved to support it (implementation, how to properly testing, etc.) and it might be easier if we do this in phases - unless it is trivial to implement and test rolling upgrade (I haven't looked this in much details.) or folks feel it is absolutely required to get rolling upgrade capability before shipping this feature.

          server logic with netty was necessary given how easy ssl was to implement with standard java `SSLSocket`s. Any arguments to the contrary?

          Today we don't use Netty for server-server chat so it seems no immediate needs to rely on Netty for this work. Though, issues like ZOOKEEPER-900 and ZOOKEEPER-901 might push towards the direction of using Netty so the FLE and server chats are none blocking, plus we have TLS on Netty for client-server secured communication, so for consistency we could choose to implement TLS on Netty for server-server as well. I am interesting to hear what others think about this.

          Show
          hanm Michael Han added a comment - Thanks Abe for driving this. I have some comments for your questions in pull request. I am using the same configuration that points to the truststore/keystore used for server <-> client ssl. Do they need to be separate? Separate configuration option provides better flexibility and is also consistent with SASL / Kerberos configurations for client-server and server-server. For example we have separate server login context for server credential in client-server and server-server cases ( ZOOKEEPER-1045 ), the keytab might be the same but the configuration options are separate. Is port unification the correct approach for rolling upgrades? I think it might be helpful to do some scoping and decide if we want to support rolling upgrade - or supporting rolling upgrade in first release of this patch. When it comes to security usually customers are OK to do a cold restart. Not saying that we should not support rolling upgrade someday but you might also want consider the amount of work involved to support it (implementation, how to properly testing, etc.) and it might be easier if we do this in phases - unless it is trivial to implement and test rolling upgrade (I haven't looked this in much details.) or folks feel it is absolutely required to get rolling upgrade capability before shipping this feature. server logic with netty was necessary given how easy ssl was to implement with standard java `SSLSocket`s. Any arguments to the contrary? Today we don't use Netty for server-server chat so it seems no immediate needs to rely on Netty for this work. Though, issues like ZOOKEEPER-900 and ZOOKEEPER-901 might push towards the direction of using Netty so the FLE and server chats are none blocking, plus we have TLS on Netty for client-server secured communication, so for consistency we could choose to implement TLS on Netty for server-server as well. I am interesting to hear what others think about this.
          Hide
          abrahamfine Abraham Fine added a comment -

          Michael Han-

          Separate configuration option provides better flexibility and is also consistent with SASL / Kerberos configurations for client-server and server-server.

          That makes sense to me. I'll update the patch. I'm also taking suggestions for what such an option should be named.

          unless it is trivial to implement and test rolling upgrade

          It was pretty trivial to implement and I imagine testing should not be too difficult either.

          Today we don't use Netty for server-server chat so it seems no immediate needs to rely on Netty for this work.

          This is how I feel as well. I'm sure we can pretty quickly come up with a list of deficiencies in the current design but I don't think there is anything severe enough at this moment to give us cause to rewrite right now.

          Show
          abrahamfine Abraham Fine added a comment - Michael Han - Separate configuration option provides better flexibility and is also consistent with SASL / Kerberos configurations for client-server and server-server. That makes sense to me. I'll update the patch. I'm also taking suggestions for what such an option should be named. unless it is trivial to implement and test rolling upgrade It was pretty trivial to implement and I imagine testing should not be too difficult either. Today we don't use Netty for server-server chat so it seems no immediate needs to rely on Netty for this work. This is how I feel as well. I'm sure we can pretty quickly come up with a list of deficiencies in the current design but I don't think there is anything severe enough at this moment to give us cause to rewrite right now.
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,

          if I understand correctly, both the operations of managing the certs (add/remove of certs) and reconfig() API to change members of quorum have to be fault-tolerant.

          Would you mind clarifying what you mean by "fault-tolerant" here? Can you give an example of how a fault would break my patch?

          Either it be CA(s) with CRL's or self signed list of certs what I am pointing to is that the way an admin manages this information should also support fault-tolerance. Not only it should be fault-tolerant but also should work nicely/easily with most probable next thing an admin would do i.e issue a reconfig() command, it could be an add/removing/modify quorum peer(s) configuration.

          It will be nice to provide a way to manage reconfiguration of quorum peers when SSL is enabled with the same weak assumptions that are necessary for reconfig() to work when SSL is not enabled.

          Providing a Truststore and asking admins to manage them on their own for the entire quorum will mean that this operation is not fault-tolerant i.e we are expecting them to first set all members of the quorum to a consistent SSL config state and then issue reconfig() command.

          It would seem that a set of quorum IP addresses dictate what the current configuration of connectivity is allowed and this has to be managed properly to ensure safety and extending this idea the set of SSL certs(be self signed or CA signed) also dictate the current configuration of connectivity. Hence if one considers the Pair<IP set, SSL set> as config and provide that to reconfig() API it should work. That is what is done for self signed certs in my patch and we should/could provide similar functionality for CA cert case.

          Hence there is no new problem to solve here, we piggy back on reconfig() API and provide a single API to manage this, we get fault-tolerance for this configuration and safety that reconfig() provides for free.

          Please consider the above comments and let me know what you think, I was not saying that your patch is breaking fault-tolerance instead what my comments pointed to is that we should provide fault-tolerance and safety for reconfiguration of SSL configuration be it self signed or CA based. There are use cases where CA cert based cluster deployment might not be possible hence it would be nice to see Zookeeper provide both possibilities but also maintain the ease of use and provide same guarantees that reconfig() does.

          This is how I feel as well. I'm sure we can pretty quickly come up with a list of deficiencies in the current design but I don't think there is anything severe enough at this moment to give us cause to rewrite right now.

          There are bugs like ZOOKEEPER-2164, ZOOKEEPER-1678 to consider along with ZOOKEEPER-901. Netty or NIO will work but considering SSL will mean Netty will make it easier to implement.

          Doing this in phases is better, getting SSL socket to work with reconfig() support is great first step. The Netty patch I have also gets this support only for FLE and not ZAB. I found it not so easier to abstract away the calls to socket(s) from ZAB code.

          Cheers
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, if I understand correctly, both the operations of managing the certs (add/remove of certs) and reconfig() API to change members of quorum have to be fault-tolerant. Would you mind clarifying what you mean by "fault-tolerant" here? Can you give an example of how a fault would break my patch? Either it be CA(s) with CRL's or self signed list of certs what I am pointing to is that the way an admin manages this information should also support fault-tolerance. Not only it should be fault-tolerant but also should work nicely/easily with most probable next thing an admin would do i.e issue a reconfig() command, it could be an add/removing/modify quorum peer(s) configuration. It will be nice to provide a way to manage reconfiguration of quorum peers when SSL is enabled with the same weak assumptions that are necessary for reconfig() to work when SSL is not enabled. Providing a Truststore and asking admins to manage them on their own for the entire quorum will mean that this operation is not fault-tolerant i.e we are expecting them to first set all members of the quorum to a consistent SSL config state and then issue reconfig() command. It would seem that a set of quorum IP addresses dictate what the current configuration of connectivity is allowed and this has to be managed properly to ensure safety and extending this idea the set of SSL certs(be self signed or CA signed) also dictate the current configuration of connectivity. Hence if one considers the Pair<IP set, SSL set> as config and provide that to reconfig() API it should work. That is what is done for self signed certs in my patch and we should/could provide similar functionality for CA cert case. Hence there is no new problem to solve here, we piggy back on reconfig() API and provide a single API to manage this, we get fault-tolerance for this configuration and safety that reconfig() provides for free. Please consider the above comments and let me know what you think, I was not saying that your patch is breaking fault-tolerance instead what my comments pointed to is that we should provide fault-tolerance and safety for reconfiguration of SSL configuration be it self signed or CA based. There are use cases where CA cert based cluster deployment might not be possible hence it would be nice to see Zookeeper provide both possibilities but also maintain the ease of use and provide same guarantees that reconfig() does. This is how I feel as well. I'm sure we can pretty quickly come up with a list of deficiencies in the current design but I don't think there is anything severe enough at this moment to give us cause to rewrite right now. There are bugs like ZOOKEEPER-2164 , ZOOKEEPER-1678 to consider along with ZOOKEEPER-901 . Netty or NIO will work but considering SSL will mean Netty will make it easier to implement. Doing this in phases is better, getting SSL socket to work with reconfig() support is great first step. The Netty patch I have also gets this support only for FLE and not ZAB. I found it not so easier to abstract away the calls to socket(s) from ZAB code. Cheers Powell.
          Hide
          phunt Patrick Hunt added a comment -

          In general I don't think we want to tie/require security through reconfig. Seems like a potentially useful feature, but most folks are pretty familiar with typically java/ssl configuration. Additionally what was originally proposed here is very consistent across the Hadoop 'ecosystem'. That said perhaps a separate jira for the reconfig feature, for those interested in that approach (although I don't know if we need two)

          Keep in mind also that some folks might want security w/o enabling reconfig - reconfig is off by default iirc in 3.5. Those that are not using reconfig might not want to enable it just to enable security.

          Show
          phunt Patrick Hunt added a comment - In general I don't think we want to tie/require security through reconfig. Seems like a potentially useful feature, but most folks are pretty familiar with typically java/ssl configuration. Additionally what was originally proposed here is very consistent across the Hadoop 'ecosystem'. That said perhaps a separate jira for the reconfig feature, for those interested in that approach (although I don't know if we need two) Keep in mind also that some folks might want security w/o enabling reconfig - reconfig is off by default iirc in 3.5. Those that are not using reconfig might not want to enable it just to enable security.
          Hide
          abrahamfine Abraham Fine added a comment -

          Powell Molleti-

          but also should work nicely/easily with most probable next thing an admin would do i.e issue a reconfig() command

          I agree that doing it through reconfig() does provide a more integrated user experience. But I am not sure that it is what an "admin" would expect as the rest of the hadoop ecosystem handles it the other way.

          Providing a Truststore and asking admins to manage them on their own for the entire quorum will mean that this operation is not fault-tolerant i.e we are expecting them to first set all members of the quorum to a consistent SSL config state and then issue reconfig() command.

          I'm not sure that requiring proper ssl configuration for nodes before they join a cluster is unreasonable to expect of an admin. I think this is a decision better left to the community.

          There are bugs like ZOOKEEPER-2164, ZOOKEEPER-1678 to consider along with ZOOKEEPER-901. Netty or NIO will work but considering SSL will mean Netty will make it easier to implement.

          I agree that there are some reasons to discuss using netty for server<->server but I think it is outside the scope of this JIRA.

          Doing this in phases is better,

          I agree. What do you think about Patrick Hunt's recommendation? Implement SSL in this JIRA in the old fashioned way (we could even backport to 3.4) here and open another JIRA for reconfig() support.

          Show
          abrahamfine Abraham Fine added a comment - Powell Molleti - but also should work nicely/easily with most probable next thing an admin would do i.e issue a reconfig() command I agree that doing it through reconfig() does provide a more integrated user experience. But I am not sure that it is what an "admin" would expect as the rest of the hadoop ecosystem handles it the other way. Providing a Truststore and asking admins to manage them on their own for the entire quorum will mean that this operation is not fault-tolerant i.e we are expecting them to first set all members of the quorum to a consistent SSL config state and then issue reconfig() command. I'm not sure that requiring proper ssl configuration for nodes before they join a cluster is unreasonable to expect of an admin. I think this is a decision better left to the community. There are bugs like ZOOKEEPER-2164 , ZOOKEEPER-1678 to consider along with ZOOKEEPER-901 . Netty or NIO will work but considering SSL will mean Netty will make it easier to implement. I agree that there are some reasons to discuss using netty for server<->server but I think it is outside the scope of this JIRA. Doing this in phases is better, I agree. What do you think about Patrick Hunt 's recommendation? Implement SSL in this JIRA in the old fashioned way (we could even backport to 3.4) here and open another JIRA for reconfig() support.
          Hide
          githubbot ASF GitHub Bot added a comment -

          GitHub user geek101 opened a pull request:

          https://github.com/apache/zookeeper/pull/188

          ZOOKEEPER-236: SSL support for ZAB and FLE [DO NOT MERGE]

          1. Zookeeper FLE & ZAB SSL

          Provides SSL for Leader Election and ZAB i.e ports 3888 and 2888.

          Goal of this patch is to build on top of SSL changes for [branch-3.4](https://github.com/geek101/zookeeper/blob/branch-3.4/README_SSL.md)

              1. Some details
                  1. Building

          ```
          git checkout branch-3.5-ssl-review5
          ant jar
          ```

          Args to enable SSL:
          ```
          -Dquorum.ssl.enabled="true"
          -Dquorum.ssl.keyStore.location="<Private key and signed cert, key store file>"
          -Dquorum.ssl.keyStore.password="<Password for the above>"
          -Dquorum.ssl.trustStore.location="<Root CA cert, key store file>"
          -Dquorum.ssl.trustStore.password="<Password for the above>"
          ```

          Example run command:
          ```
          java -Dquorum.ssl.enabled="true" -Dquorum.ssl.keyStore.location="node1.ks"
          -Dquorum.ssl.keyStore.password="CertPassword1" -Dquorum.ssl.trustStore.location="truststore.ks" -Dquorum.ssl.trustStore.password="StorePass" -cp zookeeper.jar:lib/* org.apache.zookeeper.server.quorum.QuorumPeerMain zoo1.cfg
          ```

          You can merge this pull request into a Git repository by running:

          $ git pull https://github.com/geek101/zookeeper branch-3.5-ssl-review6

          Alternatively you can review and apply these changes as the patch at:

          https://github.com/apache/zookeeper/pull/188.patch

          To close this pull request, make a commit to your master/trunk branch
          with (at least) the following in the commit message:

          This closes #188


          commit 2fe3e971839dfa9d3a5040990e50b5cb657502e3
          Author: Powell Molleti <powellm79@yahoo.com>
          Date: 2016-08-14T02:27:03Z

          SSL support for ZAB and FLE.

          Pass ZKConfig() around to make things work.
          Seperate SSL config for client and quorum
          Quorum servers will have different properties for
          SSL config, example:
          -Dquorum.ssl.enabled=true
          -Dquorum.ssl.keyStore.location=/root/zookeeper/ssl/testKeyStore.jks
          -Dquorum.ssl.keyStore.password=testpass
          -Dquorum.ssl.trustStore.location=/root/zookeeper/ssl/testTrustStore.jks
          -Dquorum.ssl.trustStore.password=testpass

          Enable each zookeeper node will be able to also allowed to be authenticated
          as a client using dynamic reconfig.

          Basic algorithm for various SSL connections.

          Client:
          1. Use the given truststore if available

          Server:
          1. Use the given truststore if available

          Quorum:
          1. Use the given truststore if available

          Add README to help explain what this code tries to accomplish.


          Show
          githubbot ASF GitHub Bot added a comment - GitHub user geek101 opened a pull request: https://github.com/apache/zookeeper/pull/188 ZOOKEEPER-236 : SSL support for ZAB and FLE [DO NOT MERGE] Zookeeper FLE & ZAB SSL Provides SSL for Leader Election and ZAB i.e ports 3888 and 2888. Goal of this patch is to build on top of SSL changes for [branch-3.4] ( https://github.com/geek101/zookeeper/blob/branch-3.4/README_SSL.md ) Some details [X509Util] ( https://github.com/geek101/zookeeper/blob/branch-3.5-ssl-review5/src/java/main/org/apache/zookeeper/common/X509Util.java ) becomes first class citizen and [QuorumX509Util] ( https://github.com/geek101/zookeeper/blob/branch-3.5-ssl-review5/src/java/main/org/apache/zookeeper/server/quorum/util/QuorumX509Util.java ) and [ServerX509Util] ( https://github.com/geek101/zookeeper/blob/branch-3.5-ssl-review5/src/java/main/org/apache/zookeeper/server/util/ServerX509Util.java ) extend it. [ZKConfig] ( https://github.com/geek101/zookeeper/blob/branch-3.5-ssl-review5/src/java/main/org/apache/zookeeper/common/ZKConfig.java ) becomes an abstract class and [QuorumSslConfig] ( https://github.com/geek101/zookeeper/blob/branch-3.5-ssl-review5/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java ) and [ZookeeperServerConfig] ( https://github.com/geek101/zookeeper/blob/branch-3.5-ssl-review5/src/java/main/org/apache/zookeeper/server/ZookeeperServerConfig.java ) implement it. Support for a Quorum peer to also be authenticated as a [ZK client] ( https://github.com/geek101/zookeeper/blob/branch-3.5-ssl-review5/src/java/main/org/apache/zookeeper/server/util/ServerX509Util.java#L62 ) (this will be removed if it breaks security and or is not needed) Building ``` git checkout branch-3.5-ssl-review5 ant jar ``` Args to enable SSL: ``` -Dquorum.ssl.enabled="true" -Dquorum.ssl.keyStore.location="<Private key and signed cert, key store file>" -Dquorum.ssl.keyStore.password="<Password for the above>" -Dquorum.ssl.trustStore.location="<Root CA cert, key store file>" -Dquorum.ssl.trustStore.password="<Password for the above>" ``` Example run command: ``` java -Dquorum.ssl.enabled="true" -Dquorum.ssl.keyStore.location="node1.ks" -Dquorum.ssl.keyStore.password="CertPassword1" -Dquorum.ssl.trustStore.location="truststore.ks" -Dquorum.ssl.trustStore.password="StorePass" -cp zookeeper.jar:lib/* org.apache.zookeeper.server.quorum.QuorumPeerMain zoo1.cfg ``` You can merge this pull request into a Git repository by running: $ git pull https://github.com/geek101/zookeeper branch-3.5-ssl-review6 Alternatively you can review and apply these changes as the patch at: https://github.com/apache/zookeeper/pull/188.patch To close this pull request, make a commit to your master/trunk branch with (at least) the following in the commit message: This closes #188 commit 2fe3e971839dfa9d3a5040990e50b5cb657502e3 Author: Powell Molleti <powellm79@yahoo.com> Date: 2016-08-14T02:27:03Z SSL support for ZAB and FLE. Pass ZKConfig() around to make things work. Seperate SSL config for client and quorum Quorum servers will have different properties for SSL config, example: -Dquorum.ssl.enabled=true -Dquorum.ssl.keyStore.location=/root/zookeeper/ssl/testKeyStore.jks -Dquorum.ssl.keyStore.password=testpass -Dquorum.ssl.trustStore.location=/root/zookeeper/ssl/testTrustStore.jks -Dquorum.ssl.trustStore.password=testpass Enable each zookeeper node will be able to also allowed to be authenticated as a client using dynamic reconfig. Basic algorithm for various SSL connections. Client: 1. Use the given truststore if available Server: 1. Use the given truststore if available Quorum: 1. Use the given truststore if available Add README to help explain what this code tries to accomplish.
          Hide
          geek101 Powell Molleti added a comment -

          Sounds good, I have created another pull request that does not have the changes for dynamic propagation of certificate fingerprints of the new quorum / new peer etc.

          https://github.com/apache/zookeeper/pull/188

          Porting this backward to branch-3.4 can be more work due to ZKConfig work done in branch-3.4. I have worked on branch-3.4 first for SSL patch and branch-3.5 work is based on this. Will try to rebase that and see how that goes.
          https://github.com/geek101/zookeeper/blob/branch-3.4/README_SSL.md

          thanks
          Powell.

          Show
          geek101 Powell Molleti added a comment - Sounds good, I have created another pull request that does not have the changes for dynamic propagation of certificate fingerprints of the new quorum / new peer etc. https://github.com/apache/zookeeper/pull/188 Porting this backward to branch-3.4 can be more work due to ZKConfig work done in branch-3.4. I have worked on branch-3.4 first for SSL patch and branch-3.5 work is based on this. Will try to rebase that and see how that goes. https://github.com/geek101/zookeeper/blob/branch-3.4/README_SSL.md thanks Powell.
          Hide
          abrahamfine Abraham Fine added a comment - - edited

          Hi Powell Molleti-

          I have been looking at your latest patch and it looks great. Just in case I am missing something, are there any major functionality differences between my original patch and pull request 188 (besides the documentation)?

          I'm also a little concerned with the use of the `ServerCfg` class. I understand how it wraps the hostString and InetSocketAddress but it creates a lot of plumbing changes that I do not think are necessary for the funcitonality we are trying to implement and complicate the diff.

          Do you think it would be easier to move forward with the patch I uploaded originally and put your reconfig changes on top of that (as I think the configuration refactoring you did is more appropriate there anyway)?

          Thanks,
          Abe

          Show
          abrahamfine Abraham Fine added a comment - - edited Hi Powell Molleti - I have been looking at your latest patch and it looks great. Just in case I am missing something, are there any major functionality differences between my original patch and pull request 188 (besides the documentation)? I'm also a little concerned with the use of the `ServerCfg` class. I understand how it wraps the hostString and InetSocketAddress but it creates a lot of plumbing changes that I do not think are necessary for the funcitonality we are trying to implement and complicate the diff. Do you think it would be easier to move forward with the patch I uploaded originally and put your reconfig changes on top of that (as I think the configuration refactoring you did is more appropriate there anyway)? Thanks, Abe
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 6 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 2 new Findbugs (version 3.0.1) warnings.

          -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings).

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//testReport/
          Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 6 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 2 new Findbugs (version 3.0.1) warnings. -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings). -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//testReport/ Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/399//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          -1 @author. The patch appears to contain 2 @author tags which the Zookeeper community has agreed to not allow in code contributions.

          +1 tests included. The patch appears to include 27 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings).

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//testReport/
          Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build -1 @author. The patch appears to contain 2 @author tags which the Zookeeper community has agreed to not allow in code contributions. +1 tests included. The patch appears to include 27 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings). -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//testReport/ Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/401//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          -1 @author. The patch appears to contain 2 @author tags which the Zookeeper community has agreed to not allow in code contributions.

          +1 tests included. The patch appears to include 27 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings).

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//testReport/
          Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build -1 @author. The patch appears to contain 2 @author tags which the Zookeeper community has agreed to not allow in code contributions. +1 tests included. The patch appears to include 27 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings). -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//testReport/ Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/402//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 30 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings).

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//testReport/
          Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 30 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings). -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//testReport/ Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/403//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 49 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings).

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//testReport/
          Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 49 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings). +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//testReport/ Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/404//console This message is automatically generated.
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,

          Thanks for looking at the patch and feedback.

          I'm also a little concerned with the use of the `ServerCfg` class. I understand how it wraps the hostString and InetSocketAddress but it creates a lot of plumbing changes that I do not think are necessary for the functionality we are trying to implement and complicate the diff.

          Noted, I was using that class to band together hostString, InetSocketAddress and Certificate fingerprint. I have removed it and eliminated the impact of it files. See my comments later in the message on the need for hostString.

          Do you think it would be easier to move forward with the patch I uploaded originally and put your reconfig changes on top of that (as I think the configuration refactoring you did is more appropriate there anyway)?

          I have no issues if you want to commit your changes. Here are few of the things I think are needed.

          • Need for separate SSL config for client to server and quorum peer to quorum peer. Changes to X509Util and ZKConfig are for this.
          • Need for Hostname verification and CRL lists at-least for quorum peer to quorum peer SSL would mean that we will need X509ExtendedTrustManager hence the reason for ZKX509TrustManager class and its helpers.
          • Hostname verification will need hostname to be supplied at SSLEngine creation time if reverse DNS lookup is not desired. I do not have this either.

          Some more risks from my patch:

          • Modifying the code paths for client to server ssl. I have ensured that these tests work. More testing might be needed.
          • Risk of using X509ExtendedTrustManager, did we miss any checks that are done by the default Trustmanager that was used before.
          • Lack of upgrade path, I like the idea of new nodes optionally supporting sockets that support both plain and ssl connections.

          To make my patch more complete I need to continue to write more unit tests specially testing quorum bootstrap with and without SSL and tests for ZKX509TrustManager, have already build tools for that.I have ported some from Netty Trunk work and I could port more to get those going.

          thanks
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, Thanks for looking at the patch and feedback. I'm also a little concerned with the use of the `ServerCfg` class. I understand how it wraps the hostString and InetSocketAddress but it creates a lot of plumbing changes that I do not think are necessary for the functionality we are trying to implement and complicate the diff. Noted, I was using that class to band together hostString, InetSocketAddress and Certificate fingerprint. I have removed it and eliminated the impact of it files. See my comments later in the message on the need for hostString. Do you think it would be easier to move forward with the patch I uploaded originally and put your reconfig changes on top of that (as I think the configuration refactoring you did is more appropriate there anyway)? I have no issues if you want to commit your changes. Here are few of the things I think are needed. Need for separate SSL config for client to server and quorum peer to quorum peer. Changes to X509Util and ZKConfig are for this. Need for Hostname verification and CRL lists at-least for quorum peer to quorum peer SSL would mean that we will need X509ExtendedTrustManager hence the reason for ZKX509TrustManager class and its helpers. Hostname verification will need hostname to be supplied at SSLEngine creation time if reverse DNS lookup is not desired. I do not have this either. Some more risks from my patch: Modifying the code paths for client to server ssl. I have ensured that these tests work. More testing might be needed. Risk of using X509ExtendedTrustManager, did we miss any checks that are done by the default Trustmanager that was used before. Lack of upgrade path, I like the idea of new nodes optionally supporting sockets that support both plain and ssl connections. To make my patch more complete I need to continue to write more unit tests specially testing quorum bootstrap with and without SSL and tests for ZKX509TrustManager, have already build tools for that.I have ported some from Netty Trunk work and I could port more to get those going. thanks Powell.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 54 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings).

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//testReport/
          Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 54 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings). -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//testReport/ Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/406//console This message is automatically generated.
          Hide
          abrahamfine Abraham Fine added a comment -

          Hi Powell Molleti-

          Need for separate SSL config for client to server and quorum peer to quorum peer. Changes to X509Util and ZKConfig are for this.

          Totally agree.

          Need for Hostname verification and CRL lists at-least for quorum peer to quorum peer SSL would mean that we will need X509ExtendedTrustManager hence the reason for ZKX509TrustManager class and its helpers.

          I'm not sure I agree with this one. I uploaded a new commit on my pr showing that hostname verification can be implemented outside of the trust manager (since hostname verification is not part of ssl). I think that is easier this way because we often do not know which zookeeper sid is connecting until some information is transferred. In addition, I thought CRL is implemented completely outside of application logic (see: http://stackoverflow.com/questions/8506661/check-x509-certificate-revocation-status-in-spring-security-before-authenticatin/8507905#8507905), I could be very wrong though. Still need to test this.

          Hostname verification will need hostname to be supplied at SSLEngine creation time if reverse DNS lookup is not desired. I do not have this either.

          For client <-> server I think this is true. We could move this into another patch as this is outside that would be outside the scope of the JIRA.

          Please take a look at my latest changes and let me know what you think. I still have not implemented separating the client and server configurations. That should be coming soon.

          Thanks,
          Abe

          Show
          abrahamfine Abraham Fine added a comment - Hi Powell Molleti - Need for separate SSL config for client to server and quorum peer to quorum peer. Changes to X509Util and ZKConfig are for this. Totally agree. Need for Hostname verification and CRL lists at-least for quorum peer to quorum peer SSL would mean that we will need X509ExtendedTrustManager hence the reason for ZKX509TrustManager class and its helpers. I'm not sure I agree with this one. I uploaded a new commit on my pr showing that hostname verification can be implemented outside of the trust manager (since hostname verification is not part of ssl). I think that is easier this way because we often do not know which zookeeper sid is connecting until some information is transferred. In addition, I thought CRL is implemented completely outside of application logic (see: http://stackoverflow.com/questions/8506661/check-x509-certificate-revocation-status-in-spring-security-before-authenticatin/8507905#8507905 ), I could be very wrong though. Still need to test this. Hostname verification will need hostname to be supplied at SSLEngine creation time if reverse DNS lookup is not desired. I do not have this either. For client <-> server I think this is true. We could move this into another patch as this is outside that would be outside the scope of the JIRA. Please take a look at my latest changes and let me know what you think. I still have not implemented separating the client and server configurations. That should be coming soon. Thanks, Abe
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 54 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings).

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//testReport/
          Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 54 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. -1 release audit. The applied patch generated 2 release audit warnings (more than the trunk's current 0 warnings). -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//testReport/ Release audit warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//artifact/trunk/patchprocess/patchReleaseAuditProblems.txt Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/411//console This message is automatically generated.
          Hide
          abrahamfine Abraham Fine added a comment -

          Powell Molleti my latest commit includes certificate revocation support. let me know if it behaves the way you would like it to.

          Abe

          Show
          abrahamfine Abraham Fine added a comment - Powell Molleti my latest commit includes certificate revocation support. let me know if it behaves the way you would like it to. Abe
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,

          Few comments about the changes:

          One way to go is to minimize changes to Leader.java, Follower.java, Learner.java etc. This will help anyone who wants to abstract away sockets from that code. Let me know what you think.

          Regarding host verification one other way to go is to follow this:
          X509ExtendedTrustManager mentions about where to plugin host verification , it specifically quotes:

           Besides TLS 1.2 support, the X509ExtendedTrustManager class also support algorithm constraints and SSL layer hostname verification. For JSSE providers and trust manager implementations, the X509ExtendedTrustManager class is highly recommended rather than the legacy X509TrustManager interface. 

          It is possible to argue that thought TLS does not perform host verification I do not think it mentions this should be not be done at cert verification time nor should we allow exchange of application bits when the certificate is not what we expected it to be.

          It is possible to minimize risk by extending X509ExtendedTrustManager and calling default TrustManager's API first and then performing extra checks. Something like a composite X509TrustManager. This will also help others add to the list of checks in one place and all of this could be conditional based on options passed.

          I am fine with either OSCP or CRL verification as long as the admin is aware of how this affects the latency of session setup and reliability of the Quorum since they are all perhaps talking to one entity(hopefully not) for this to work.

          Also I noticed the ZOOKEEPER-2184 could be addressed by some of my plumbing changes that pass the configured hostname around along with the resolved ip address. Hence avoiding reverse DNS lookups when hostname is provided at config time etc.

          It is best to combine the PRs into one so we can collaborate and increase the velocity. We should agree on few things, I would like to suggest that we use X509ExtendedTrustManager and also minimize the amount of changes and maintain compatibility for client - server, I would like to keep the BC helper code in unit tests and the unit tests. I lean towards just changing Socket() calls to something else but not adding any more code to Leader.java, Follower.java, Learner.java etc.

          Let me know what you prefer.
          thanks
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, Few comments about the changes: One way to go is to minimize changes to Leader.java, Follower.java, Learner.java etc. This will help anyone who wants to abstract away sockets from that code. Let me know what you think. Regarding host verification one other way to go is to follow this: X509ExtendedTrustManager mentions about where to plugin host verification , it specifically quotes: Besides TLS 1.2 support, the X509ExtendedTrustManager class also support algorithm constraints and SSL layer hostname verification. For JSSE providers and trust manager implementations, the X509ExtendedTrustManager class is highly recommended rather than the legacy X509TrustManager interface. It is possible to argue that thought TLS does not perform host verification I do not think it mentions this should be not be done at cert verification time nor should we allow exchange of application bits when the certificate is not what we expected it to be. It is possible to minimize risk by extending X509ExtendedTrustManager and calling default TrustManager's API first and then performing extra checks. Something like a composite X509TrustManager. This will also help others add to the list of checks in one place and all of this could be conditional based on options passed. I am fine with either OSCP or CRL verification as long as the admin is aware of how this affects the latency of session setup and reliability of the Quorum since they are all perhaps talking to one entity(hopefully not) for this to work. Also I noticed the ZOOKEEPER-2184 could be addressed by some of my plumbing changes that pass the configured hostname around along with the resolved ip address. Hence avoiding reverse DNS lookups when hostname is provided at config time etc. It is best to combine the PRs into one so we can collaborate and increase the velocity. We should agree on few things, I would like to suggest that we use X509ExtendedTrustManager and also minimize the amount of changes and maintain compatibility for client - server, I would like to keep the BC helper code in unit tests and the unit tests. I lean towards just changing Socket() calls to something else but not adding any more code to Leader.java, Follower.java, Learner.java etc. Let me know what you prefer. thanks Powell.
          Hide
          abrahamfine Abraham Fine added a comment - - edited

          Powell Molleti-

          One way to go is to minimize changes to Leader.java, Follower.java, Learner.java etc

          I agree. That will make understanding those classes much easier

          I do not think it mentions this should be not be done at cert verification time nor should we allow exchange of application bits when the certificate is not what we expected it to be.

          So the issue is more zookeeper specific I think. Imagine the case where, and I know this is very contrived but I think the principal is valid, we have 3 zk servers all running on the same host with different ports. We have 3 dns records pointing to this machine with different names, say zk1, zk2, and zk3. Each zkX has a certificate with the zkX common name. Our zookeeper configuration identifies these servers by the correct name server.1=zk1... When one of these servers connects to the server socket on the other I do not think it is possible for the "server" to tell which zkX connected until the sid is read from the socket unless we want to start doing reverse dns lookups. I would rather just use the hostname we already know about. That is why I think we cannot do hostname verification in the trust manager. Or you could argue that we only need hostname verification for the "client", but I would rather have it both ways. Please let me know if I am missing something.

          I am fine with either OSCP or CRL verification as long as the admin is aware of how this affects the latency of session setup and reliability of the Quorum since they are all perhaps talking to one entity(hopefully not) for this to work.

          I agree. We need to document this drawbacks clearly.

          Also I noticed the ZOOKEEPER-2184 could be addressed by some of my plumbing changes that pass the configured hostname around along with the resolved ip address.

          There is an active PR for this so I don't think we need to address it in this patch.

          It is best to combine the PRs into one so we can collaborate and increase the velocity.

          I agree.

          I would like to suggest that we use X509ExtendedTrustManager

          If the concerns I listed above with hostname verification can be addressed I would be open to this.

          I would like to keep the BC helper code

          What do you mean by bc helper code?

          I lean towards just changing Socket() calls to something else but not adding any more code to Leader.java, Follower.java, Learner.java etc.

          Sounds like a good idea. Once we get the implementation details flushed out there will be plenty of refactoring to clean this stuff up.

          Thanks,
          Abe

          Show
          abrahamfine Abraham Fine added a comment - - edited Powell Molleti - One way to go is to minimize changes to Leader.java, Follower.java, Learner.java etc I agree. That will make understanding those classes much easier I do not think it mentions this should be not be done at cert verification time nor should we allow exchange of application bits when the certificate is not what we expected it to be. So the issue is more zookeeper specific I think. Imagine the case where, and I know this is very contrived but I think the principal is valid, we have 3 zk servers all running on the same host with different ports. We have 3 dns records pointing to this machine with different names, say zk1, zk2, and zk3. Each zkX has a certificate with the zkX common name. Our zookeeper configuration identifies these servers by the correct name server.1=zk1... When one of these servers connects to the server socket on the other I do not think it is possible for the "server" to tell which zkX connected until the sid is read from the socket unless we want to start doing reverse dns lookups. I would rather just use the hostname we already know about. That is why I think we cannot do hostname verification in the trust manager. Or you could argue that we only need hostname verification for the "client", but I would rather have it both ways. Please let me know if I am missing something. I am fine with either OSCP or CRL verification as long as the admin is aware of how this affects the latency of session setup and reliability of the Quorum since they are all perhaps talking to one entity(hopefully not) for this to work. I agree. We need to document this drawbacks clearly. Also I noticed the ZOOKEEPER-2184 could be addressed by some of my plumbing changes that pass the configured hostname around along with the resolved ip address. There is an active PR for this so I don't think we need to address it in this patch. It is best to combine the PRs into one so we can collaborate and increase the velocity. I agree. I would like to suggest that we use X509ExtendedTrustManager If the concerns I listed above with hostname verification can be addressed I would be open to this. I would like to keep the BC helper code What do you mean by bc helper code? I lean towards just changing Socket() calls to something else but not adding any more code to Leader.java, Follower.java, Learner.java etc. Sounds like a good idea. Once we get the implementation details flushed out there will be plenty of refactoring to clean this stuff up. Thanks, Abe
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,

          I do not think it mentions this should be not be done at cert verification time nor should we allow exchange of application bits when the certificate is not what we expected it to be.

          So the issue is more zookeeper specific I think. Imagine the case where, and I know this is very contrived but I think the principal is valid, we have 3 zk servers all running on the same host with different ports. We have 3 dns records pointing to this machine with different names, say zk1, zk2, and zk3. Each zkX has a certificate with the zkX common name. Our zookeeper configuration identifies these servers by the correct name server.1=zk1... When one of these servers connects to the server socket on the other I do not think it is possible for the "server" to tell which zkX connected until the sid is read from the socket unless we want to start doing reverse dns lookups. I would rather just use the hostname we already know about. That is why I think we cannot do hostname verification in the trust manager. Or you could argue that we only need hostname verification for the "client", but I would rather have it both ways. Please let me know if I am missing something.

          If multiple servers have certs with the same subjectAltName entry of type dNSName and that is indeed is how the CA signed them then it should be ok from TLS perspective, since we trust the CA, we trust any server signed by the CA and we match the hostname(i.e we trust DNS lookup or we trust the config provided). We do not have to verify the specific host since it is sufficient to verify that it is one of the valid hosts, this is secure.

          Take a case where if someone can subvert the CA get signed by it for the same domain and subvert DNS then they might as well try few sids starting from zero before ZK lets the server connect.

          Hostname verification does not apply to self-signed certs. I am quite skeptical about the need for hostname verification in private CA (enterprise setting) too. We should probably have it off by default and let the admin turn it on.

          I would like to keep the BC helper code

          What do you mean by bc helper code?

          BC is bouncy castle.

          Also wanted to ask you if we could make the all sockets BufferedSocket by default rather then making that conditional on port unification configuration.

          I need to restore backward compatibility to X509Util, I removed the public statics that were marked deprecated, probably cannot do that just yet?.

          thanks
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, I do not think it mentions this should be not be done at cert verification time nor should we allow exchange of application bits when the certificate is not what we expected it to be. So the issue is more zookeeper specific I think. Imagine the case where, and I know this is very contrived but I think the principal is valid, we have 3 zk servers all running on the same host with different ports. We have 3 dns records pointing to this machine with different names, say zk1, zk2, and zk3. Each zkX has a certificate with the zkX common name. Our zookeeper configuration identifies these servers by the correct name server.1=zk1... When one of these servers connects to the server socket on the other I do not think it is possible for the "server" to tell which zkX connected until the sid is read from the socket unless we want to start doing reverse dns lookups. I would rather just use the hostname we already know about. That is why I think we cannot do hostname verification in the trust manager. Or you could argue that we only need hostname verification for the "client", but I would rather have it both ways. Please let me know if I am missing something. If multiple servers have certs with the same subjectAltName entry of type dNSName and that is indeed is how the CA signed them then it should be ok from TLS perspective, since we trust the CA, we trust any server signed by the CA and we match the hostname(i.e we trust DNS lookup or we trust the config provided). We do not have to verify the specific host since it is sufficient to verify that it is one of the valid hosts, this is secure. Take a case where if someone can subvert the CA get signed by it for the same domain and subvert DNS then they might as well try few sids starting from zero before ZK lets the server connect. Hostname verification does not apply to self-signed certs. I am quite skeptical about the need for hostname verification in private CA (enterprise setting) too. We should probably have it off by default and let the admin turn it on. I would like to keep the BC helper code What do you mean by bc helper code? BC is bouncy castle. Also wanted to ask you if we could make the all sockets BufferedSocket by default rather then making that conditional on port unification configuration. I need to restore backward compatibility to X509Util, I removed the public statics that were marked deprecated, probably cannot do that just yet?. thanks Powell.
          Hide
          abrahamfine Abraham Fine added a comment -

          Hi Powell Molleti-

          If multiple servers have certs with the same subjectAltName entry of type dNSName and that is indeed is how the CA signed them then it should be ok from TLS perspective

          I agree that at least one of the alt_names needs to match the host from configuration (not all of them).

          Take a case where if someone can subvert the CA get signed by it for the same domain and subvert DNS then they might as well try few sids starting from zero before ZK lets the server connect.

          If the CA is subverted (certificates are being issued for servers for domains not under their control) and the name service is subverted, hostname verification of any kind can't work. Either the CA or the NS (dns or the zk config) needs to have integrity. But I think you raise an interesting issue where a client can just try multiple id's when connecting to another server until it matches the one on its (lets assume stolen) certificate. I did not consider that. Let me dig into this a little deeper and see if I can get a better idea of what is going on.

          We should probably have it off by default and let the admin turn it on.

          The latest patch has this behavior.

          Also wanted to ask you if we could make the all sockets BufferedSocket by default rather then making that conditional on port unification configuration.

          Why would we want to do this? I think there is a small performance hit involved.

          Show
          abrahamfine Abraham Fine added a comment - Hi Powell Molleti - If multiple servers have certs with the same subjectAltName entry of type dNSName and that is indeed is how the CA signed them then it should be ok from TLS perspective I agree that at least one of the alt_names needs to match the host from configuration (not all of them). Take a case where if someone can subvert the CA get signed by it for the same domain and subvert DNS then they might as well try few sids starting from zero before ZK lets the server connect. If the CA is subverted (certificates are being issued for servers for domains not under their control) and the name service is subverted, hostname verification of any kind can't work. Either the CA or the NS (dns or the zk config) needs to have integrity. But I think you raise an interesting issue where a client can just try multiple id's when connecting to another server until it matches the one on its (lets assume stolen) certificate. I did not consider that. Let me dig into this a little deeper and see if I can get a better idea of what is going on. We should probably have it off by default and let the admin turn it on. The latest patch has this behavior. Also wanted to ask you if we could make the all sockets BufferedSocket by default rather then making that conditional on port unification configuration. Why would we want to do this? I think there is a small performance hit involved.
          Hide
          abrahamfine Abraham Fine added a comment - - edited

          Hi Powell Molleti-

          Regarding host verification one other way to go is to follow this: X509ExtendedTrustManager mentions about where to plugin host verification , it specifically quotes:

          Let me know if this is what you had in mind. We do not need to subclass `X509ExtendedTrustManager` ourselves to get this to work, since the `X509TrustManagerImpl` object generated by the PKIX trustmanager factory (and x509 as well I think) extends `X509ExtendedTrustManager` already. If endpoint verification is set on the sslParameters of the sslSocket we get endpoint verification for free in `X509ExtendedTrustManager`. The issue is that we are limited to the built in implementations of endpoint verification but I think the "https" algorithm is sufficient for our use case.

          I implemented this here: https://github.com/apache/zookeeper/pull/184/commits/bebe09660f652f243e746905460ecbdffe2d155e

          Show
          abrahamfine Abraham Fine added a comment - - edited Hi Powell Molleti - Regarding host verification one other way to go is to follow this: X509ExtendedTrustManager mentions about where to plugin host verification , it specifically quotes: Let me know if this is what you had in mind. We do not need to subclass `X509ExtendedTrustManager` ourselves to get this to work, since the `X509TrustManagerImpl` object generated by the PKIX trustmanager factory (and x509 as well I think) extends `X509ExtendedTrustManager` already. If endpoint verification is set on the sslParameters of the sslSocket we get endpoint verification for free in `X509ExtendedTrustManager`. The issue is that we are limited to the built in implementations of endpoint verification but I think the "https" algorithm is sufficient for our use case. I implemented this here: https://github.com/apache/zookeeper/pull/184/commits/bebe09660f652f243e746905460ecbdffe2d155e
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,

          Not sure that will work, I think SSL engine will need the the hostname set for this to work. Since server cannot know which client will connect it cannot use this mechanism I believe. This was meant for client side only.

          thanks
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, Not sure that will work, I think SSL engine will need the the hostname set for this to work. Since server cannot know which client will connect it cannot use this mechanism I believe. This was meant for client side only. thanks Powell.
          Hide
          abrahamfine Abraham Fine added a comment -

          Powell Molleti-

          You are correct. Apparently java doesn't do a reverse dns lookup by default. I had the IP address to be included as a subjectAltName of the client's certificate, so I didn't notice this failure. So I pushed an update with a subclass to X509ExtendedTrustManager:

                      for (final TrustManager tm : tmf.getTrustManagers()) {
                          if (tm instanceof X509TrustManager) {
                              return new X509ExtendedTrustManager() {
                                  HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);
          
                                  @Override
                                  public X509Certificate[] getAcceptedIssuers() {
                                      return ((X509ExtendedTrustManager) tm).getAcceptedIssuers();
                                  }
          
                                  @Override
                                  public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
                                      hostnameChecker.match(socket.getInetAddress().getHostName(), x509Certificates[0]);
                                      ((X509ExtendedTrustManager) tm).checkClientTrusted(x509Certificates, s, socket);
                                  }
          
                                  @Override
                                  public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
                                      hostnameChecker.match(((SSLSocket) socket).getHandshakeSession().getPeerHost(), x509Certificates[0]);
                                      ((X509ExtendedTrustManager) tm).checkServerTrusted(x509Certificates, s, socket);
                                  }
          

          We do a reverse dns lookup when a client is connecting to the server. This works. The primary issue I see is that import sun.security.util.HostnameChecker is proprietary and throws a compile warning.

              [javac] /Users/abefine/cloudera_code/zookeeper/src/java/main/org/apache/zookeeper/common/X509Util.java:26: warning: HostnameChecker is internal proprietary API and may be removed in a future release
              [javac] import sun.security.util.HostnameChecker;
          

          I'm not sure if it is the preference of the community to copy the code contained in HostnameChecker, add a dependency with similar functionality, or leave it as is. The issue is described clearly here https://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/:

          To make matters worse, the check is not trivial (consider SAN and wildcard matching) and is implemented in sun.security.util.HostnameChecker (a Sun internal proprietary API). This leaves the developer in the position of either depending on an internal API or finding/copying/creating another implementation of this functionality. For the examples in this article, I have opted for the first option.

          Thanks,
          Abe

          Show
          abrahamfine Abraham Fine added a comment - Powell Molleti - You are correct. Apparently java doesn't do a reverse dns lookup by default. I had the IP address to be included as a subjectAltName of the client's certificate, so I didn't notice this failure. So I pushed an update with a subclass to X509ExtendedTrustManager : for ( final TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509TrustManager) { return new X509ExtendedTrustManager() { HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS); @Override public X509Certificate[] getAcceptedIssuers() { return ((X509ExtendedTrustManager) tm).getAcceptedIssuers(); } @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { hostnameChecker.match(socket.getInetAddress().getHostName(), x509Certificates[0]); ((X509ExtendedTrustManager) tm).checkClientTrusted(x509Certificates, s, socket); } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { hostnameChecker.match(((SSLSocket) socket).getHandshakeSession().getPeerHost(), x509Certificates[0]); ((X509ExtendedTrustManager) tm).checkServerTrusted(x509Certificates, s, socket); } We do a reverse dns lookup when a client is connecting to the server. This works. The primary issue I see is that import sun.security.util.HostnameChecker is proprietary and throws a compile warning. [javac] /Users/abefine/cloudera_code/zookeeper/src/java/main/org/apache/zookeeper/common/X509Util.java:26: warning: HostnameChecker is internal proprietary API and may be removed in a future release [javac] import sun.security.util.HostnameChecker; I'm not sure if it is the preference of the community to copy the code contained in HostnameChecker, add a dependency with similar functionality, or leave it as is. The issue is described clearly here https://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/: To make matters worse, the check is not trivial (consider SAN and wildcard matching) and is implemented in sun.security.util.HostnameChecker (a Sun internal proprietary API). This leaves the developer in the position of either depending on an internal API or finding/copying/creating another implementation of this functionality. For the examples in this article, I have opted for the first option. Thanks, Abe
          Hide
          abrahamfine Abraham Fine added a comment -

          I found that org.apache.httpcomponents.httpclient has org.apache.http.conn.ssl.DefaultHostnameVerifier which should serve our purposes well.

          Show
          abrahamfine Abraham Fine added a comment - I found that org.apache.httpcomponents.httpclient has org.apache.http.conn.ssl.DefaultHostnameVerifier which should serve our purposes well.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user geek101 commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r108835534

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str
          tmf.init(new CertPathTrustManagerParameters(pbParams));

          for (final TrustManager tm : tmf.getTrustManagers()) {

          • if (tm instanceof X509TrustManager) {
            + if (tm instanceof X509ExtendedTrustManager) {
            return new X509ExtendedTrustManager() {
          • HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);
            + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm;
            + HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();

          @Override
          public X509Certificate[] getAcceptedIssuers()

          { - return ((X509ExtendedTrustManager) tm).getAcceptedIssuers(); + return x509ExtendedTrustManager.getAcceptedIssuers(); }

          @Override

          • public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
          • hostnameChecker.match(socket.getInetAddress().getHostName(), x509Certificates[0]);
          • ((X509ExtendedTrustManager) tm).checkClientTrusted(x509Certificates, s, socket);
            + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
            + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
            + hostnameVerifier.verify(socket.getInetAddress().getHostName(), ((SSLSocket) socket).getSession());
              • End diff –

          Will the hostname be available on the server side to verify?. How will the server know who this client is, one way is to implement a special HostnameVerifier that can be provide a set of peer hostnames or refer back to QuorumPeer to verify using the QuorumVerifier?.

          Show
          githubbot ASF GitHub Bot added a comment - Github user geek101 commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r108835534 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str tmf.init(new CertPathTrustManagerParameters(pbParams)); for (final TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509TrustManager) { + if (tm instanceof X509ExtendedTrustManager) { return new X509ExtendedTrustManager() { HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS); + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm; + HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); @Override public X509Certificate[] getAcceptedIssuers() { - return ((X509ExtendedTrustManager) tm).getAcceptedIssuers(); + return x509ExtendedTrustManager.getAcceptedIssuers(); } @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { hostnameChecker.match(socket.getInetAddress().getHostName(), x509Certificates [0] ); ((X509ExtendedTrustManager) tm).checkClientTrusted(x509Certificates, s, socket); + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + hostnameVerifier.verify(socket.getInetAddress().getHostName(), ((SSLSocket) socket).getSession()); End diff – Will the hostname be available on the server side to verify?. How will the server know who this client is, one way is to implement a special HostnameVerifier that can be provide a set of peer hostnames or refer back to QuorumPeer to verify using the QuorumVerifier?.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user geek101 commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r108835547

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str
          tmf.init(new CertPathTrustManagerParameters(pbParams));

          for (final TrustManager tm : tmf.getTrustManagers()) {

          • if (tm instanceof X509TrustManager) {
            + if (tm instanceof X509ExtendedTrustManager) {
            return new X509ExtendedTrustManager() {
          • HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);
            + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm;
              • End diff –

          Do you think extending X509ExtendedTrustManager outside will help us test it better?.

          Show
          githubbot ASF GitHub Bot added a comment - Github user geek101 commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r108835547 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str tmf.init(new CertPathTrustManagerParameters(pbParams)); for (final TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509TrustManager) { + if (tm instanceof X509ExtendedTrustManager) { return new X509ExtendedTrustManager() { HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS); + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm; End diff – Do you think extending X509ExtendedTrustManager outside will help us test it better?.
          Hide
          geek101 Powell Molleti added a comment -

          Hi Abe,
          I have added my comments on PR directly. We could table the hostname verification for next iteration of SSL support to move forward if required. For server to server SSL I think this is useful but will need help from QuorumPeer. Also for server to client SSL this is most likely not used. Performing a reverse DNS lookup to verify will mean that there is implicit trust for this, if customers provide just hostnames(or IPs) then that is the basis for trust and DNS reverse lookup might not be.

          Let me know what you think.

          Cheers.
          Powell.

          Show
          geek101 Powell Molleti added a comment - Hi Abe, I have added my comments on PR directly. We could table the hostname verification for next iteration of SSL support to move forward if required. For server to server SSL I think this is useful but will need help from QuorumPeer. Also for server to client SSL this is most likely not used. Performing a reverse DNS lookup to verify will mean that there is implicit trust for this, if customers provide just hostnames(or IPs) then that is the basis for trust and DNS reverse lookup might not be. Let me know what you think. Cheers. Powell.
          Hide
          abrahamfine Abraham Fine added a comment -

          Hi Powell Molleti-

          So I think it would be tough to table Hostname Verification as without it I think we are left with something relatively insecure.

          So if customers provide IPs alternative names can be specified as IPs so hostname verification should still work. DNS requires a reverse lookup, but I think that is fine if users are choosing to rely on DNS.

          I uploaded a new revision with some integration style tests. I know I still need to add tests for OCSP. The tests still require plenty of refactoring and the timeout is way to long, but they are working. Let me know what additional tests we should add.

          Abe

          Show
          abrahamfine Abraham Fine added a comment - Hi Powell Molleti - So I think it would be tough to table Hostname Verification as without it I think we are left with something relatively insecure. So if customers provide IPs alternative names can be specified as IPs so hostname verification should still work. DNS requires a reverse lookup, but I think that is fine if users are choosing to rely on DNS. I uploaded a new revision with some integration style tests. I know I still need to add tests for OCSP. The tests still require plenty of refactoring and the timeout is way to long, but they are working. Let me know what additional tests we should add. Abe
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r109263498

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str
          tmf.init(new CertPathTrustManagerParameters(pbParams));

          for (final TrustManager tm : tmf.getTrustManagers()) {

          • if (tm instanceof X509TrustManager) {
            + if (tm instanceof X509ExtendedTrustManager) {
            return new X509ExtendedTrustManager() {
          • HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);
            + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm;
            + HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();

          @Override
          public X509Certificate[] getAcceptedIssuers()

          { - return ((X509ExtendedTrustManager) tm).getAcceptedIssuers(); + return x509ExtendedTrustManager.getAcceptedIssuers(); }

          @Override

          • public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
          • hostnameChecker.match(socket.getInetAddress().getHostName(), x509Certificates[0]);
          • ((X509ExtendedTrustManager) tm).checkClientTrusted(x509Certificates, s, socket);
            + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
            + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
            + hostnameVerifier.verify(socket.getInetAddress().getHostName(), ((SSLSocket) socket).getSession());
              • End diff –

          Yes, the reverse dns lookup is done on the performHostnameVerification... line

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r109263498 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str tmf.init(new CertPathTrustManagerParameters(pbParams)); for (final TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509TrustManager) { + if (tm instanceof X509ExtendedTrustManager) { return new X509ExtendedTrustManager() { HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS); + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm; + HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); @Override public X509Certificate[] getAcceptedIssuers() { - return ((X509ExtendedTrustManager) tm).getAcceptedIssuers(); + return x509ExtendedTrustManager.getAcceptedIssuers(); } @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { hostnameChecker.match(socket.getInetAddress().getHostName(), x509Certificates [0] ); ((X509ExtendedTrustManager) tm).checkClientTrusted(x509Certificates, s, socket); + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + hostnameVerifier.verify(socket.getInetAddress().getHostName(), ((SSLSocket) socket).getSession()); End diff – Yes, the reverse dns lookup is done on the performHostnameVerification... line
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r109263833

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str
          tmf.init(new CertPathTrustManagerParameters(pbParams));

          for (final TrustManager tm : tmf.getTrustManagers()) {

          • if (tm instanceof X509TrustManager) {
            + if (tm instanceof X509ExtendedTrustManager) {
            return new X509ExtendedTrustManager() {
          • HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);
            + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm;
              • End diff –

          Not sure yet. The createTrustManager method might give us all the exposure to this implementation that we need. I think this will become more obvious as we begin expanding the tests.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r109263833 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -214,45 +237,56 @@ public static X509TrustManager createTrustManager(String trustStoreLocation, Str tmf.init(new CertPathTrustManagerParameters(pbParams)); for (final TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509TrustManager) { + if (tm instanceof X509ExtendedTrustManager) { return new X509ExtendedTrustManager() { HostnameChecker hostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS); + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm; End diff – Not sure yet. The createTrustManager method might give us all the exposure to this implementation that we need. I think this will become more obvious as we begin expanding the tests.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 37 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 3 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/493//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/493//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/493//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 37 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 3 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/493//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/493//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/493//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 37 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 3 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/500//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/500//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/500//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 37 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 3 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/500//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/500//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/500//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 37 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/501//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/501//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/501//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 37 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/501//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/501//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/501//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110057806

          — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java —
          @@ -0,0 +1,231 @@
          +/**
          — End diff –

          This is supposed to be a test file, right? If so, test file should be under src/test folder. Does this even build?

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110057806 — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java — @@ -0,0 +1,231 @@ +/** — End diff – This is supposed to be a test file, right? If so, test file should be under src/test folder. Does this even build?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110057980

          — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java —
          @@ -0,0 +1,231 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + *
          + * http://www.apache.org/licenses/LICENSE-2.0
          + *
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.common;
          +
          +import org.apache.zookeeper.ZKTestCase;
          +import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.server.ServerCnxnFactory;
          +import org.bouncycastle.asn1.x500.X500NameBuilder;
          +import org.bouncycastle.asn1.x500.style.BCStyle;
          +import org.bouncycastle.asn1.x509.BasicConstraints;
          +import org.bouncycastle.asn1.x509.Extension;
          +import org.bouncycastle.asn1.x509.KeyUsage;
          +import org.bouncycastle.cert.X509v3CertificateBuilder;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
          +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
          +import org.bouncycastle.jce.provider.BouncyCastleProvider;
          +import org.bouncycastle.operator.ContentSigner;
          +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
          +import org.junit.After;
          +import org.junit.AfterClass;
          +import org.junit.Assert;
          +import org.junit.Before;
          +import org.junit.BeforeClass;
          +import org.junit.Test;
          +
          +import javax.net.ssl.SSLContext;
          +import javax.net.ssl.SSLSocket;
          +import java.io.FileOutputStream;
          +import java.math.BigInteger;
          +import java.security.KeyPair;
          +import java.security.KeyPairGenerator;
          +import java.security.KeyStore;
          +import java.security.Security;
          +import java.security.cert.Certificate;
          +import java.security.cert.X509Certificate;
          +import java.util.Calendar;
          +import java.util.Date;
          +import java.util.Random;
          +
          +import static org.apache.zookeeper.test.ClientBase.createTmpDir;
          +
          +public class X509UtilTest extends ZKTestCase {
          +
          + private static final char[] PASSWORD = "password".toCharArray();
          + private X509Certificate rootCertificate;
          +
          + private String truststorePath;
          + private String keystorePath;
          + private static KeyPair rootKeyPair;
          +
          + private X509Util x509Util;
          +
          + @BeforeClass
          + public static void createKeyPair() throws Exception

          { + Security.addProvider(new BouncyCastleProvider()); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + rootKeyPair = keyPairGenerator.genKeyPair(); + }

          +
          + @AfterClass
          + public static void removeBouncyCastleProvider() throws Exception

          { + Security.removeProvider("BC"); + }

          +
          + @Before
          + public void setUp() throws Exception

          { + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + String tmpDir = createTmpDir().getAbsolutePath(); + truststorePath = tmpDir + "/truststore.jks"; + keystorePath = tmpDir + "/keystore.jks"; + + x509Util = new ClientX509Util(); + + writeKeystore(rootCertificate, rootKeyPair, keystorePath); + + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(x509Util.getSslKeystoreLocationProperty(), keystorePath); + System.setProperty(x509Util.getSslKeystorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(x509Util.getSslTruststorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslHostnameVerificationEnabledProperty(), "false"); + + writeTrustStore(PASSWORD); + }

          +
          + private void writeKeystore(X509Certificate certificate, KeyPair keyPair, String path) throws Exception {
          + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
          + keyStore.load(null, PASSWORD);
          + keyStore.setKeyEntry("alias", keyPair.getPrivate(), PASSWORD, new Certificate[]

          { certificate }

          );
          + FileOutputStream outputStream = new FileOutputStream(path);
          + keyStore.store(outputStream, PASSWORD);
          + outputStream.flush();
          + outputStream.close();
          + }
          +
          + private void writeTrustStore(char[] password) throws Exception {
          + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
          + trustStore.load(null, password);
          + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate);
          + FileOutputStream outputStream = new FileOutputStream(truststorePath);
          + if (password == null)

          { + trustStore.store(outputStream, new char[0]); + }

          else

          { + trustStore.store(outputStream, password); + }

          + outputStream.flush();
          + outputStream.close();
          + }
          +
          + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception

          { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, "localhost"); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate()); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + }

          +
          + @After
          + public void cleanUp() throws Exception

          { + System.clearProperty(x509Util.getSslKeystoreLocationProperty()); + System.clearProperty(x509Util.getSslKeystorePasswdProperty()); + System.clearProperty(x509Util.getSslTruststoreLocationProperty()); + System.clearProperty(x509Util.getSslTruststorePasswdProperty()); + System.clearProperty(x509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(x509Util.getSslOcspEnabledProperty()); + System.clearProperty(x509Util.getSslCrlEnabledProperty()); + System.clearProperty("com.sun.net.ssl.checkRevocation"); + System.clearProperty("com.sun.security.enableCRLDP"); + Security.setProperty("com.sun.security.enableCRLDP", "false"); + }

          +
          + @Test
          — End diff –

          Just in case if any of these test method run for a long time (which I doubt) then it'll be good to specify a timeout value annotation.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110057980 — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java — @@ -0,0 +1,231 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.common; + +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import java.io.FileOutputStream; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; +import java.util.Random; + +import static org.apache.zookeeper.test.ClientBase.createTmpDir; + +public class X509UtilTest extends ZKTestCase { + + private static final char[] PASSWORD = "password".toCharArray(); + private X509Certificate rootCertificate; + + private String truststorePath; + private String keystorePath; + private static KeyPair rootKeyPair; + + private X509Util x509Util; + + @BeforeClass + public static void createKeyPair() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + rootKeyPair = keyPairGenerator.genKeyPair(); + } + + @AfterClass + public static void removeBouncyCastleProvider() throws Exception { + Security.removeProvider("BC"); + } + + @Before + public void setUp() throws Exception { + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + String tmpDir = createTmpDir().getAbsolutePath(); + truststorePath = tmpDir + "/truststore.jks"; + keystorePath = tmpDir + "/keystore.jks"; + + x509Util = new ClientX509Util(); + + writeKeystore(rootCertificate, rootKeyPair, keystorePath); + + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(x509Util.getSslKeystoreLocationProperty(), keystorePath); + System.setProperty(x509Util.getSslKeystorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(x509Util.getSslTruststorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslHostnameVerificationEnabledProperty(), "false"); + + writeTrustStore(PASSWORD); + } + + private void writeKeystore(X509Certificate certificate, KeyPair keyPair, String path) throws Exception { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, PASSWORD); + keyStore.setKeyEntry("alias", keyPair.getPrivate(), PASSWORD, new Certificate[] { certificate } ); + FileOutputStream outputStream = new FileOutputStream(path); + keyStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + } + + private void writeTrustStore(char[] password) throws Exception { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, password); + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate); + FileOutputStream outputStream = new FileOutputStream(truststorePath); + if (password == null) { + trustStore.store(outputStream, new char[0]); + } else { + trustStore.store(outputStream, password); + } + outputStream.flush(); + outputStream.close(); + } + + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, "localhost"); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate()); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + } + + @After + public void cleanUp() throws Exception { + System.clearProperty(x509Util.getSslKeystoreLocationProperty()); + System.clearProperty(x509Util.getSslKeystorePasswdProperty()); + System.clearProperty(x509Util.getSslTruststoreLocationProperty()); + System.clearProperty(x509Util.getSslTruststorePasswdProperty()); + System.clearProperty(x509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(x509Util.getSslOcspEnabledProperty()); + System.clearProperty(x509Util.getSslCrlEnabledProperty()); + System.clearProperty("com.sun.net.ssl.checkRevocation"); + System.clearProperty("com.sun.security.enableCRLDP"); + Security.setProperty("com.sun.security.enableCRLDP", "false"); + } + + @Test — End diff – Just in case if any of these test method run for a long time (which I doubt) then it'll be good to specify a timeout value annotation.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110060745

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Observer.java —
          @@ -63,10 +63,10 @@ void observeLeader() throws Exception {
          zk.registerJMX(new ObserverBean(this, zk), self.jmxLocalPeerBean);

          try {

          • InetSocketAddress addr = findLeader();
              • End diff –

          I am not sure what this change of addr from type InetSocketAddress to QuorumServer is for. I may miss something though.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110060745 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Observer.java — @@ -63,10 +63,10 @@ void observeLeader() throws Exception { zk.registerJMX(new ObserverBean(this, zk), self.jmxLocalPeerBean); try { InetSocketAddress addr = findLeader(); End diff – I am not sure what this change of addr from type InetSocketAddress to QuorumServer is for. I may miss something though.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110060990

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -193,21 +198,18 @@ void request(Request request) throws IOException {
          /**

          • Returns the address of the node we think is the leader.
            */
          • protected InetSocketAddress findLeader() {
            + protected QuorumServer findLeader() {
              • End diff –

          Not sure why findLeader is returning a QuorumServer instead of InetSocketAddress?

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110060990 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -193,21 +198,18 @@ void request(Request request) throws IOException { /** Returns the address of the node we think is the leader. */ protected InetSocketAddress findLeader() { + protected QuorumServer findLeader() { End diff – Not sure why findLeader is returning a QuorumServer instead of InetSocketAddress?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110061068

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -253,7 +255,10 @@ protected void connectToLeader(InetSocketAddress addr)
          throw new IOException("initLimit exceeded on retries.");
          }

          • sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime));
            + sockConnect(sock, leader.addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime));
              • End diff –

          leader could be null in leader.addr right? Still wondering why change the leader type from an address to a QuorumServer.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110061068 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -253,7 +255,10 @@ protected void connectToLeader(InetSocketAddress addr) throw new IOException("initLimit exceeded on retries."); } sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime)); + sockConnect(sock, leader.addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime)); End diff – leader could be null in leader.addr right? Still wondering why change the leader type from an address to a QuorumServer.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110061229

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -230,15 +232,15 @@ protected void sockConnect(Socket sock, InetSocketAddress addr, int timeout)
          /**

          • Establish a connection with the Leader found by findLeader. Retries
          • until either initLimit time has elapsed or 5 tries have happened.
          • * @param addr - the address of the Leader to connect to.
            + * @param leader - the QuorumServer elected leader
          • @throws IOException - if the socket connection fails on the 5th attempt
          • @throws ConnectException
          • @throws InterruptedException
            */
          • protected void connectToLeader(InetSocketAddress addr)
          • throws IOException, ConnectException, InterruptedException {
          • sock = new Socket();
          • sock.setSoTimeout(self.tickTime * self.initLimit);
            + protected void connectToLeader(QuorumServer leader)
            + throws IOException, InterruptedException, X509Exception {
            + QuorumX509Util quorumX509Util = new QuorumX509Util();
              • End diff –

          We now create QuorumX509Util whenever connectToLeader is called, even if ssl is not enabled. Could this be improved so the SSL related code path is only involved when ssl is enabled?

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110061229 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -230,15 +232,15 @@ protected void sockConnect(Socket sock, InetSocketAddress addr, int timeout) /** Establish a connection with the Leader found by findLeader. Retries until either initLimit time has elapsed or 5 tries have happened. * @param addr - the address of the Leader to connect to. + * @param leader - the QuorumServer elected leader @throws IOException - if the socket connection fails on the 5th attempt @throws ConnectException @throws InterruptedException */ protected void connectToLeader(InetSocketAddress addr) throws IOException, ConnectException, InterruptedException { sock = new Socket(); sock.setSoTimeout(self.tickTime * self.initLimit); + protected void connectToLeader(QuorumServer leader) + throws IOException, InterruptedException, X509Exception { + QuorumX509Util quorumX509Util = new QuorumX509Util(); End diff – We now create QuorumX509Util whenever connectToLeader is called, even if ssl is not enabled. Could this be improved so the SSL related code path is only involved when ssl is enabled?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110068698

          — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java —
          @@ -0,0 +1,231 @@
          +/**
          — End diff –

          Whoops... Fixed.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110068698 — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java — @@ -0,0 +1,231 @@ +/** — End diff – Whoops... Fixed.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110068782

          — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java —
          @@ -0,0 +1,231 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + *
          + * http://www.apache.org/licenses/LICENSE-2.0
          + *
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.common;
          +
          +import org.apache.zookeeper.ZKTestCase;
          +import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.server.ServerCnxnFactory;
          +import org.bouncycastle.asn1.x500.X500NameBuilder;
          +import org.bouncycastle.asn1.x500.style.BCStyle;
          +import org.bouncycastle.asn1.x509.BasicConstraints;
          +import org.bouncycastle.asn1.x509.Extension;
          +import org.bouncycastle.asn1.x509.KeyUsage;
          +import org.bouncycastle.cert.X509v3CertificateBuilder;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
          +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
          +import org.bouncycastle.jce.provider.BouncyCastleProvider;
          +import org.bouncycastle.operator.ContentSigner;
          +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
          +import org.junit.After;
          +import org.junit.AfterClass;
          +import org.junit.Assert;
          +import org.junit.Before;
          +import org.junit.BeforeClass;
          +import org.junit.Test;
          +
          +import javax.net.ssl.SSLContext;
          +import javax.net.ssl.SSLSocket;
          +import java.io.FileOutputStream;
          +import java.math.BigInteger;
          +import java.security.KeyPair;
          +import java.security.KeyPairGenerator;
          +import java.security.KeyStore;
          +import java.security.Security;
          +import java.security.cert.Certificate;
          +import java.security.cert.X509Certificate;
          +import java.util.Calendar;
          +import java.util.Date;
          +import java.util.Random;
          +
          +import static org.apache.zookeeper.test.ClientBase.createTmpDir;
          +
          +public class X509UtilTest extends ZKTestCase {
          +
          + private static final char[] PASSWORD = "password".toCharArray();
          + private X509Certificate rootCertificate;
          +
          + private String truststorePath;
          + private String keystorePath;
          + private static KeyPair rootKeyPair;
          +
          + private X509Util x509Util;
          +
          + @BeforeClass
          + public static void createKeyPair() throws Exception

          { + Security.addProvider(new BouncyCastleProvider()); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + rootKeyPair = keyPairGenerator.genKeyPair(); + }

          +
          + @AfterClass
          + public static void removeBouncyCastleProvider() throws Exception

          { + Security.removeProvider("BC"); + }

          +
          + @Before
          + public void setUp() throws Exception

          { + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + String tmpDir = createTmpDir().getAbsolutePath(); + truststorePath = tmpDir + "/truststore.jks"; + keystorePath = tmpDir + "/keystore.jks"; + + x509Util = new ClientX509Util(); + + writeKeystore(rootCertificate, rootKeyPair, keystorePath); + + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(x509Util.getSslKeystoreLocationProperty(), keystorePath); + System.setProperty(x509Util.getSslKeystorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(x509Util.getSslTruststorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslHostnameVerificationEnabledProperty(), "false"); + + writeTrustStore(PASSWORD); + }

          +
          + private void writeKeystore(X509Certificate certificate, KeyPair keyPair, String path) throws Exception {
          + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
          + keyStore.load(null, PASSWORD);
          + keyStore.setKeyEntry("alias", keyPair.getPrivate(), PASSWORD, new Certificate[]

          { certificate }

          );
          + FileOutputStream outputStream = new FileOutputStream(path);
          + keyStore.store(outputStream, PASSWORD);
          + outputStream.flush();
          + outputStream.close();
          + }
          +
          + private void writeTrustStore(char[] password) throws Exception {
          + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
          + trustStore.load(null, password);
          + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate);
          + FileOutputStream outputStream = new FileOutputStream(truststorePath);
          + if (password == null)

          { + trustStore.store(outputStream, new char[0]); + }

          else

          { + trustStore.store(outputStream, password); + }

          + outputStream.flush();
          + outputStream.close();
          + }
          +
          + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception

          { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, "localhost"); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate()); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + }

          +
          + @After
          + public void cleanUp() throws Exception

          { + System.clearProperty(x509Util.getSslKeystoreLocationProperty()); + System.clearProperty(x509Util.getSslKeystorePasswdProperty()); + System.clearProperty(x509Util.getSslTruststoreLocationProperty()); + System.clearProperty(x509Util.getSslTruststorePasswdProperty()); + System.clearProperty(x509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(x509Util.getSslOcspEnabledProperty()); + System.clearProperty(x509Util.getSslCrlEnabledProperty()); + System.clearProperty("com.sun.net.ssl.checkRevocation"); + System.clearProperty("com.sun.security.enableCRLDP"); + Security.setProperty("com.sun.security.enableCRLDP", "false"); + }

          +
          + @Test
          — End diff –

          Agreed.

          The QuorumSSLTest needs them too.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110068782 — Diff: src/java/main/org/apache/zookeeper/common/X509UtilTest.java — @@ -0,0 +1,231 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.common; + +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import java.io.FileOutputStream; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; +import java.util.Random; + +import static org.apache.zookeeper.test.ClientBase.createTmpDir; + +public class X509UtilTest extends ZKTestCase { + + private static final char[] PASSWORD = "password".toCharArray(); + private X509Certificate rootCertificate; + + private String truststorePath; + private String keystorePath; + private static KeyPair rootKeyPair; + + private X509Util x509Util; + + @BeforeClass + public static void createKeyPair() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + rootKeyPair = keyPairGenerator.genKeyPair(); + } + + @AfterClass + public static void removeBouncyCastleProvider() throws Exception { + Security.removeProvider("BC"); + } + + @Before + public void setUp() throws Exception { + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + String tmpDir = createTmpDir().getAbsolutePath(); + truststorePath = tmpDir + "/truststore.jks"; + keystorePath = tmpDir + "/keystore.jks"; + + x509Util = new ClientX509Util(); + + writeKeystore(rootCertificate, rootKeyPair, keystorePath); + + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(x509Util.getSslKeystoreLocationProperty(), keystorePath); + System.setProperty(x509Util.getSslKeystorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(x509Util.getSslTruststorePasswdProperty(), new String(PASSWORD)); + System.setProperty(x509Util.getSslHostnameVerificationEnabledProperty(), "false"); + + writeTrustStore(PASSWORD); + } + + private void writeKeystore(X509Certificate certificate, KeyPair keyPair, String path) throws Exception { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, PASSWORD); + keyStore.setKeyEntry("alias", keyPair.getPrivate(), PASSWORD, new Certificate[] { certificate } ); + FileOutputStream outputStream = new FileOutputStream(path); + keyStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + } + + private void writeTrustStore(char[] password) throws Exception { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, password); + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate); + FileOutputStream outputStream = new FileOutputStream(truststorePath); + if (password == null) { + trustStore.store(outputStream, new char[0]); + } else { + trustStore.store(outputStream, password); + } + outputStream.flush(); + outputStream.close(); + } + + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, "localhost"); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate()); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + } + + @After + public void cleanUp() throws Exception { + System.clearProperty(x509Util.getSslKeystoreLocationProperty()); + System.clearProperty(x509Util.getSslKeystorePasswdProperty()); + System.clearProperty(x509Util.getSslTruststoreLocationProperty()); + System.clearProperty(x509Util.getSslTruststorePasswdProperty()); + System.clearProperty(x509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(x509Util.getSslOcspEnabledProperty()); + System.clearProperty(x509Util.getSslCrlEnabledProperty()); + System.clearProperty("com.sun.net.ssl.checkRevocation"); + System.clearProperty("com.sun.security.enableCRLDP"); + Security.setProperty("com.sun.security.enableCRLDP", "false"); + } + + @Test — End diff – Agreed. The QuorumSSLTest needs them too.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110068829

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -193,21 +198,18 @@ void request(Request request) throws IOException {
          /**

          • Returns the address of the node we think is the leader.
            */
          • protected InetSocketAddress findLeader() {
            + protected QuorumServer findLeader() {
              • End diff –

          Egah, this is a change from an earlier iteration that I forgot to remove. Fixed now.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110068829 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -193,21 +198,18 @@ void request(Request request) throws IOException { /** Returns the address of the node we think is the leader. */ protected InetSocketAddress findLeader() { + protected QuorumServer findLeader() { End diff – Egah, this is a change from an earlier iteration that I forgot to remove. Fixed now.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110068870

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -253,7 +255,10 @@ protected void connectToLeader(InetSocketAddress addr)
          throw new IOException("initLimit exceeded on retries.");
          }

          • sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime));
            + sockConnect(sock, leader.addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime));
              • End diff –

          This should be fixed.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110068870 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -253,7 +255,10 @@ protected void connectToLeader(InetSocketAddress addr) throw new IOException("initLimit exceeded on retries."); } sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime)); + sockConnect(sock, leader.addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime)); End diff – This should be fixed.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 41 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/504//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/504//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/504//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 41 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/504//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/504//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/504//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110070800

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -230,15 +232,15 @@ protected void sockConnect(Socket sock, InetSocketAddress addr, int timeout)
          /**

          • Establish a connection with the Leader found by findLeader. Retries
          • until either initLimit time has elapsed or 5 tries have happened.
          • * @param addr - the address of the Leader to connect to.
            + * @param leader - the QuorumServer elected leader
          • @throws IOException - if the socket connection fails on the 5th attempt
          • @throws ConnectException
          • @throws InterruptedException
            */
          • protected void connectToLeader(InetSocketAddress addr)
          • throws IOException, ConnectException, InterruptedException {
          • sock = new Socket();
          • sock.setSoTimeout(self.tickTime * self.initLimit);
            + protected void connectToLeader(QuorumServer leader)
            + throws IOException, InterruptedException, X509Exception {
            + QuorumX509Util quorumX509Util = new QuorumX509Util();
              • End diff –

          Agreed. Fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110070800 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -230,15 +232,15 @@ protected void sockConnect(Socket sock, InetSocketAddress addr, int timeout) /** Establish a connection with the Leader found by findLeader. Retries until either initLimit time has elapsed or 5 tries have happened. * @param addr - the address of the Leader to connect to. + * @param leader - the QuorumServer elected leader @throws IOException - if the socket connection fails on the 5th attempt @throws ConnectException @throws InterruptedException */ protected void connectToLeader(InetSocketAddress addr) throws IOException, ConnectException, InterruptedException { sock = new Socket(); sock.setSoTimeout(self.tickTime * self.initLimit); + protected void connectToLeader(QuorumServer leader) + throws IOException, InterruptedException, X509Exception { + QuorumX509Util quorumX509Util = new QuorumX509Util(); End diff – Agreed. Fixed
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 41 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/505//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/505//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/505//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 41 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/505//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/505//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/505//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user geek101 commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110088588

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Leader.java —
          @@ -216,18 +218,35 @@ public boolean isQuorumSynced(QuorumVerifier qv) {

          private final ServerSocket ss;

          • Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException {
            + Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException, X509Exception {
            this.self = self;
            try {
          • if (self.getQuorumListenOnAllIPs()) {
          • ss = new ServerSocket(self.getQuorumAddress().getPort());
            + if (self.shouldUsePortUnification()) {
              • End diff –

          Is it possible to move all this code away from here to a different class like SSLSocketFactory which can help keep Leader code have similar number of branches if not more.

          Show
          githubbot ASF GitHub Bot added a comment - Github user geek101 commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110088588 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Leader.java — @@ -216,18 +218,35 @@ public boolean isQuorumSynced(QuorumVerifier qv) { private final ServerSocket ss; Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException { + Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException, X509Exception { this.self = self; try { if (self.getQuorumListenOnAllIPs()) { ss = new ServerSocket(self.getQuorumAddress().getPort()); + if (self.shouldUsePortUnification()) { End diff – Is it possible to move all this code away from here to a different class like SSLSocketFactory which can help keep Leader code have similar number of branches if not more.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user geek101 commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110089020

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -171,21 +226,100 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke
          }
          }

          • public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword)
            + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword,
            + boolean crlEnabled, boolean ocspEnabled,
            + final boolean hostnameVerificationEnabled,
            + final boolean shouldVerifyClientHostname)
            throws TrustManagerException {
            FileInputStream inputStream = null;
            try {
          • char[] trustStorePasswordChars = trustStorePassword.toCharArray();
            File trustStoreFile = new File(trustStoreLocation);
            KeyStore ts = KeyStore.getInstance("JKS");
            inputStream = new FileInputStream(trustStoreFile);
          • ts.load(inputStream, trustStorePasswordChars);
          • TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
          • tmf.init(ts);
            + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + }

            else

            { + ts.load(inputStream, null); + }
          • for (TrustManager tm : tmf.getTrustManagers()) {
          • if (tm instanceof X509TrustManager) {
          • return (X509TrustManager) tm;
            + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector());
            + if (crlEnabled || ocspEnabled)
            Unknown macro: { + pbParams.setRevocationEnabled(true); + System.setProperty("com.sun.net.ssl.checkRevocation", "true"); + System.setProperty("com.sun.security.enableCRLDP", "true"); + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + } + + }

            else

            { + pbParams.setRevocationEnabled(false); + }

            +
            + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
            + tmf.init(new CertPathTrustManagerParameters(pbParams));
            +
            + for (final TrustManager tm : tmf.getTrustManagers()) {
            + if (tm instanceof X509ExtendedTrustManager) {
            + return new X509ExtendedTrustManager() {
            + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm;
            + DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
            +
            + @Override
            + public X509Certificate[] getAcceptedIssuers()

            { + return x509ExtendedTrustManager.getAcceptedIssuers(); + }

            +
            + @Override
            + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
            + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
            + performHostnameVerification(socket.getInetAddress().getHostName(), chain[0]);

              • End diff –

          For Quorum server connection I am not entirely sure if getInetAddres().getHostName() is a good idea when on server side this will force a reverse DNS lookup. When customer has only provided a ip address as config perhaps using hostname is not correct. And if customer has provided a hostname performing reverse dns lookup is not necessary and can be argued as not safe. Since the trust anchor is the user provided config and not the DNS service. Let me know what you think.

          Show
          githubbot ASF GitHub Bot added a comment - Github user geek101 commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110089020 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -171,21 +226,100 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke } } public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword) + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword, + boolean crlEnabled, boolean ocspEnabled, + final boolean hostnameVerificationEnabled, + final boolean shouldVerifyClientHostname) throws TrustManagerException { FileInputStream inputStream = null; try { char[] trustStorePasswordChars = trustStorePassword.toCharArray(); File trustStoreFile = new File(trustStoreLocation); KeyStore ts = KeyStore.getInstance("JKS"); inputStream = new FileInputStream(trustStoreFile); ts.load(inputStream, trustStorePasswordChars); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(ts); + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + } else { + ts.load(inputStream, null); + } for (TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509TrustManager) { return (X509TrustManager) tm; + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); + if (crlEnabled || ocspEnabled) Unknown macro: { + pbParams.setRevocationEnabled(true); + System.setProperty("com.sun.net.ssl.checkRevocation", "true"); + System.setProperty("com.sun.security.enableCRLDP", "true"); + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + } + + } else { + pbParams.setRevocationEnabled(false); + } + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(new CertPathTrustManagerParameters(pbParams)); + + for (final TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509ExtendedTrustManager) { + return new X509ExtendedTrustManager() { + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm; + DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); + + @Override + public X509Certificate[] getAcceptedIssuers() { + return x509ExtendedTrustManager.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + performHostnameVerification(socket.getInetAddress().getHostName(), chain [0] ); End diff – For Quorum server connection I am not entirely sure if getInetAddres().getHostName() is a good idea when on server side this will force a reverse DNS lookup. When customer has only provided a ip address as config perhaps using hostname is not correct. And if customer has provided a hostname performing reverse dns lookup is not necessary and can be argued as not safe. Since the trust anchor is the user provided config and not the DNS service. Let me know what you think.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user geek101 commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110086744

          — Diff: ivy.xml —
          @@ -63,6 +67,9 @@
          <dependency org="commons-collections" name="commons-collections"
          rev="3.2.2" conf="test->default"/>

          + <dependency org="org.bouncycastle" name="bcprov-jdk15on" rev="1.56" conf="test->default"/>
          + <dependency org="org.bouncycastle" name="bcpkix-jdk15on" rev="1.56" conf="test->default"/>
          — End diff –

          is this specified twice?

          Show
          githubbot ASF GitHub Bot added a comment - Github user geek101 commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110086744 — Diff: ivy.xml — @@ -63,6 +67,9 @@ <dependency org="commons-collections" name="commons-collections" rev="3.2.2" conf="test->default"/> + <dependency org="org.bouncycastle" name="bcprov-jdk15on" rev="1.56" conf="test->default"/> + <dependency org="org.bouncycastle" name="bcpkix-jdk15on" rev="1.56" conf="test->default"/> — End diff – is this specified twice?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user geek101 commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110090086

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -18,63 +18,116 @@
          package org.apache.zookeeper.common;

          +import org.apache.http.conn.ssl.DefaultHostnameVerifier;
          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.CertPathTrustManagerParameters;
          import javax.net.ssl.KeyManager;
          import javax.net.ssl.KeyManagerFactory;
          import javax.net.ssl.SSLContext;
          +import javax.net.ssl.SSLEngine;
          +import javax.net.ssl.SSLException;
          +import javax.net.ssl.SSLParameters;
          +import javax.net.ssl.SSLServerSocket;
          +import javax.net.ssl.SSLSocket;
          import javax.net.ssl.TrustManager;
          import javax.net.ssl.TrustManagerFactory;
          +import javax.net.ssl.X509ExtendedTrustManager;
          import javax.net.ssl.X509KeyManager;
          import javax.net.ssl.X509TrustManager;
          import java.io.File;
          import java.io.FileInputStream;
          import java.io.IOException;
          +import java.net.Socket;
          +import java.security.KeyManagementException;
          import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.Security;
          +import java.security.cert.CertificateException;
          +import java.security.cert.PKIXBuilderParameters;
          +import java.security.cert.X509CertSelector;
          +import java.security.cert.X509Certificate;

          -import org.slf4j.Logger;
          -import org.slf4j.LoggerFactory;
          -
          -import static org.apache.zookeeper.common.X509Exception.KeyManagerException;
          -import static org.apache.zookeeper.common.X509Exception.SSLContextException;
          -import static org.apache.zookeeper.common.X509Exception.TrustManagerException;
          +import static org.apache.zookeeper.common.X509Exception.*;

          /**

          • Utility code for X509 handling
            */
            -public class X509Util {
            +public abstract class X509Util {
            private static final Logger LOG = LoggerFactory.getLogger(X509Util.class);
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_LOCATION}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_KEYSTORE_LOCATION = "zookeeper.ssl.keyStore.location";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_PASSWD}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_KEYSTORE_PASSWD = "zookeeper.ssl.keyStore.password";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_LOCATION}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_TRUSTSTORE_LOCATION = "zookeeper.ssl.trustStore.location";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_PASSWD}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_TRUSTSTORE_PASSWD = "zookeeper.ssl.trustStore.password";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_AUTHPROVIDER}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_AUTHPROVIDER = "zookeeper.ssl.authProvider";
            -
          • public static SSLContext createSSLContext() throws SSLContextException {
            + public static final String DEFAULT_PROTOCOL = "TLSv1";
            +
            + private String sslProtocolProperty = getConfigPrefix() + "protocol";
            + private String cipherSuitesProperty = getConfigPrefix() + "ciphersuites";
              • End diff –

          nice.

          Show
          githubbot ASF GitHub Bot added a comment - Github user geek101 commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110090086 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -18,63 +18,116 @@ package org.apache.zookeeper.common; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.net.Socket; +import java.security.KeyManagementException; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.apache.zookeeper.common.X509Exception.KeyManagerException; -import static org.apache.zookeeper.common.X509Exception.SSLContextException; -import static org.apache.zookeeper.common.X509Exception.TrustManagerException; +import static org.apache.zookeeper.common.X509Exception.*; /** Utility code for X509 handling */ -public class X509Util { +public abstract class X509Util { private static final Logger LOG = LoggerFactory.getLogger(X509Util.class); /** * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_LOCATION} * instead. */ @Deprecated public static final String SSL_KEYSTORE_LOCATION = "zookeeper.ssl.keyStore.location"; /** * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_PASSWD} * instead. */ @Deprecated public static final String SSL_KEYSTORE_PASSWD = "zookeeper.ssl.keyStore.password"; /** * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_LOCATION} * instead. */ @Deprecated public static final String SSL_TRUSTSTORE_LOCATION = "zookeeper.ssl.trustStore.location"; /** * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_PASSWD} * instead. */ @Deprecated public static final String SSL_TRUSTSTORE_PASSWD = "zookeeper.ssl.trustStore.password"; /** * @deprecated Use {@link ZKConfig#SSL_AUTHPROVIDER} * instead. */ @Deprecated public static final String SSL_AUTHPROVIDER = "zookeeper.ssl.authProvider"; - public static SSLContext createSSLContext() throws SSLContextException { + public static final String DEFAULT_PROTOCOL = "TLSv1"; + + private String sslProtocolProperty = getConfigPrefix() + "protocol"; + private String cipherSuitesProperty = getConfigPrefix() + "ciphersuites"; End diff – nice.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user geek101 commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110089934

          — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java —
          @@ -0,0 +1,603 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + * <p/>
          + * http://www.apache.org/licenses/LICENSE-2.0
          + * <p/>
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.test;
          +
          +import com.sun.net.httpserver.Headers;
          +import com.sun.net.httpserver.HttpHandler;
          +import com.sun.net.httpserver.HttpServer;
          +import org.apache.zookeeper.PortAssignment;
          +import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.common.QuorumX509Util;
          +import org.apache.zookeeper.server.ServerCnxnFactory;
          +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase;
          +import org.bouncycastle.asn1.ocsp.OCSPResponse;
          +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
          +import org.bouncycastle.asn1.x500.X500Name;
          +import org.bouncycastle.asn1.x500.X500NameBuilder;
          +import org.bouncycastle.asn1.x500.style.BCStyle;
          +import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
          +import org.bouncycastle.asn1.x509.BasicConstraints;
          +import org.bouncycastle.asn1.x509.CRLDistPoint;
          +import org.bouncycastle.asn1.x509.CRLNumber;
          +import org.bouncycastle.asn1.x509.CRLReason;
          +import org.bouncycastle.asn1.x509.DistributionPoint;
          +import org.bouncycastle.asn1.x509.DistributionPointName;
          +import org.bouncycastle.asn1.x509.Extension;
          +import org.bouncycastle.asn1.x509.GeneralName;
          +import org.bouncycastle.asn1.x509.GeneralNames;
          +import org.bouncycastle.asn1.x509.KeyUsage;
          +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
          +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
          +import org.bouncycastle.cert.X509CRLHolder;
          +import org.bouncycastle.cert.X509CertificateHolder;
          +import org.bouncycastle.cert.X509ExtensionUtils;
          +import org.bouncycastle.cert.X509v2CRLBuilder;
          +import org.bouncycastle.cert.X509v3CertificateBuilder;
          +import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
          +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder;
          +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
          +import org.bouncycastle.cert.ocsp.BasicOCSPResp;
          +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.CertificateID;
          +import org.bouncycastle.cert.ocsp.CertificateStatus;
          +import org.bouncycastle.cert.ocsp.OCSPException;
          +import org.bouncycastle.cert.ocsp.OCSPReq;
          +import org.bouncycastle.cert.ocsp.OCSPResp;
          +import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.Req;
          +import org.bouncycastle.cert.ocsp.UnknownStatus;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID;
          +import org.bouncycastle.crypto.util.PublicKeyFactory;
          +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
          +import org.bouncycastle.jce.provider.BouncyCastleProvider;
          +import org.bouncycastle.openssl.MiscPEMGenerator;
          +import org.bouncycastle.operator.ContentSigner;
          +import org.bouncycastle.operator.DigestCalculator;
          +import org.bouncycastle.operator.OperatorException;
          +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
          +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
          +import org.bouncycastle.util.io.pem.PemWriter;
          +import org.junit.After;
          +import org.junit.Assert;
          +import org.junit.Before;
          +import org.junit.Test;
          +
          +import java.io.FileOutputStream;
          +import java.io.FileWriter;
          +import java.io.IOException;
          +import java.io.InputStream;
          +import java.io.OutputStream;
          +import java.math.BigInteger;
          +import java.net.InetSocketAddress;
          +import java.security.KeyPair;
          +import java.security.KeyPairGenerator;
          +import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.NoSuchProviderException;
          +import java.security.PrivateKey;
          +import java.security.Security;
          +import java.security.cert.Certificate;
          +import java.security.cert.CertificateEncodingException;
          +import java.security.cert.X509Certificate;
          +import java.util.Calendar;
          +import java.util.Date;
          +import java.util.HashMap;
          +import java.util.Map;
          +import java.util.Random;
          +
          +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
          +import static org.apache.zookeeper.test.ClientBase.createTmpDir;
          +
          +public class QuorumSSLTest extends QuorumPeerTestBase {
          — End diff –

          This is very nice great stuff

          Show
          githubbot ASF GitHub Bot added a comment - Github user geek101 commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110089934 — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java — @@ -0,0 +1,603 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.test; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.common.QuorumX509Util; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.bouncycastle.asn1.ocsp.OCSPResponse; +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.CRLNumber; +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509ExtensionUtils; +import org.bouncycastle.cert.X509v2CRLBuilder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.cert.ocsp.BasicOCSPResp; +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.CertificateStatus; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.ocsp.OCSPRespBuilder; +import org.bouncycastle.cert.ocsp.Req; +import org.bouncycastle.cert.ocsp.UnknownStatus; +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.MiscPEMGenerator; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.util.io.pem.PemWriter; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import static org.apache.zookeeper.test.ClientBase.createTmpDir; + +public class QuorumSSLTest extends QuorumPeerTestBase { — End diff – This is very nice great stuff
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110224561

          — Diff: ivy.xml —
          @@ -63,6 +67,9 @@
          <dependency org="commons-collections" name="commons-collections"
          rev="3.2.2" conf="test->default"/>

          + <dependency org="org.bouncycastle" name="bcprov-jdk15on" rev="1.56" conf="test->default"/>
          + <dependency org="org.bouncycastle" name="bcpkix-jdk15on" rev="1.56" conf="test->default"/>
          — End diff –

          One is bcprov-jdk15on and the other is bcpkix-jdk15on.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110224561 — Diff: ivy.xml — @@ -63,6 +67,9 @@ <dependency org="commons-collections" name="commons-collections" rev="3.2.2" conf="test->default"/> + <dependency org="org.bouncycastle" name="bcprov-jdk15on" rev="1.56" conf="test->default"/> + <dependency org="org.bouncycastle" name="bcpkix-jdk15on" rev="1.56" conf="test->default"/> — End diff – One is bcprov-jdk15on and the other is bcpkix-jdk15on.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110224694

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -18,63 +18,116 @@
          package org.apache.zookeeper.common;

          +import org.apache.http.conn.ssl.DefaultHostnameVerifier;
          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.CertPathTrustManagerParameters;
          import javax.net.ssl.KeyManager;
          import javax.net.ssl.KeyManagerFactory;
          import javax.net.ssl.SSLContext;
          +import javax.net.ssl.SSLEngine;
          +import javax.net.ssl.SSLException;
          +import javax.net.ssl.SSLParameters;
          +import javax.net.ssl.SSLServerSocket;
          +import javax.net.ssl.SSLSocket;
          import javax.net.ssl.TrustManager;
          import javax.net.ssl.TrustManagerFactory;
          +import javax.net.ssl.X509ExtendedTrustManager;
          import javax.net.ssl.X509KeyManager;
          import javax.net.ssl.X509TrustManager;
          import java.io.File;
          import java.io.FileInputStream;
          import java.io.IOException;
          +import java.net.Socket;
          +import java.security.KeyManagementException;
          import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.Security;
          +import java.security.cert.CertificateException;
          +import java.security.cert.PKIXBuilderParameters;
          +import java.security.cert.X509CertSelector;
          +import java.security.cert.X509Certificate;

          -import org.slf4j.Logger;
          -import org.slf4j.LoggerFactory;
          -
          -import static org.apache.zookeeper.common.X509Exception.KeyManagerException;
          -import static org.apache.zookeeper.common.X509Exception.SSLContextException;
          -import static org.apache.zookeeper.common.X509Exception.TrustManagerException;
          +import static org.apache.zookeeper.common.X509Exception.*;

          /**

          • Utility code for X509 handling
            */
            -public class X509Util {
            +public abstract class X509Util {
            private static final Logger LOG = LoggerFactory.getLogger(X509Util.class);
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_LOCATION}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_KEYSTORE_LOCATION = "zookeeper.ssl.keyStore.location";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_PASSWD}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_KEYSTORE_PASSWD = "zookeeper.ssl.keyStore.password";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_LOCATION}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_TRUSTSTORE_LOCATION = "zookeeper.ssl.trustStore.location";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_PASSWD}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_TRUSTSTORE_PASSWD = "zookeeper.ssl.trustStore.password";
          • /**
          • * @deprecated Use {@link ZKConfig#SSL_AUTHPROVIDER}
          • * instead.
          • */
          • @Deprecated
          • public static final String SSL_AUTHPROVIDER = "zookeeper.ssl.authProvider";
            -
          • public static SSLContext createSSLContext() throws SSLContextException {
            + public static final String DEFAULT_PROTOCOL = "TLSv1";
            +
            + private String sslProtocolProperty = getConfigPrefix() + "protocol";
            + private String cipherSuitesProperty = getConfigPrefix() + "ciphersuites";
              • End diff –

          Thanks!

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110224694 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -18,63 +18,116 @@ package org.apache.zookeeper.common; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.net.Socket; +import java.security.KeyManagementException; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.apache.zookeeper.common.X509Exception.KeyManagerException; -import static org.apache.zookeeper.common.X509Exception.SSLContextException; -import static org.apache.zookeeper.common.X509Exception.TrustManagerException; +import static org.apache.zookeeper.common.X509Exception.*; /** Utility code for X509 handling */ -public class X509Util { +public abstract class X509Util { private static final Logger LOG = LoggerFactory.getLogger(X509Util.class); /** * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_LOCATION} * instead. */ @Deprecated public static final String SSL_KEYSTORE_LOCATION = "zookeeper.ssl.keyStore.location"; /** * @deprecated Use {@link ZKConfig#SSL_KEYSTORE_PASSWD} * instead. */ @Deprecated public static final String SSL_KEYSTORE_PASSWD = "zookeeper.ssl.keyStore.password"; /** * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_LOCATION} * instead. */ @Deprecated public static final String SSL_TRUSTSTORE_LOCATION = "zookeeper.ssl.trustStore.location"; /** * @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_PASSWD} * instead. */ @Deprecated public static final String SSL_TRUSTSTORE_PASSWD = "zookeeper.ssl.trustStore.password"; /** * @deprecated Use {@link ZKConfig#SSL_AUTHPROVIDER} * instead. */ @Deprecated public static final String SSL_AUTHPROVIDER = "zookeeper.ssl.authProvider"; - public static SSLContext createSSLContext() throws SSLContextException { + public static final String DEFAULT_PROTOCOL = "TLSv1"; + + private String sslProtocolProperty = getConfigPrefix() + "protocol"; + private String cipherSuitesProperty = getConfigPrefix() + "ciphersuites"; End diff – Thanks!
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110224715

          — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java —
          @@ -0,0 +1,603 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + * <p/>
          + * http://www.apache.org/licenses/LICENSE-2.0
          + * <p/>
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.test;
          +
          +import com.sun.net.httpserver.Headers;
          +import com.sun.net.httpserver.HttpHandler;
          +import com.sun.net.httpserver.HttpServer;
          +import org.apache.zookeeper.PortAssignment;
          +import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.common.QuorumX509Util;
          +import org.apache.zookeeper.server.ServerCnxnFactory;
          +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase;
          +import org.bouncycastle.asn1.ocsp.OCSPResponse;
          +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
          +import org.bouncycastle.asn1.x500.X500Name;
          +import org.bouncycastle.asn1.x500.X500NameBuilder;
          +import org.bouncycastle.asn1.x500.style.BCStyle;
          +import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
          +import org.bouncycastle.asn1.x509.BasicConstraints;
          +import org.bouncycastle.asn1.x509.CRLDistPoint;
          +import org.bouncycastle.asn1.x509.CRLNumber;
          +import org.bouncycastle.asn1.x509.CRLReason;
          +import org.bouncycastle.asn1.x509.DistributionPoint;
          +import org.bouncycastle.asn1.x509.DistributionPointName;
          +import org.bouncycastle.asn1.x509.Extension;
          +import org.bouncycastle.asn1.x509.GeneralName;
          +import org.bouncycastle.asn1.x509.GeneralNames;
          +import org.bouncycastle.asn1.x509.KeyUsage;
          +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
          +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
          +import org.bouncycastle.cert.X509CRLHolder;
          +import org.bouncycastle.cert.X509CertificateHolder;
          +import org.bouncycastle.cert.X509ExtensionUtils;
          +import org.bouncycastle.cert.X509v2CRLBuilder;
          +import org.bouncycastle.cert.X509v3CertificateBuilder;
          +import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
          +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder;
          +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
          +import org.bouncycastle.cert.ocsp.BasicOCSPResp;
          +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.CertificateID;
          +import org.bouncycastle.cert.ocsp.CertificateStatus;
          +import org.bouncycastle.cert.ocsp.OCSPException;
          +import org.bouncycastle.cert.ocsp.OCSPReq;
          +import org.bouncycastle.cert.ocsp.OCSPResp;
          +import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.Req;
          +import org.bouncycastle.cert.ocsp.UnknownStatus;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID;
          +import org.bouncycastle.crypto.util.PublicKeyFactory;
          +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
          +import org.bouncycastle.jce.provider.BouncyCastleProvider;
          +import org.bouncycastle.openssl.MiscPEMGenerator;
          +import org.bouncycastle.operator.ContentSigner;
          +import org.bouncycastle.operator.DigestCalculator;
          +import org.bouncycastle.operator.OperatorException;
          +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
          +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
          +import org.bouncycastle.util.io.pem.PemWriter;
          +import org.junit.After;
          +import org.junit.Assert;
          +import org.junit.Before;
          +import org.junit.Test;
          +
          +import java.io.FileOutputStream;
          +import java.io.FileWriter;
          +import java.io.IOException;
          +import java.io.InputStream;
          +import java.io.OutputStream;
          +import java.math.BigInteger;
          +import java.net.InetSocketAddress;
          +import java.security.KeyPair;
          +import java.security.KeyPairGenerator;
          +import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.NoSuchProviderException;
          +import java.security.PrivateKey;
          +import java.security.Security;
          +import java.security.cert.Certificate;
          +import java.security.cert.CertificateEncodingException;
          +import java.security.cert.X509Certificate;
          +import java.util.Calendar;
          +import java.util.Date;
          +import java.util.HashMap;
          +import java.util.Map;
          +import java.util.Random;
          +
          +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
          +import static org.apache.zookeeper.test.ClientBase.createTmpDir;
          +
          +public class QuorumSSLTest extends QuorumPeerTestBase {
          — End diff –

          Thanks!

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110224715 — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java — @@ -0,0 +1,603 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.test; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.common.QuorumX509Util; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.bouncycastle.asn1.ocsp.OCSPResponse; +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.CRLNumber; +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509ExtensionUtils; +import org.bouncycastle.cert.X509v2CRLBuilder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.cert.ocsp.BasicOCSPResp; +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.CertificateStatus; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.ocsp.OCSPRespBuilder; +import org.bouncycastle.cert.ocsp.Req; +import org.bouncycastle.cert.ocsp.UnknownStatus; +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.MiscPEMGenerator; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.util.io.pem.PemWriter; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import static org.apache.zookeeper.test.ClientBase.createTmpDir; + +public class QuorumSSLTest extends QuorumPeerTestBase { — End diff – Thanks!
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110227948

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Leader.java —
          @@ -216,18 +218,35 @@ public boolean isQuorumSynced(QuorumVerifier qv) {

          private final ServerSocket ss;

          • Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException {
            + Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException, X509Exception {
            this.self = self;
            try {
          • if (self.getQuorumListenOnAllIPs()) {
          • ss = new ServerSocket(self.getQuorumAddress().getPort());
            + if (self.shouldUsePortUnification()) {
              • End diff –

          This is a tough call IMO. I think you are correct that moving this out of the Leader would make the leader code simpler but on the other hand this is logic that I think "belongs" to the leader and is not really relevant anywhere else.

          What do you think about the compromise of moving this to a static method in Leader where it can be tested and prevents cluttering up the constructor logic?

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110227948 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Leader.java — @@ -216,18 +218,35 @@ public boolean isQuorumSynced(QuorumVerifier qv) { private final ServerSocket ss; Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException { + Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException, X509Exception { this.self = self; try { if (self.getQuorumListenOnAllIPs()) { ss = new ServerSocket(self.getQuorumAddress().getPort()); + if (self.shouldUsePortUnification()) { End diff – This is a tough call IMO. I think you are correct that moving this out of the Leader would make the leader code simpler but on the other hand this is logic that I think "belongs" to the leader and is not really relevant anywhere else. What do you think about the compromise of moving this to a static method in Leader where it can be tested and prevents cluttering up the constructor logic?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r110263282

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -171,21 +226,100 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke
          }
          }

          • public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword)
            + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword,
            + boolean crlEnabled, boolean ocspEnabled,
            + final boolean hostnameVerificationEnabled,
            + final boolean shouldVerifyClientHostname)
            throws TrustManagerException {
            FileInputStream inputStream = null;
            try {
          • char[] trustStorePasswordChars = trustStorePassword.toCharArray();
            File trustStoreFile = new File(trustStoreLocation);
            KeyStore ts = KeyStore.getInstance("JKS");
            inputStream = new FileInputStream(trustStoreFile);
          • ts.load(inputStream, trustStorePasswordChars);
          • TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
          • tmf.init(ts);
            + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + }

            else

            { + ts.load(inputStream, null); + }
          • for (TrustManager tm : tmf.getTrustManagers()) {
          • if (tm instanceof X509TrustManager) {
          • return (X509TrustManager) tm;
            + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector());
            + if (crlEnabled || ocspEnabled)
            Unknown macro: { + pbParams.setRevocationEnabled(true); + System.setProperty("com.sun.net.ssl.checkRevocation", "true"); + System.setProperty("com.sun.security.enableCRLDP", "true"); + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + } + + }

            else

            { + pbParams.setRevocationEnabled(false); + }

            +
            + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
            + tmf.init(new CertPathTrustManagerParameters(pbParams));
            +
            + for (final TrustManager tm : tmf.getTrustManagers()) {
            + if (tm instanceof X509ExtendedTrustManager) {
            + return new X509ExtendedTrustManager() {
            + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm;
            + DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
            +
            + @Override
            + public X509Certificate[] getAcceptedIssuers()

            { + return x509ExtendedTrustManager.getAcceptedIssuers(); + }

            +
            + @Override
            + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
            + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
            + performHostnameVerification(socket.getInetAddress().getHostName(), chain[0]);

              • End diff –

          I agree that we should not do reverse DNS lookups when we do not need to.

          I changed the host verification logic to only perform a reverse dns lookup when host verification fails using the ip address. I think it is still fair to try the reverse dns lookup at that point as the value given in the altname of the certificate is not necessarily tied to the zookeeper configuration.

          Let me know if you think this is reasonable.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r110263282 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -171,21 +226,100 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke } } public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword) + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword, + boolean crlEnabled, boolean ocspEnabled, + final boolean hostnameVerificationEnabled, + final boolean shouldVerifyClientHostname) throws TrustManagerException { FileInputStream inputStream = null; try { char[] trustStorePasswordChars = trustStorePassword.toCharArray(); File trustStoreFile = new File(trustStoreLocation); KeyStore ts = KeyStore.getInstance("JKS"); inputStream = new FileInputStream(trustStoreFile); ts.load(inputStream, trustStorePasswordChars); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(ts); + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + } else { + ts.load(inputStream, null); + } for (TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509TrustManager) { return (X509TrustManager) tm; + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); + if (crlEnabled || ocspEnabled) Unknown macro: { + pbParams.setRevocationEnabled(true); + System.setProperty("com.sun.net.ssl.checkRevocation", "true"); + System.setProperty("com.sun.security.enableCRLDP", "true"); + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + } + + } else { + pbParams.setRevocationEnabled(false); + } + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(new CertPathTrustManagerParameters(pbParams)); + + for (final TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509ExtendedTrustManager) { + return new X509ExtendedTrustManager() { + X509ExtendedTrustManager x509ExtendedTrustManager = (X509ExtendedTrustManager) tm; + DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); + + @Override + public X509Certificate[] getAcceptedIssuers() { + return x509ExtendedTrustManager.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + performHostnameVerification(socket.getInetAddress().getHostName(), chain [0] ); End diff – I agree that we should not do reverse DNS lookups when we do not need to. I changed the host verification logic to only perform a reverse dns lookup when host verification fails using the ip address. I think it is still fair to try the reverse dns lookup at that point as the value given in the altname of the certificate is not necessarily tied to the zookeeper configuration. Let me know if you think this is reasonable.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 41 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/506//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/506//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/506//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 41 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/506//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/506//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/506//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 41 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/508//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/508//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/508//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 41 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/508//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/508//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/508//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 41 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/509//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/509//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/509//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 41 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. -1 findbugs. The patch appears to introduce 4 new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/509//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/509//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/509//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 41 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/519//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/519//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/519//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 41 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/519//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/519//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/519//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 41 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/520//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/520//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/520//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 41 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/520//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/520//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/520//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/521//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/521//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/521//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/521//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/521//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/521//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111483370

          — Diff: src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java —
          @@ -21,6 +21,7 @@
          import org.apache.zookeeper.ClientCnxn.EndOfStreamException;
          import org.apache.zookeeper.ClientCnxn.Packet;
          import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.common.ClientX509Util;
          import org.apache.zookeeper.common.X509Util;
          — End diff –

          Unused import.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111483370 — Diff: src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java — @@ -21,6 +21,7 @@ import org.apache.zookeeper.ClientCnxn.EndOfStreamException; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.X509Util; — End diff – Unused import.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111483464

          — Diff: src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java —
          @@ -31,6 +31,7 @@
          import javax.net.ssl.SSLSocket;
          import javax.net.ssl.SSLSocketFactory;

          +import org.apache.zookeeper.common.ClientX509Util;
          import org.apache.zookeeper.common.X509Exception.SSLContextException;
          import org.apache.zookeeper.common.X509Util;
          — End diff –

          Unused import.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111483464 — Diff: src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java — @@ -31,6 +31,7 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; +import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.common.X509Util; — End diff – Unused import.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111483819

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -18,63 +18,110 @@
          package org.apache.zookeeper.common;

          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.CertPathTrustManagerParameters;
          import javax.net.ssl.KeyManager;
          import javax.net.ssl.KeyManagerFactory;
          import javax.net.ssl.SSLContext;
          +import javax.net.ssl.SSLParameters;
          +import javax.net.ssl.SSLServerSocket;
          +import javax.net.ssl.SSLSocket;
          import javax.net.ssl.TrustManager;
          import javax.net.ssl.TrustManagerFactory;
          +import javax.net.ssl.X509ExtendedTrustManager;
          import javax.net.ssl.X509KeyManager;
          import javax.net.ssl.X509TrustManager;
          import java.io.File;
          import java.io.FileInputStream;
          import java.io.IOException;
          +import java.security.KeyManagementException;
          import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.Security;
          +import java.security.cert.PKIXBuilderParameters;
          +import java.security.cert.X509CertSelector;

          -import org.slf4j.Logger;
          -import org.slf4j.LoggerFactory;
          -
          -import static org.apache.zookeeper.common.X509Exception.KeyManagerException;
          -import static org.apache.zookeeper.common.X509Exception.SSLContextException;
          -import static org.apache.zookeeper.common.X509Exception.TrustManagerException;
          +import static org.apache.zookeeper.common.X509Exception.*;
          — End diff –

          Use explicit imports instead of wildcards imports - I guess here IDE tried to be clever..

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111483819 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -18,63 +18,110 @@ package org.apache.zookeeper.common; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.security.KeyManagementException; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.apache.zookeeper.common.X509Exception.KeyManagerException; -import static org.apache.zookeeper.common.X509Exception.SSLContextException; -import static org.apache.zookeeper.common.X509Exception.TrustManagerException; +import static org.apache.zookeeper.common.X509Exception.*; — End diff – Use explicit imports instead of wildcards imports - I guess here IDE tried to be clever..
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111484712

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -150,7 +196,7 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke
          KeyStore ks = KeyStore.getInstance("JKS");
          inputStream = new FileInputStream(keyStoreFile);
          ks.load(inputStream, keyStorePasswordChars);

          • KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
              • End diff –

          Any comments on what this change is for?

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111484712 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -150,7 +196,7 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke KeyStore ks = KeyStore.getInstance("JKS"); inputStream = new FileInputStream(keyStoreFile); ks.load(inputStream, keyStorePasswordChars); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); End diff – Any comments on what this change is for?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111485824

          — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java —
          @@ -0,0 +1,137 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + *
          + * http://www.apache.org/licenses/LICENSE-2.0
          + *
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.common;
          +
          +import org.apache.http.conn.ssl.DefaultHostnameVerifier;
          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.SSLEngine;
          +import javax.net.ssl.SSLException;
          +import javax.net.ssl.X509ExtendedTrustManager;
          +import java.net.InetAddress;
          +import java.net.Socket;
          +import java.net.UnknownHostException;
          +import java.security.cert.CertificateException;
          +import java.security.cert.X509Certificate;
          +
          +/**
          + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier.
          + *
          + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a
          + * reverse DNS lookup and verify using the hostname.
          + */
          +public class ZKTrustManager extends X509ExtendedTrustManager {
          +
          + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class);
          +
          + private X509ExtendedTrustManager x509ExtendedTrustManager;
          + private boolean hostnameVerificationEnabled;
          + private boolean shouldVerifyClientHostname;
          +
          + private DefaultHostnameVerifier hostnameVerifier;
          +
          + /**
          + * Instantiate a new ZKTrustManager.
          + *
          + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic
          + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames.
          + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client
          + * connecting to this machine will be verified in addition to the servers that this
          + * instance connects to. If false, and hostnameVerificationEnabled is true, only
          + * the hostnames of servers that this instance connects to will be verified. If
          + * hostnameVerificationEnabled is false, this argument is ignored.
          + */
          + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname)

          { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + }

          +
          + @Override
          + public X509Certificate[] getAcceptedIssuers()

          { + return x509ExtendedTrustManager.getAcceptedIssuers(); + }

          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname)

          { + performHostVerification(socket.getInetAddress(), chain[0]); + }
          + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + }

          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
          + try

          { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + }
          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled) {
          + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + }

          catch (UnknownHostException e)

          { + throw new CertificateException("failed to verify host", e); + }

          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + }

          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + }

          +
          + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException {
          + try {
          + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate);
          — End diff –

          IIUC, this only verifies that the host name matches what's stored in the certificates, right? If so, how do we verify that same subject is a valid peer we intended to connect to, rather than any random server which happens to have a valid cert? Or is this already taken care of by other parts of code?

          For reference, in ZOOKEEPER-1045 what we did for similar verification is by comparing the name of the server with the name retrieved from Kerberos principal. The name of the server is retrieved from the zoo.cfg and because of this we require if users want this verification they need encode the server as FQDN in zoo.cfg rather than using IP address. Maybe we can do something similar here.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111485824 — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java — @@ -0,0 +1,137 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.common; + +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.X509ExtendedTrustManager; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier. + * + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a + * reverse DNS lookup and verify using the hostname. + */ +public class ZKTrustManager extends X509ExtendedTrustManager { + + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class); + + private X509ExtendedTrustManager x509ExtendedTrustManager; + private boolean hostnameVerificationEnabled; + private boolean shouldVerifyClientHostname; + + private DefaultHostnameVerifier hostnameVerifier; + + /** + * Instantiate a new ZKTrustManager. + * + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames. + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client + * connecting to this machine will be verified in addition to the servers that this + * instance connects to. If false, and hostnameVerificationEnabled is true, only + * the hostnames of servers that this instance connects to will be verified. If + * hostnameVerificationEnabled is false, this argument is ignored. + */ + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname) { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return x509ExtendedTrustManager.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + } + + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException { + try { + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate); — End diff – IIUC, this only verifies that the host name matches what's stored in the certificates, right? If so, how do we verify that same subject is a valid peer we intended to connect to, rather than any random server which happens to have a valid cert? Or is this already taken care of by other parts of code? For reference, in ZOOKEEPER-1045 what we did for similar verification is by comparing the name of the server with the name retrieved from Kerberos principal. The name of the server is retrieved from the zoo.cfg and because of this we require if users want this verification they need encode the server as FQDN in zoo.cfg rather than using IP address. Maybe we can do something similar here.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111486010

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Follower.java —
          @@ -71,7 +71,7 @@ void followLeader() throws InterruptedException {
          self.end_fle = 0;
          fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean);
          try {

          • InetSocketAddress addr = findLeader();
            + InetSocketAddress addr = findLeader();
              • End diff –

          Unnecessary change here - this file can be removed from the change list.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111486010 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Follower.java — @@ -71,7 +71,7 @@ void followLeader() throws InterruptedException { self.end_fle = 0; fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean); try { InetSocketAddress addr = findLeader(); + InetSocketAddress addr = findLeader(); End diff – Unnecessary change here - this file can be removed from the change list.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111486323

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java —
          @@ -51,6 +53,8 @@
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;

          +import javax.net.ssl.SSLSocket;
          +
          — End diff –

          Looks like these imports are not used. Another look is this file can also be removed from change list because nothing changed.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111486323 — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java — @@ -51,6 +53,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.SSLSocket; + — End diff – Looks like these imports are not used. Another look is this file can also be removed from change list because nothing changed.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111486591

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java —
          @@ -383,6 +387,7 @@ public void run()

          { + leader.self.getView().get(this.sid).toString()); }

          else {
          LOG.info("Follower sid: " + this.sid + " not in the current config " + Long.toHexString(leader.self.getQuorumVerifier().getVersion()));
          + //TODO: Hostname verification possible
          — End diff –

          Suggest to remove this TODO as it appertains to no code.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111486591 — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java — @@ -383,6 +387,7 @@ public void run() { + leader.self.getView().get(this.sid).toString()); } else { LOG.info("Follower sid: " + this.sid + " not in the current config " + Long.toHexString(leader.self.getQuorumVerifier().getVersion())); + //TODO: Hostname verification possible — End diff – Suggest to remove this TODO as it appertains to no code.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111487159

          — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java —
          @@ -0,0 +1,668 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + * <p/>
          + * http://www.apache.org/licenses/LICENSE-2.0
          + * <p/>
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.test;
          +
          +import com.sun.net.httpserver.Headers;
          +import com.sun.net.httpserver.HttpHandler;
          +import com.sun.net.httpserver.HttpServer;
          +import org.apache.zookeeper.PortAssignment;
          +import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.common.QuorumX509Util;
          +import org.apache.zookeeper.server.ServerCnxnFactory;
          +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase;
          +import org.bouncycastle.asn1.ocsp.OCSPResponse;
          +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
          +import org.bouncycastle.asn1.x500.X500Name;
          +import org.bouncycastle.asn1.x500.X500NameBuilder;
          +import org.bouncycastle.asn1.x500.style.BCStyle;
          +import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
          +import org.bouncycastle.asn1.x509.BasicConstraints;
          +import org.bouncycastle.asn1.x509.CRLDistPoint;
          +import org.bouncycastle.asn1.x509.CRLNumber;
          +import org.bouncycastle.asn1.x509.CRLReason;
          +import org.bouncycastle.asn1.x509.DistributionPoint;
          +import org.bouncycastle.asn1.x509.DistributionPointName;
          +import org.bouncycastle.asn1.x509.Extension;
          +import org.bouncycastle.asn1.x509.GeneralName;
          +import org.bouncycastle.asn1.x509.GeneralNames;
          +import org.bouncycastle.asn1.x509.KeyUsage;
          +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
          +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
          +import org.bouncycastle.cert.X509CRLHolder;
          +import org.bouncycastle.cert.X509CertificateHolder;
          +import org.bouncycastle.cert.X509ExtensionUtils;
          +import org.bouncycastle.cert.X509v2CRLBuilder;
          +import org.bouncycastle.cert.X509v3CertificateBuilder;
          +import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
          +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder;
          +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
          +import org.bouncycastle.cert.ocsp.BasicOCSPResp;
          +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.CertificateID;
          +import org.bouncycastle.cert.ocsp.CertificateStatus;
          +import org.bouncycastle.cert.ocsp.OCSPException;
          +import org.bouncycastle.cert.ocsp.OCSPReq;
          +import org.bouncycastle.cert.ocsp.OCSPResp;
          +import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.Req;
          +import org.bouncycastle.cert.ocsp.UnknownStatus;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID;
          +import org.bouncycastle.crypto.util.PublicKeyFactory;
          +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
          +import org.bouncycastle.jce.provider.BouncyCastleProvider;
          +import org.bouncycastle.openssl.MiscPEMGenerator;
          +import org.bouncycastle.operator.ContentSigner;
          +import org.bouncycastle.operator.DigestCalculator;
          +import org.bouncycastle.operator.OperatorException;
          +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
          +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
          +import org.bouncycastle.util.io.pem.PemWriter;
          +import org.junit.After;
          +import org.junit.Assert;
          +import org.junit.Before;
          +import org.junit.Test;
          +
          +import java.io.FileOutputStream;
          +import java.io.FileWriter;
          +import java.io.IOException;
          +import java.io.InputStream;
          +import java.io.OutputStream;
          +import java.math.BigInteger;
          +import java.net.InetSocketAddress;
          +import java.security.KeyPair;
          +import java.security.KeyPairGenerator;
          +import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.NoSuchProviderException;
          +import java.security.PrivateKey;
          +import java.security.Security;
          +import java.security.cert.Certificate;
          +import java.security.cert.CertificateEncodingException;
          +import java.security.cert.X509Certificate;
          +import java.util.ArrayList;
          +import java.util.Calendar;
          +import java.util.Date;
          +import java.util.HashMap;
          +import java.util.List;
          +import java.util.Map;
          +import java.util.Random;
          +
          +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
          +import static org.apache.zookeeper.test.ClientBase.createTmpDir;
          +
          +public class QuorumSSLTest extends QuorumPeerTestBase {
          +
          + private static final String SSL_QUORUM_ENABLED = "sslQuorum=true\n";
          + private static final String PORT_UNIFICATION_ENABLED = "portUnification=true\n";
          + private static final String PORT_UNIFICATION_DISABLED = "portUnification=false\n";
          +
          + private static final char[] PASSWORD = "testpass".toCharArray();
          + private static final String HOSTNAME = "localhost";
          +
          + private QuorumX509Util quorumX509Util = new QuorumX509Util();
          +
          + private MainThread q1;
          + private MainThread q2;
          + private MainThread q3;
          +
          + private int clientPortQp1;
          + private int clientPortQp2;
          + private int clientPortQp3;
          +
          + private String tmpDir;
          +
          + private String quorumConfiguration;
          + private String validKeystorePath;
          + private String truststorePath;
          +
          + private KeyPair rootKeyPair;
          + private X509Certificate rootCertificate;
          +
          + private KeyPair defaultKeyPair;
          +
          + private ContentSigner contentSigner;
          +
          + @Before
          + public void setup() throws Exception

          { + ClientBase.setupTestEnv(); + + tmpDir = createTmpDir().getAbsolutePath(); + + clientPortQp1 = PortAssignment.unique(); + clientPortQp2 = PortAssignment.unique(); + clientPortQp3 = PortAssignment.unique(); + + validKeystorePath = tmpDir + "/valid.jks"; + truststorePath = tmpDir + "/truststore.jks"; + + quorumConfiguration = generateQuorumConfiguration(); + + Security.addProvider(new BouncyCastleProvider()); + + rootKeyPair = createKeyPair(); + contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(rootKeyPair.getPrivate()); + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + // Write the truststore + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, PASSWORD); + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate); + FileOutputStream outputStream = new FileOutputStream(truststorePath); + trustStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + + defaultKeyPair = createKeyPair(); + X509Certificate validCertificate = buildEndEntityCert(defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), + HOSTNAME, null, null, null); + writeKeystore(validCertificate, defaultKeyPair, validKeystorePath); + + setSSLSystemProperties(); + }

          +
          + private void writeKeystore(X509Certificate certificate, KeyPair entityKeyPair, String path) throws Exception {
          + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
          + keyStore.load(null, PASSWORD);
          + keyStore.setKeyEntry("alias", entityKeyPair.getPrivate(), PASSWORD, new Certificate[]

          { certificate }

          );
          + FileOutputStream outputStream = new FileOutputStream(path);
          + keyStore.store(outputStream, PASSWORD);
          + outputStream.flush();
          + outputStream.close();
          + }
          +
          +
          + private class OCSPHandler implements HttpHandler {
          +
          + private X509Certificate revokedCert;
          +
          + // Builds an OCSPHandler that responds with a good status for all certificates
          + // except revokedCert.
          + public OCSPHandler(X509Certificate revokedCert)

          { + this.revokedCert = revokedCert; + }

          +
          + @Override
          + public void handle(com.sun.net.httpserver.HttpExchange httpExchange) throws IOException {
          + byte[] responseBytes;
          + try {
          + InputStream request = httpExchange.getRequestBody();
          + byte[] requestBytes = new byte[10000];
          + request.read(requestBytes);
          +
          + OCSPReq ocspRequest = new OCSPReq(requestBytes);
          + Req[] requestList = ocspRequest.getRequestList();
          +
          + DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1);
          +
          + BasicOCSPRespBuilder responseBuilder = new JcaBasicOCSPRespBuilder(rootKeyPair.getPublic(), digestCalculator);
          + for ( Req req : requestList ) {
          + CertificateID certId = req.getCertID();
          + CertificateID revokedCertId = new JcaCertificateID(digestCalculator, rootCertificate, revokedCert.getSerialNumber());
          + CertificateStatus certificateStatus;
          + if (revokedCertId.equals(certId))

          { + certificateStatus = new UnknownStatus(); + }

          else

          { + certificateStatus = CertificateStatus.GOOD; + }

          +
          + responseBuilder.addResponse(certId, certificateStatus,null);
          + }
          +
          + X509CertificateHolder[] chain = new X509CertificateHolder[]

          { new JcaX509CertificateHolder(rootCertificate) }

          ;
          + ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(rootKeyPair.getPrivate());
          + BasicOCSPResp ocspResponse = responseBuilder.build(signer, chain, Calendar.getInstance().getTime() );
          +
          + responseBytes = new OCSPRespBuilder().build(OCSPRespBuilder.SUCCESSFUL, ocspResponse).getEncoded();
          + } catch (OperatorException | CertificateEncodingException | OCSPException exception)

          { + responseBytes = new OCSPResp(new OCSPResponse(new OCSPResponseStatus(OCSPRespBuilder.INTERNAL_ERROR), null)).getEncoded(); + }

          +
          + Headers rh = httpExchange.getResponseHeaders();
          + rh.set("Content-Type", "application/ocsp-response");
          + httpExchange.sendResponseHeaders(200, responseBytes.length);
          +
          + OutputStream os = httpExchange.getResponseBody();
          + os.write(responseBytes);
          + os.close();
          + }
          + }
          +
          + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception

          { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, HOSTNAME); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + }

          +
          + private void buildCRL(X509Certificate x509Certificate, String crlPath) throws Exception

          { + X509v2CRLBuilder builder = new JcaX509v2CRLBuilder(x509Certificate.getIssuerX500Principal(), new Date()); + Date notBefore = new Date(); + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + builder.setNextUpdate(notAfter); + builder.addCRLEntry(x509Certificate.getSerialNumber(), new Date(), CRLReason.cACompromise); + builder.addExtension(Extension.authorityKeyIdentifier, false, new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(rootCertificate)); + builder.addExtension(Extension.cRLNumber, false, new CRLNumber(new BigInteger("1000"))); + + X509CRLHolder cRLHolder = builder.build(contentSigner); + + PemWriter pemWriter = new PemWriter(new FileWriter(crlPath)); + pemWriter.writeObject(new MiscPEMGenerator(cRLHolder)); + pemWriter.flush(); + pemWriter.close(); + }

          +
          + public X509Certificate buildEndEntityCert(KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivateKey,
          + String hostname, String ipAddress, String crlPath, Integer ocspPort) throws Exception {
          + X509CertificateHolder holder = new JcaX509CertificateHolder(caCert);
          + ContentSigner signer =new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPrivateKey);
          +
          + List<GeneralName> generalNames = new ArrayList<>();
          + if (hostname != null)

          { + generalNames.add(new GeneralName(GeneralName.dNSName, hostname)); + }

          +
          + if (ipAddress != null)

          { + generalNames.add(new GeneralName(GeneralName.iPAddress, ipAddress)); + }

          +
          + SubjectPublicKeyInfo entityKeyInfo =
          + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(PublicKeyFactory.createKey(keyPair.getPublic().getEncoded()));
          + X509ExtensionUtils extensionUtils = new BcX509ExtensionUtils();
          + X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(holder.getSubject(), new BigInteger(128, new Random()),
          + new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + 100000),
          + new X500Name("CN=Test End Entity Certificate"), keyPair.getPublic())
          + .addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(holder))
          + .addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(entityKeyInfo))
          + .addExtension(Extension.basicConstraints, true, new BasicConstraints(false))
          + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
          +
          + if (!generalNames.isEmpty()) {
          + certificateBuilder.addExtension(Extension.subjectAlternativeName, true, new GeneralNames(generalNames.toArray(new GeneralName[] {})));
          + }
          +
          + if (crlPath != null) {
          + DistributionPointName distPointOne = new DistributionPointName(new GeneralNames(
          + new GeneralName(GeneralName.uniformResourceIdentifier,"file://" + crlPath)));
          +
          + certificateBuilder.addExtension(Extension.cRLDistributionPoints, false,
          + new CRLDistPoint(new DistributionPoint[]

          { new DistributionPoint(distPointOne, null, null) }

          ));
          + }
          +
          + if (ocspPort != null)

          { + certificateBuilder.addExtension(Extension.authorityInfoAccess, false, new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, + new GeneralName(GeneralName.uniformResourceIdentifier, "http://" + hostname + ":" + ocspPort))); + }

          +
          + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(signer));
          + }
          +
          +
          + private KeyPair createKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException

          { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + return keyPair; + }

          +
          + private String generateQuorumConfiguration()

          { + int portQp1 = PortAssignment.unique(); + int portQp2 = PortAssignment.unique(); + int portQp3 = PortAssignment.unique(); + + int portLe1 = PortAssignment.unique(); + int portLe2 = PortAssignment.unique(); + int portLe3 = PortAssignment.unique(); + + + + return "server.1=127.0.0.1:" + (portQp1) + ":" + (portLe1) + ";" + clientPortQp1 + "\n" + + "server.2=127.0.0.1:" + (portQp2) + ":" + (portLe2) + ";" + clientPortQp2 + "\n" + + "server.3=127.0.0.1:" + (portQp3) + ":" + (portLe3) + ";" + clientPortQp3; + }

          +
          +
          + public void setSSLSystemProperties()

          { + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), validKeystorePath); + System.setProperty(quorumX509Util.getSslKeystorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(quorumX509Util.getSslTruststorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty(), "false"); + }

          +
          + @After
          + public void cleanUp() throws Exception

          { + clearSSLSystemProperties(); + q1.shutdown(); + q2.shutdown(); + q3.shutdown(); + + Security.removeProvider("BC"); + }

          +
          + private void clearSSLSystemProperties()

          { + System.clearProperty(quorumX509Util.getSslKeystoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslKeystorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslTruststoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslTruststorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(quorumX509Util.getSslOcspEnabledProperty()); + System.clearProperty(quorumX509Util.getSslCrlEnabledProperty()); + }

          +
          + @Test(timeout = 300000)
          + public void testQuorumSSL() throws Exception

          { + q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); + q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); + + + q1.start(); + q2.start(); + + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); + + clearSSLSystemProperties(); + + // This server should fail to join the quorum as it is not using ssl. + q3 = new MainThread(3, clientPortQp3, quorumConfiguration); + q3.start(); + + Assert.assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); + }

          +
          + @Test(timeout = 300000)
          + public void testRollingUpgrade() throws Exception {
          — End diff –

          This test is pretty flaky - please check internal Jenkins to diagnose.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111487159 — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java — @@ -0,0 +1,668 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.test; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.common.QuorumX509Util; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.bouncycastle.asn1.ocsp.OCSPResponse; +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.CRLNumber; +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509ExtensionUtils; +import org.bouncycastle.cert.X509v2CRLBuilder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.cert.ocsp.BasicOCSPResp; +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.CertificateStatus; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.ocsp.OCSPRespBuilder; +import org.bouncycastle.cert.ocsp.Req; +import org.bouncycastle.cert.ocsp.UnknownStatus; +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.MiscPEMGenerator; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.util.io.pem.PemWriter; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import static org.apache.zookeeper.test.ClientBase.createTmpDir; + +public class QuorumSSLTest extends QuorumPeerTestBase { + + private static final String SSL_QUORUM_ENABLED = "sslQuorum=true\n"; + private static final String PORT_UNIFICATION_ENABLED = "portUnification=true\n"; + private static final String PORT_UNIFICATION_DISABLED = "portUnification=false\n"; + + private static final char[] PASSWORD = "testpass".toCharArray(); + private static final String HOSTNAME = "localhost"; + + private QuorumX509Util quorumX509Util = new QuorumX509Util(); + + private MainThread q1; + private MainThread q2; + private MainThread q3; + + private int clientPortQp1; + private int clientPortQp2; + private int clientPortQp3; + + private String tmpDir; + + private String quorumConfiguration; + private String validKeystorePath; + private String truststorePath; + + private KeyPair rootKeyPair; + private X509Certificate rootCertificate; + + private KeyPair defaultKeyPair; + + private ContentSigner contentSigner; + + @Before + public void setup() throws Exception { + ClientBase.setupTestEnv(); + + tmpDir = createTmpDir().getAbsolutePath(); + + clientPortQp1 = PortAssignment.unique(); + clientPortQp2 = PortAssignment.unique(); + clientPortQp3 = PortAssignment.unique(); + + validKeystorePath = tmpDir + "/valid.jks"; + truststorePath = tmpDir + "/truststore.jks"; + + quorumConfiguration = generateQuorumConfiguration(); + + Security.addProvider(new BouncyCastleProvider()); + + rootKeyPair = createKeyPair(); + contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(rootKeyPair.getPrivate()); + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + // Write the truststore + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, PASSWORD); + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate); + FileOutputStream outputStream = new FileOutputStream(truststorePath); + trustStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + + defaultKeyPair = createKeyPair(); + X509Certificate validCertificate = buildEndEntityCert(defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), + HOSTNAME, null, null, null); + writeKeystore(validCertificate, defaultKeyPair, validKeystorePath); + + setSSLSystemProperties(); + } + + private void writeKeystore(X509Certificate certificate, KeyPair entityKeyPair, String path) throws Exception { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, PASSWORD); + keyStore.setKeyEntry("alias", entityKeyPair.getPrivate(), PASSWORD, new Certificate[] { certificate } ); + FileOutputStream outputStream = new FileOutputStream(path); + keyStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + } + + + private class OCSPHandler implements HttpHandler { + + private X509Certificate revokedCert; + + // Builds an OCSPHandler that responds with a good status for all certificates + // except revokedCert. + public OCSPHandler(X509Certificate revokedCert) { + this.revokedCert = revokedCert; + } + + @Override + public void handle(com.sun.net.httpserver.HttpExchange httpExchange) throws IOException { + byte[] responseBytes; + try { + InputStream request = httpExchange.getRequestBody(); + byte[] requestBytes = new byte [10000] ; + request.read(requestBytes); + + OCSPReq ocspRequest = new OCSPReq(requestBytes); + Req[] requestList = ocspRequest.getRequestList(); + + DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1); + + BasicOCSPRespBuilder responseBuilder = new JcaBasicOCSPRespBuilder(rootKeyPair.getPublic(), digestCalculator); + for ( Req req : requestList ) { + CertificateID certId = req.getCertID(); + CertificateID revokedCertId = new JcaCertificateID(digestCalculator, rootCertificate, revokedCert.getSerialNumber()); + CertificateStatus certificateStatus; + if (revokedCertId.equals(certId)) { + certificateStatus = new UnknownStatus(); + } else { + certificateStatus = CertificateStatus.GOOD; + } + + responseBuilder.addResponse(certId, certificateStatus,null); + } + + X509CertificateHolder[] chain = new X509CertificateHolder[] { new JcaX509CertificateHolder(rootCertificate) } ; + ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(rootKeyPair.getPrivate()); + BasicOCSPResp ocspResponse = responseBuilder.build(signer, chain, Calendar.getInstance().getTime() ); + + responseBytes = new OCSPRespBuilder().build(OCSPRespBuilder.SUCCESSFUL, ocspResponse).getEncoded(); + } catch (OperatorException | CertificateEncodingException | OCSPException exception) { + responseBytes = new OCSPResp(new OCSPResponse(new OCSPResponseStatus(OCSPRespBuilder.INTERNAL_ERROR), null)).getEncoded(); + } + + Headers rh = httpExchange.getResponseHeaders(); + rh.set("Content-Type", "application/ocsp-response"); + httpExchange.sendResponseHeaders(200, responseBytes.length); + + OutputStream os = httpExchange.getResponseBody(); + os.write(responseBytes); + os.close(); + } + } + + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, HOSTNAME); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + } + + private void buildCRL(X509Certificate x509Certificate, String crlPath) throws Exception { + X509v2CRLBuilder builder = new JcaX509v2CRLBuilder(x509Certificate.getIssuerX500Principal(), new Date()); + Date notBefore = new Date(); + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + builder.setNextUpdate(notAfter); + builder.addCRLEntry(x509Certificate.getSerialNumber(), new Date(), CRLReason.cACompromise); + builder.addExtension(Extension.authorityKeyIdentifier, false, new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(rootCertificate)); + builder.addExtension(Extension.cRLNumber, false, new CRLNumber(new BigInteger("1000"))); + + X509CRLHolder cRLHolder = builder.build(contentSigner); + + PemWriter pemWriter = new PemWriter(new FileWriter(crlPath)); + pemWriter.writeObject(new MiscPEMGenerator(cRLHolder)); + pemWriter.flush(); + pemWriter.close(); + } + + public X509Certificate buildEndEntityCert(KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivateKey, + String hostname, String ipAddress, String crlPath, Integer ocspPort) throws Exception { + X509CertificateHolder holder = new JcaX509CertificateHolder(caCert); + ContentSigner signer =new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPrivateKey); + + List<GeneralName> generalNames = new ArrayList<>(); + if (hostname != null) { + generalNames.add(new GeneralName(GeneralName.dNSName, hostname)); + } + + if (ipAddress != null) { + generalNames.add(new GeneralName(GeneralName.iPAddress, ipAddress)); + } + + SubjectPublicKeyInfo entityKeyInfo = + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(PublicKeyFactory.createKey(keyPair.getPublic().getEncoded())); + X509ExtensionUtils extensionUtils = new BcX509ExtensionUtils(); + X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(holder.getSubject(), new BigInteger(128, new Random()), + new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + 100000), + new X500Name("CN=Test End Entity Certificate"), keyPair.getPublic()) + .addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(holder)) + .addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(entityKeyInfo)) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(false)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)); + + if (!generalNames.isEmpty()) { + certificateBuilder.addExtension(Extension.subjectAlternativeName, true, new GeneralNames(generalNames.toArray(new GeneralName[] {}))); + } + + if (crlPath != null) { + DistributionPointName distPointOne = new DistributionPointName(new GeneralNames( + new GeneralName(GeneralName.uniformResourceIdentifier,"file://" + crlPath))); + + certificateBuilder.addExtension(Extension.cRLDistributionPoints, false, + new CRLDistPoint(new DistributionPoint[] { new DistributionPoint(distPointOne, null, null) } )); + } + + if (ocspPort != null) { + certificateBuilder.addExtension(Extension.authorityInfoAccess, false, new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, + new GeneralName(GeneralName.uniformResourceIdentifier, "http://" + hostname + ":" + ocspPort))); + } + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(signer)); + } + + + private KeyPair createKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + return keyPair; + } + + private String generateQuorumConfiguration() { + int portQp1 = PortAssignment.unique(); + int portQp2 = PortAssignment.unique(); + int portQp3 = PortAssignment.unique(); + + int portLe1 = PortAssignment.unique(); + int portLe2 = PortAssignment.unique(); + int portLe3 = PortAssignment.unique(); + + + + return "server.1=127.0.0.1:" + (portQp1) + ":" + (portLe1) + ";" + clientPortQp1 + "\n" + + "server.2=127.0.0.1:" + (portQp2) + ":" + (portLe2) + ";" + clientPortQp2 + "\n" + + "server.3=127.0.0.1:" + (portQp3) + ":" + (portLe3) + ";" + clientPortQp3; + } + + + public void setSSLSystemProperties() { + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), validKeystorePath); + System.setProperty(quorumX509Util.getSslKeystorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(quorumX509Util.getSslTruststorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty(), "false"); + } + + @After + public void cleanUp() throws Exception { + clearSSLSystemProperties(); + q1.shutdown(); + q2.shutdown(); + q3.shutdown(); + + Security.removeProvider("BC"); + } + + private void clearSSLSystemProperties() { + System.clearProperty(quorumX509Util.getSslKeystoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslKeystorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslTruststoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslTruststorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(quorumX509Util.getSslOcspEnabledProperty()); + System.clearProperty(quorumX509Util.getSslCrlEnabledProperty()); + } + + @Test(timeout = 300000) + public void testQuorumSSL() throws Exception { + q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); + q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); + + + q1.start(); + q2.start(); + + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); + + clearSSLSystemProperties(); + + // This server should fail to join the quorum as it is not using ssl. + q3 = new MainThread(3, clientPortQp3, quorumConfiguration); + q3.start(); + + Assert.assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); + } + + @Test(timeout = 300000) + public void testRollingUpgrade() throws Exception { — End diff – This test is pretty flaky - please check internal Jenkins to diagnose.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/543//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/543//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/543//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/543//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/543//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/543//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111603427

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -150,7 +196,7 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke
          KeyStore ks = KeyStore.getInstance("JKS");
          inputStream = new FileInputStream(keyStoreFile);
          ks.load(inputStream, keyStorePasswordChars);

          • KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
              • End diff –

          A comment has been added where pkix is used for the trustmanager factory. We don't actually need this change for the keymanager, but i preferred to be consistent.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111603427 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -150,7 +196,7 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke KeyStore ks = KeyStore.getInstance("JKS"); inputStream = new FileInputStream(keyStoreFile); ks.load(inputStream, keyStorePasswordChars); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); End diff – A comment has been added where pkix is used for the trustmanager factory. We don't actually need this change for the keymanager, but i preferred to be consistent.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111604664

          — Diff: src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java —
          @@ -21,6 +21,7 @@
          import org.apache.zookeeper.ClientCnxn.EndOfStreamException;
          import org.apache.zookeeper.ClientCnxn.Packet;
          import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.common.ClientX509Util;
          import org.apache.zookeeper.common.X509Util;
          — End diff –

          fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111604664 — Diff: src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java — @@ -21,6 +21,7 @@ import org.apache.zookeeper.ClientCnxn.EndOfStreamException; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.X509Util; — End diff – fixed
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111604682

          — Diff: src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java —
          @@ -31,6 +31,7 @@
          import javax.net.ssl.SSLSocket;
          import javax.net.ssl.SSLSocketFactory;

          +import org.apache.zookeeper.common.ClientX509Util;
          import org.apache.zookeeper.common.X509Exception.SSLContextException;
          import org.apache.zookeeper.common.X509Util;
          — End diff –

          fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111604682 — Diff: src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java — @@ -31,6 +31,7 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; +import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.common.X509Util; — End diff – fixed
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111605341

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java —
          @@ -383,6 +387,7 @@ public void run()

          { + leader.self.getView().get(this.sid).toString()); }

          else {
          LOG.info("Follower sid: " + this.sid + " not in the current config " + Long.toHexString(leader.self.getQuorumVerifier().getVersion()));
          + //TODO: Hostname verification possible
          — End diff –

          fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111605341 — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java — @@ -383,6 +387,7 @@ public void run() { + leader.self.getView().get(this.sid).toString()); } else { LOG.info("Follower sid: " + this.sid + " not in the current config " + Long.toHexString(leader.self.getQuorumVerifier().getVersion())); + //TODO: Hostname verification possible — End diff – fixed
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111605399

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java —
          @@ -51,6 +53,8 @@
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;

          +import javax.net.ssl.SSLSocket;
          +
          — End diff –

          fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111605399 — Diff: src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java — @@ -51,6 +53,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.SSLSocket; + — End diff – fixed
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111605424

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Follower.java —
          @@ -71,7 +71,7 @@ void followLeader() throws InterruptedException {
          self.end_fle = 0;
          fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean);
          try {

          • InetSocketAddress addr = findLeader();
            + InetSocketAddress addr = findLeader();
              • End diff –

          fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111605424 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Follower.java — @@ -71,7 +71,7 @@ void followLeader() throws InterruptedException { self.end_fle = 0; fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean); try { InetSocketAddress addr = findLeader(); + InetSocketAddress addr = findLeader(); End diff – fixed
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111605464

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -18,63 +18,110 @@
          package org.apache.zookeeper.common;

          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.CertPathTrustManagerParameters;
          import javax.net.ssl.KeyManager;
          import javax.net.ssl.KeyManagerFactory;
          import javax.net.ssl.SSLContext;
          +import javax.net.ssl.SSLParameters;
          +import javax.net.ssl.SSLServerSocket;
          +import javax.net.ssl.SSLSocket;
          import javax.net.ssl.TrustManager;
          import javax.net.ssl.TrustManagerFactory;
          +import javax.net.ssl.X509ExtendedTrustManager;
          import javax.net.ssl.X509KeyManager;
          import javax.net.ssl.X509TrustManager;
          import java.io.File;
          import java.io.FileInputStream;
          import java.io.IOException;
          +import java.security.KeyManagementException;
          import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.Security;
          +import java.security.cert.PKIXBuilderParameters;
          +import java.security.cert.X509CertSelector;

          -import org.slf4j.Logger;
          -import org.slf4j.LoggerFactory;
          -
          -import static org.apache.zookeeper.common.X509Exception.KeyManagerException;
          -import static org.apache.zookeeper.common.X509Exception.SSLContextException;
          -import static org.apache.zookeeper.common.X509Exception.TrustManagerException;
          +import static org.apache.zookeeper.common.X509Exception.*;
          — End diff –

          fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111605464 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -18,63 +18,110 @@ package org.apache.zookeeper.common; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.security.KeyManagementException; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.apache.zookeeper.common.X509Exception.KeyManagerException; -import static org.apache.zookeeper.common.X509Exception.SSLContextException; -import static org.apache.zookeeper.common.X509Exception.TrustManagerException; +import static org.apache.zookeeper.common.X509Exception.*; — End diff – fixed
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/544//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/544//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/544//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/544//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/544//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/544//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111612166

          — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java —
          @@ -0,0 +1,137 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + *
          + * http://www.apache.org/licenses/LICENSE-2.0
          + *
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.common;
          +
          +import org.apache.http.conn.ssl.DefaultHostnameVerifier;
          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.SSLEngine;
          +import javax.net.ssl.SSLException;
          +import javax.net.ssl.X509ExtendedTrustManager;
          +import java.net.InetAddress;
          +import java.net.Socket;
          +import java.net.UnknownHostException;
          +import java.security.cert.CertificateException;
          +import java.security.cert.X509Certificate;
          +
          +/**
          + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier.
          + *
          + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a
          + * reverse DNS lookup and verify using the hostname.
          + */
          +public class ZKTrustManager extends X509ExtendedTrustManager {
          +
          + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class);
          +
          + private X509ExtendedTrustManager x509ExtendedTrustManager;
          + private boolean hostnameVerificationEnabled;
          + private boolean shouldVerifyClientHostname;
          +
          + private DefaultHostnameVerifier hostnameVerifier;
          +
          + /**
          + * Instantiate a new ZKTrustManager.
          + *
          + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic
          + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames.
          + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client
          + * connecting to this machine will be verified in addition to the servers that this
          + * instance connects to. If false, and hostnameVerificationEnabled is true, only
          + * the hostnames of servers that this instance connects to will be verified. If
          + * hostnameVerificationEnabled is false, this argument is ignored.
          + */
          + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname)

          { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + }

          +
          + @Override
          + public X509Certificate[] getAcceptedIssuers()

          { + return x509ExtendedTrustManager.getAcceptedIssuers(); + }

          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname)

          { + performHostVerification(socket.getInetAddress(), chain[0]); + }
          + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + }

          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
          + try

          { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + }
          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled) {
          + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + }

          catch (UnknownHostException e)

          { + throw new CertificateException("failed to verify host", e); + }

          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + }

          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + }

          +
          + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException {
          + try {
          + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate);
          — End diff –

          So we have a few hostname checks that we could consider. Please let me know if I am missing any.

          1. Check that the address/hostname of the machine matches what is listed on the certificate
          2. Check that the address/hostname of the machine matches what is expected from zoo.cfg (this may also require reading the sid from the packet sent when a connection is established).
          3. Check that the address/hostname listed on the certificate matches what is expected from zoo.cfg

          This patch only performs check 1. I think that check 3 is something @geek101 mentioned earlier and we agreed this can be moved to another patch as it is more complicated.

          I think it is also possible to implement check 2. This can exist entirely outside of tls/ssl support and is something we should consider based on its own merits.

          I think having checks 1 and 2 would be plenty secure and we would not need check 3.

          Let me know what you think @hanm.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111612166 — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java — @@ -0,0 +1,137 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.common; + +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.X509ExtendedTrustManager; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier. + * + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a + * reverse DNS lookup and verify using the hostname. + */ +public class ZKTrustManager extends X509ExtendedTrustManager { + + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class); + + private X509ExtendedTrustManager x509ExtendedTrustManager; + private boolean hostnameVerificationEnabled; + private boolean shouldVerifyClientHostname; + + private DefaultHostnameVerifier hostnameVerifier; + + /** + * Instantiate a new ZKTrustManager. + * + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames. + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client + * connecting to this machine will be verified in addition to the servers that this + * instance connects to. If false, and hostnameVerificationEnabled is true, only + * the hostnames of servers that this instance connects to will be verified. If + * hostnameVerificationEnabled is false, this argument is ignored. + */ + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname) { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return x509ExtendedTrustManager.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + } + + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException { + try { + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate); — End diff – So we have a few hostname checks that we could consider. Please let me know if I am missing any. 1. Check that the address/hostname of the machine matches what is listed on the certificate 2. Check that the address/hostname of the machine matches what is expected from zoo.cfg (this may also require reading the sid from the packet sent when a connection is established). 3. Check that the address/hostname listed on the certificate matches what is expected from zoo.cfg This patch only performs check 1. I think that check 3 is something @geek101 mentioned earlier and we agreed this can be moved to another patch as it is more complicated. I think it is also possible to implement check 2. This can exist entirely outside of tls/ssl support and is something we should consider based on its own merits. I think having checks 1 and 2 would be plenty secure and we would not need check 3. Let me know what you think @hanm.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111613841

          — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java —
          @@ -0,0 +1,137 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + *
          + * http://www.apache.org/licenses/LICENSE-2.0
          + *
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.common;
          +
          +import org.apache.http.conn.ssl.DefaultHostnameVerifier;
          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.SSLEngine;
          +import javax.net.ssl.SSLException;
          +import javax.net.ssl.X509ExtendedTrustManager;
          +import java.net.InetAddress;
          +import java.net.Socket;
          +import java.net.UnknownHostException;
          +import java.security.cert.CertificateException;
          +import java.security.cert.X509Certificate;
          +
          +/**
          + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier.
          + *
          + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a
          + * reverse DNS lookup and verify using the hostname.
          + */
          +public class ZKTrustManager extends X509ExtendedTrustManager {
          +
          + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class);
          +
          + private X509ExtendedTrustManager x509ExtendedTrustManager;
          + private boolean hostnameVerificationEnabled;
          + private boolean shouldVerifyClientHostname;
          +
          + private DefaultHostnameVerifier hostnameVerifier;
          +
          + /**
          + * Instantiate a new ZKTrustManager.
          + *
          + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic
          + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames.
          + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client
          + * connecting to this machine will be verified in addition to the servers that this
          + * instance connects to. If false, and hostnameVerificationEnabled is true, only
          + * the hostnames of servers that this instance connects to will be verified. If
          + * hostnameVerificationEnabled is false, this argument is ignored.
          + */
          + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname)

          { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + }

          +
          + @Override
          + public X509Certificate[] getAcceptedIssuers()

          { + return x509ExtendedTrustManager.getAcceptedIssuers(); + }

          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname)

          { + performHostVerification(socket.getInetAddress(), chain[0]); + }
          + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + }

          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
          + try

          { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + }
          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled) {
          + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + }

          catch (UnknownHostException e)

          { + throw new CertificateException("failed to verify host", e); + }

          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + }

          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + }

          +
          + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException {
          + try {
          + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate);
          — End diff –

          check 1 and check 2 together do what check 3 does, no? So I think 1+2 is enough. The point is to make sure that:

          • The peer is what it claims to be (check 1 - authentication).
          • The peer is authorized to join quorum (check 2 - authorization).
          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111613841 — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java — @@ -0,0 +1,137 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.common; + +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.X509ExtendedTrustManager; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier. + * + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a + * reverse DNS lookup and verify using the hostname. + */ +public class ZKTrustManager extends X509ExtendedTrustManager { + + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class); + + private X509ExtendedTrustManager x509ExtendedTrustManager; + private boolean hostnameVerificationEnabled; + private boolean shouldVerifyClientHostname; + + private DefaultHostnameVerifier hostnameVerifier; + + /** + * Instantiate a new ZKTrustManager. + * + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames. + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client + * connecting to this machine will be verified in addition to the servers that this + * instance connects to. If false, and hostnameVerificationEnabled is true, only + * the hostnames of servers that this instance connects to will be verified. If + * hostnameVerificationEnabled is false, this argument is ignored. + */ + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname) { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return x509ExtendedTrustManager.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + } + + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException { + try { + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate); — End diff – check 1 and check 2 together do what check 3 does, no? So I think 1+2 is enough. The point is to make sure that: The peer is what it claims to be (check 1 - authentication). The peer is authorized to join quorum (check 2 - authorization).
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/547//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/547//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/547//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/547//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/547//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/547//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r111656663

          — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java —
          @@ -0,0 +1,668 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + * <p/>
          + * http://www.apache.org/licenses/LICENSE-2.0
          + * <p/>
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.test;
          +
          +import com.sun.net.httpserver.Headers;
          +import com.sun.net.httpserver.HttpHandler;
          +import com.sun.net.httpserver.HttpServer;
          +import org.apache.zookeeper.PortAssignment;
          +import org.apache.zookeeper.client.ZKClientConfig;
          +import org.apache.zookeeper.common.QuorumX509Util;
          +import org.apache.zookeeper.server.ServerCnxnFactory;
          +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase;
          +import org.bouncycastle.asn1.ocsp.OCSPResponse;
          +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
          +import org.bouncycastle.asn1.x500.X500Name;
          +import org.bouncycastle.asn1.x500.X500NameBuilder;
          +import org.bouncycastle.asn1.x500.style.BCStyle;
          +import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
          +import org.bouncycastle.asn1.x509.BasicConstraints;
          +import org.bouncycastle.asn1.x509.CRLDistPoint;
          +import org.bouncycastle.asn1.x509.CRLNumber;
          +import org.bouncycastle.asn1.x509.CRLReason;
          +import org.bouncycastle.asn1.x509.DistributionPoint;
          +import org.bouncycastle.asn1.x509.DistributionPointName;
          +import org.bouncycastle.asn1.x509.Extension;
          +import org.bouncycastle.asn1.x509.GeneralName;
          +import org.bouncycastle.asn1.x509.GeneralNames;
          +import org.bouncycastle.asn1.x509.KeyUsage;
          +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
          +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
          +import org.bouncycastle.cert.X509CRLHolder;
          +import org.bouncycastle.cert.X509CertificateHolder;
          +import org.bouncycastle.cert.X509ExtensionUtils;
          +import org.bouncycastle.cert.X509v2CRLBuilder;
          +import org.bouncycastle.cert.X509v3CertificateBuilder;
          +import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
          +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
          +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
          +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder;
          +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
          +import org.bouncycastle.cert.ocsp.BasicOCSPResp;
          +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.CertificateID;
          +import org.bouncycastle.cert.ocsp.CertificateStatus;
          +import org.bouncycastle.cert.ocsp.OCSPException;
          +import org.bouncycastle.cert.ocsp.OCSPReq;
          +import org.bouncycastle.cert.ocsp.OCSPResp;
          +import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.Req;
          +import org.bouncycastle.cert.ocsp.UnknownStatus;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder;
          +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID;
          +import org.bouncycastle.crypto.util.PublicKeyFactory;
          +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
          +import org.bouncycastle.jce.provider.BouncyCastleProvider;
          +import org.bouncycastle.openssl.MiscPEMGenerator;
          +import org.bouncycastle.operator.ContentSigner;
          +import org.bouncycastle.operator.DigestCalculator;
          +import org.bouncycastle.operator.OperatorException;
          +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
          +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
          +import org.bouncycastle.util.io.pem.PemWriter;
          +import org.junit.After;
          +import org.junit.Assert;
          +import org.junit.Before;
          +import org.junit.Test;
          +
          +import java.io.FileOutputStream;
          +import java.io.FileWriter;
          +import java.io.IOException;
          +import java.io.InputStream;
          +import java.io.OutputStream;
          +import java.math.BigInteger;
          +import java.net.InetSocketAddress;
          +import java.security.KeyPair;
          +import java.security.KeyPairGenerator;
          +import java.security.KeyStore;
          +import java.security.NoSuchAlgorithmException;
          +import java.security.NoSuchProviderException;
          +import java.security.PrivateKey;
          +import java.security.Security;
          +import java.security.cert.Certificate;
          +import java.security.cert.CertificateEncodingException;
          +import java.security.cert.X509Certificate;
          +import java.util.ArrayList;
          +import java.util.Calendar;
          +import java.util.Date;
          +import java.util.HashMap;
          +import java.util.List;
          +import java.util.Map;
          +import java.util.Random;
          +
          +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
          +import static org.apache.zookeeper.test.ClientBase.createTmpDir;
          +
          +public class QuorumSSLTest extends QuorumPeerTestBase {
          +
          + private static final String SSL_QUORUM_ENABLED = "sslQuorum=true\n";
          + private static final String PORT_UNIFICATION_ENABLED = "portUnification=true\n";
          + private static final String PORT_UNIFICATION_DISABLED = "portUnification=false\n";
          +
          + private static final char[] PASSWORD = "testpass".toCharArray();
          + private static final String HOSTNAME = "localhost";
          +
          + private QuorumX509Util quorumX509Util = new QuorumX509Util();
          +
          + private MainThread q1;
          + private MainThread q2;
          + private MainThread q3;
          +
          + private int clientPortQp1;
          + private int clientPortQp2;
          + private int clientPortQp3;
          +
          + private String tmpDir;
          +
          + private String quorumConfiguration;
          + private String validKeystorePath;
          + private String truststorePath;
          +
          + private KeyPair rootKeyPair;
          + private X509Certificate rootCertificate;
          +
          + private KeyPair defaultKeyPair;
          +
          + private ContentSigner contentSigner;
          +
          + @Before
          + public void setup() throws Exception

          { + ClientBase.setupTestEnv(); + + tmpDir = createTmpDir().getAbsolutePath(); + + clientPortQp1 = PortAssignment.unique(); + clientPortQp2 = PortAssignment.unique(); + clientPortQp3 = PortAssignment.unique(); + + validKeystorePath = tmpDir + "/valid.jks"; + truststorePath = tmpDir + "/truststore.jks"; + + quorumConfiguration = generateQuorumConfiguration(); + + Security.addProvider(new BouncyCastleProvider()); + + rootKeyPair = createKeyPair(); + contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(rootKeyPair.getPrivate()); + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + // Write the truststore + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, PASSWORD); + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate); + FileOutputStream outputStream = new FileOutputStream(truststorePath); + trustStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + + defaultKeyPair = createKeyPair(); + X509Certificate validCertificate = buildEndEntityCert(defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), + HOSTNAME, null, null, null); + writeKeystore(validCertificate, defaultKeyPair, validKeystorePath); + + setSSLSystemProperties(); + }

          +
          + private void writeKeystore(X509Certificate certificate, KeyPair entityKeyPair, String path) throws Exception {
          + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
          + keyStore.load(null, PASSWORD);
          + keyStore.setKeyEntry("alias", entityKeyPair.getPrivate(), PASSWORD, new Certificate[]

          { certificate }

          );
          + FileOutputStream outputStream = new FileOutputStream(path);
          + keyStore.store(outputStream, PASSWORD);
          + outputStream.flush();
          + outputStream.close();
          + }
          +
          +
          + private class OCSPHandler implements HttpHandler {
          +
          + private X509Certificate revokedCert;
          +
          + // Builds an OCSPHandler that responds with a good status for all certificates
          + // except revokedCert.
          + public OCSPHandler(X509Certificate revokedCert)

          { + this.revokedCert = revokedCert; + }

          +
          + @Override
          + public void handle(com.sun.net.httpserver.HttpExchange httpExchange) throws IOException {
          + byte[] responseBytes;
          + try {
          + InputStream request = httpExchange.getRequestBody();
          + byte[] requestBytes = new byte[10000];
          + request.read(requestBytes);
          +
          + OCSPReq ocspRequest = new OCSPReq(requestBytes);
          + Req[] requestList = ocspRequest.getRequestList();
          +
          + DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1);
          +
          + BasicOCSPRespBuilder responseBuilder = new JcaBasicOCSPRespBuilder(rootKeyPair.getPublic(), digestCalculator);
          + for ( Req req : requestList ) {
          + CertificateID certId = req.getCertID();
          + CertificateID revokedCertId = new JcaCertificateID(digestCalculator, rootCertificate, revokedCert.getSerialNumber());
          + CertificateStatus certificateStatus;
          + if (revokedCertId.equals(certId))

          { + certificateStatus = new UnknownStatus(); + }

          else

          { + certificateStatus = CertificateStatus.GOOD; + }

          +
          + responseBuilder.addResponse(certId, certificateStatus,null);
          + }
          +
          + X509CertificateHolder[] chain = new X509CertificateHolder[]

          { new JcaX509CertificateHolder(rootCertificate) }

          ;
          + ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(rootKeyPair.getPrivate());
          + BasicOCSPResp ocspResponse = responseBuilder.build(signer, chain, Calendar.getInstance().getTime() );
          +
          + responseBytes = new OCSPRespBuilder().build(OCSPRespBuilder.SUCCESSFUL, ocspResponse).getEncoded();
          + } catch (OperatorException | CertificateEncodingException | OCSPException exception)

          { + responseBytes = new OCSPResp(new OCSPResponse(new OCSPResponseStatus(OCSPRespBuilder.INTERNAL_ERROR), null)).getEncoded(); + }

          +
          + Headers rh = httpExchange.getResponseHeaders();
          + rh.set("Content-Type", "application/ocsp-response");
          + httpExchange.sendResponseHeaders(200, responseBytes.length);
          +
          + OutputStream os = httpExchange.getResponseBody();
          + os.write(responseBytes);
          + os.close();
          + }
          + }
          +
          + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception

          { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, HOSTNAME); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + }

          +
          + private void buildCRL(X509Certificate x509Certificate, String crlPath) throws Exception

          { + X509v2CRLBuilder builder = new JcaX509v2CRLBuilder(x509Certificate.getIssuerX500Principal(), new Date()); + Date notBefore = new Date(); + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + builder.setNextUpdate(notAfter); + builder.addCRLEntry(x509Certificate.getSerialNumber(), new Date(), CRLReason.cACompromise); + builder.addExtension(Extension.authorityKeyIdentifier, false, new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(rootCertificate)); + builder.addExtension(Extension.cRLNumber, false, new CRLNumber(new BigInteger("1000"))); + + X509CRLHolder cRLHolder = builder.build(contentSigner); + + PemWriter pemWriter = new PemWriter(new FileWriter(crlPath)); + pemWriter.writeObject(new MiscPEMGenerator(cRLHolder)); + pemWriter.flush(); + pemWriter.close(); + }

          +
          + public X509Certificate buildEndEntityCert(KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivateKey,
          + String hostname, String ipAddress, String crlPath, Integer ocspPort) throws Exception {
          + X509CertificateHolder holder = new JcaX509CertificateHolder(caCert);
          + ContentSigner signer =new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPrivateKey);
          +
          + List<GeneralName> generalNames = new ArrayList<>();
          + if (hostname != null)

          { + generalNames.add(new GeneralName(GeneralName.dNSName, hostname)); + }

          +
          + if (ipAddress != null)

          { + generalNames.add(new GeneralName(GeneralName.iPAddress, ipAddress)); + }

          +
          + SubjectPublicKeyInfo entityKeyInfo =
          + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(PublicKeyFactory.createKey(keyPair.getPublic().getEncoded()));
          + X509ExtensionUtils extensionUtils = new BcX509ExtensionUtils();
          + X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(holder.getSubject(), new BigInteger(128, new Random()),
          + new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + 100000),
          + new X500Name("CN=Test End Entity Certificate"), keyPair.getPublic())
          + .addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(holder))
          + .addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(entityKeyInfo))
          + .addExtension(Extension.basicConstraints, true, new BasicConstraints(false))
          + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
          +
          + if (!generalNames.isEmpty()) {
          + certificateBuilder.addExtension(Extension.subjectAlternativeName, true, new GeneralNames(generalNames.toArray(new GeneralName[] {})));
          + }
          +
          + if (crlPath != null) {
          + DistributionPointName distPointOne = new DistributionPointName(new GeneralNames(
          + new GeneralName(GeneralName.uniformResourceIdentifier,"file://" + crlPath)));
          +
          + certificateBuilder.addExtension(Extension.cRLDistributionPoints, false,
          + new CRLDistPoint(new DistributionPoint[]

          { new DistributionPoint(distPointOne, null, null) }

          ));
          + }
          +
          + if (ocspPort != null)

          { + certificateBuilder.addExtension(Extension.authorityInfoAccess, false, new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, + new GeneralName(GeneralName.uniformResourceIdentifier, "http://" + hostname + ":" + ocspPort))); + }

          +
          + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(signer));
          + }
          +
          +
          + private KeyPair createKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException

          { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + return keyPair; + }

          +
          + private String generateQuorumConfiguration()

          { + int portQp1 = PortAssignment.unique(); + int portQp2 = PortAssignment.unique(); + int portQp3 = PortAssignment.unique(); + + int portLe1 = PortAssignment.unique(); + int portLe2 = PortAssignment.unique(); + int portLe3 = PortAssignment.unique(); + + + + return "server.1=127.0.0.1:" + (portQp1) + ":" + (portLe1) + ";" + clientPortQp1 + "\n" + + "server.2=127.0.0.1:" + (portQp2) + ":" + (portLe2) + ";" + clientPortQp2 + "\n" + + "server.3=127.0.0.1:" + (portQp3) + ":" + (portLe3) + ";" + clientPortQp3; + }

          +
          +
          + public void setSSLSystemProperties()

          { + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), validKeystorePath); + System.setProperty(quorumX509Util.getSslKeystorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(quorumX509Util.getSslTruststorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty(), "false"); + }

          +
          + @After
          + public void cleanUp() throws Exception

          { + clearSSLSystemProperties(); + q1.shutdown(); + q2.shutdown(); + q3.shutdown(); + + Security.removeProvider("BC"); + }

          +
          + private void clearSSLSystemProperties()

          { + System.clearProperty(quorumX509Util.getSslKeystoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslKeystorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslTruststoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslTruststorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(quorumX509Util.getSslOcspEnabledProperty()); + System.clearProperty(quorumX509Util.getSslCrlEnabledProperty()); + }

          +
          + @Test(timeout = 300000)
          + public void testQuorumSSL() throws Exception

          { + q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); + q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); + + + q1.start(); + q2.start(); + + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); + + clearSSLSystemProperties(); + + // This server should fail to join the quorum as it is not using ssl. + q3 = new MainThread(3, clientPortQp3, quorumConfiguration); + q3.start(); + + Assert.assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); + }

          +
          + @Test(timeout = 300000)
          + public void testRollingUpgrade() throws Exception {
          — End diff –

          Fixed

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r111656663 — Diff: src/java/test/org/apache/zookeeper/test/QuorumSSLTest.java — @@ -0,0 +1,668 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.test; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.common.QuorumX509Util; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.bouncycastle.asn1.ocsp.OCSPResponse; +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.CRLNumber; +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509ExtensionUtils; +import org.bouncycastle.cert.X509v2CRLBuilder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.cert.ocsp.BasicOCSPResp; +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.CertificateStatus; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.ocsp.OCSPRespBuilder; +import org.bouncycastle.cert.ocsp.Req; +import org.bouncycastle.cert.ocsp.UnknownStatus; +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.MiscPEMGenerator; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.util.io.pem.PemWriter; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import static org.apache.zookeeper.test.ClientBase.createTmpDir; + +public class QuorumSSLTest extends QuorumPeerTestBase { + + private static final String SSL_QUORUM_ENABLED = "sslQuorum=true\n"; + private static final String PORT_UNIFICATION_ENABLED = "portUnification=true\n"; + private static final String PORT_UNIFICATION_DISABLED = "portUnification=false\n"; + + private static final char[] PASSWORD = "testpass".toCharArray(); + private static final String HOSTNAME = "localhost"; + + private QuorumX509Util quorumX509Util = new QuorumX509Util(); + + private MainThread q1; + private MainThread q2; + private MainThread q3; + + private int clientPortQp1; + private int clientPortQp2; + private int clientPortQp3; + + private String tmpDir; + + private String quorumConfiguration; + private String validKeystorePath; + private String truststorePath; + + private KeyPair rootKeyPair; + private X509Certificate rootCertificate; + + private KeyPair defaultKeyPair; + + private ContentSigner contentSigner; + + @Before + public void setup() throws Exception { + ClientBase.setupTestEnv(); + + tmpDir = createTmpDir().getAbsolutePath(); + + clientPortQp1 = PortAssignment.unique(); + clientPortQp2 = PortAssignment.unique(); + clientPortQp3 = PortAssignment.unique(); + + validKeystorePath = tmpDir + "/valid.jks"; + truststorePath = tmpDir + "/truststore.jks"; + + quorumConfiguration = generateQuorumConfiguration(); + + Security.addProvider(new BouncyCastleProvider()); + + rootKeyPair = createKeyPair(); + contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(rootKeyPair.getPrivate()); + rootCertificate = createSelfSignedCertifcate(rootKeyPair); + + // Write the truststore + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, PASSWORD); + trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate); + FileOutputStream outputStream = new FileOutputStream(truststorePath); + trustStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + + defaultKeyPair = createKeyPair(); + X509Certificate validCertificate = buildEndEntityCert(defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), + HOSTNAME, null, null, null); + writeKeystore(validCertificate, defaultKeyPair, validKeystorePath); + + setSSLSystemProperties(); + } + + private void writeKeystore(X509Certificate certificate, KeyPair entityKeyPair, String path) throws Exception { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, PASSWORD); + keyStore.setKeyEntry("alias", entityKeyPair.getPrivate(), PASSWORD, new Certificate[] { certificate } ); + FileOutputStream outputStream = new FileOutputStream(path); + keyStore.store(outputStream, PASSWORD); + outputStream.flush(); + outputStream.close(); + } + + + private class OCSPHandler implements HttpHandler { + + private X509Certificate revokedCert; + + // Builds an OCSPHandler that responds with a good status for all certificates + // except revokedCert. + public OCSPHandler(X509Certificate revokedCert) { + this.revokedCert = revokedCert; + } + + @Override + public void handle(com.sun.net.httpserver.HttpExchange httpExchange) throws IOException { + byte[] responseBytes; + try { + InputStream request = httpExchange.getRequestBody(); + byte[] requestBytes = new byte [10000] ; + request.read(requestBytes); + + OCSPReq ocspRequest = new OCSPReq(requestBytes); + Req[] requestList = ocspRequest.getRequestList(); + + DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1); + + BasicOCSPRespBuilder responseBuilder = new JcaBasicOCSPRespBuilder(rootKeyPair.getPublic(), digestCalculator); + for ( Req req : requestList ) { + CertificateID certId = req.getCertID(); + CertificateID revokedCertId = new JcaCertificateID(digestCalculator, rootCertificate, revokedCert.getSerialNumber()); + CertificateStatus certificateStatus; + if (revokedCertId.equals(certId)) { + certificateStatus = new UnknownStatus(); + } else { + certificateStatus = CertificateStatus.GOOD; + } + + responseBuilder.addResponse(certId, certificateStatus,null); + } + + X509CertificateHolder[] chain = new X509CertificateHolder[] { new JcaX509CertificateHolder(rootCertificate) } ; + ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(rootKeyPair.getPrivate()); + BasicOCSPResp ocspResponse = responseBuilder.build(signer, chain, Calendar.getInstance().getTime() ); + + responseBytes = new OCSPRespBuilder().build(OCSPRespBuilder.SUCCESSFUL, ocspResponse).getEncoded(); + } catch (OperatorException | CertificateEncodingException | OCSPException exception) { + responseBytes = new OCSPResp(new OCSPResponse(new OCSPResponseStatus(OCSPRespBuilder.INTERNAL_ERROR), null)).getEncoded(); + } + + Headers rh = httpExchange.getResponseHeaders(); + rh.set("Content-Type", "application/ocsp-response"); + httpExchange.sendResponseHeaders(200, responseBytes.length); + + OutputStream os = httpExchange.getResponseBody(); + os.write(responseBytes); + os.close(); + } + } + + private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception { + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, HOSTNAME); + Date notBefore = new Date(); // time from which certificate is valid + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + BigInteger serialNumber = new BigInteger(128, new Random()); + + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); + } + + private void buildCRL(X509Certificate x509Certificate, String crlPath) throws Exception { + X509v2CRLBuilder builder = new JcaX509v2CRLBuilder(x509Certificate.getIssuerX500Principal(), new Date()); + Date notBefore = new Date(); + Calendar cal = Calendar.getInstance(); + cal.setTime(notBefore); + cal.add(Calendar.YEAR, 1); + Date notAfter = cal.getTime(); + builder.setNextUpdate(notAfter); + builder.addCRLEntry(x509Certificate.getSerialNumber(), new Date(), CRLReason.cACompromise); + builder.addExtension(Extension.authorityKeyIdentifier, false, new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(rootCertificate)); + builder.addExtension(Extension.cRLNumber, false, new CRLNumber(new BigInteger("1000"))); + + X509CRLHolder cRLHolder = builder.build(contentSigner); + + PemWriter pemWriter = new PemWriter(new FileWriter(crlPath)); + pemWriter.writeObject(new MiscPEMGenerator(cRLHolder)); + pemWriter.flush(); + pemWriter.close(); + } + + public X509Certificate buildEndEntityCert(KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivateKey, + String hostname, String ipAddress, String crlPath, Integer ocspPort) throws Exception { + X509CertificateHolder holder = new JcaX509CertificateHolder(caCert); + ContentSigner signer =new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPrivateKey); + + List<GeneralName> generalNames = new ArrayList<>(); + if (hostname != null) { + generalNames.add(new GeneralName(GeneralName.dNSName, hostname)); + } + + if (ipAddress != null) { + generalNames.add(new GeneralName(GeneralName.iPAddress, ipAddress)); + } + + SubjectPublicKeyInfo entityKeyInfo = + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(PublicKeyFactory.createKey(keyPair.getPublic().getEncoded())); + X509ExtensionUtils extensionUtils = new BcX509ExtensionUtils(); + X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(holder.getSubject(), new BigInteger(128, new Random()), + new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + 100000), + new X500Name("CN=Test End Entity Certificate"), keyPair.getPublic()) + .addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(holder)) + .addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(entityKeyInfo)) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(false)) + .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)); + + if (!generalNames.isEmpty()) { + certificateBuilder.addExtension(Extension.subjectAlternativeName, true, new GeneralNames(generalNames.toArray(new GeneralName[] {}))); + } + + if (crlPath != null) { + DistributionPointName distPointOne = new DistributionPointName(new GeneralNames( + new GeneralName(GeneralName.uniformResourceIdentifier,"file://" + crlPath))); + + certificateBuilder.addExtension(Extension.cRLDistributionPoints, false, + new CRLDistPoint(new DistributionPoint[] { new DistributionPoint(distPointOne, null, null) } )); + } + + if (ocspPort != null) { + certificateBuilder.addExtension(Extension.authorityInfoAccess, false, new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, + new GeneralName(GeneralName.uniformResourceIdentifier, "http://" + hostname + ":" + ocspPort))); + } + + return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(signer)); + } + + + private KeyPair createKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + return keyPair; + } + + private String generateQuorumConfiguration() { + int portQp1 = PortAssignment.unique(); + int portQp2 = PortAssignment.unique(); + int portQp3 = PortAssignment.unique(); + + int portLe1 = PortAssignment.unique(); + int portLe2 = PortAssignment.unique(); + int portLe3 = PortAssignment.unique(); + + + + return "server.1=127.0.0.1:" + (portQp1) + ":" + (portLe1) + ";" + clientPortQp1 + "\n" + + "server.2=127.0.0.1:" + (portQp2) + ":" + (portLe2) + ";" + clientPortQp2 + "\n" + + "server.3=127.0.0.1:" + (portQp3) + ":" + (portLe3) + ";" + clientPortQp3; + } + + + public void setSSLSystemProperties() { + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); + System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); + System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), validKeystorePath); + System.setProperty(quorumX509Util.getSslKeystorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslTruststoreLocationProperty(), truststorePath); + System.setProperty(quorumX509Util.getSslTruststorePasswdProperty(), "testpass"); + System.setProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty(), "false"); + } + + @After + public void cleanUp() throws Exception { + clearSSLSystemProperties(); + q1.shutdown(); + q2.shutdown(); + q3.shutdown(); + + Security.removeProvider("BC"); + } + + private void clearSSLSystemProperties() { + System.clearProperty(quorumX509Util.getSslKeystoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslKeystorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslTruststoreLocationProperty()); + System.clearProperty(quorumX509Util.getSslTruststorePasswdProperty()); + System.clearProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty()); + System.clearProperty(quorumX509Util.getSslOcspEnabledProperty()); + System.clearProperty(quorumX509Util.getSslCrlEnabledProperty()); + } + + @Test(timeout = 300000) + public void testQuorumSSL() throws Exception { + q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); + q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); + + + q1.start(); + q2.start(); + + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); + Assert.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); + + clearSSLSystemProperties(); + + // This server should fail to join the quorum as it is not using ssl. + q3 = new MainThread(3, clientPortQp3, quorumConfiguration); + q3.start(); + + Assert.assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); + } + + @Test(timeout = 300000) + public void testRollingUpgrade() throws Exception { — End diff – Fixed
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/565//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/565//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/565//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/565//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/565//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/565//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/566//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/566//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/566//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/566//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/566//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/566//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/567//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/567//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/567//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/567//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/567//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/567//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r112559332

          — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java —
          @@ -0,0 +1,137 @@
          +/**
          + * Licensed to the Apache Software Foundation (ASF) under one
          + * or more contributor license agreements. See the NOTICE file
          + * distributed with this work for additional information
          + * regarding copyright ownership. The ASF licenses this file
          + * to you under the Apache License, Version 2.0 (the
          + * "License"); you may not use this file except in compliance
          + * with the License. You may obtain a copy of the License at
          + *
          + * http://www.apache.org/licenses/LICENSE-2.0
          + *
          + * Unless required by applicable law or agreed to in writing, software
          + * distributed under the License is distributed on an "AS IS" BASIS,
          + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          + * See the License for the specific language governing permissions and
          + * limitations under the License.
          + */
          +package org.apache.zookeeper.common;
          +
          +import org.apache.http.conn.ssl.DefaultHostnameVerifier;
          +import org.slf4j.Logger;
          +import org.slf4j.LoggerFactory;
          +
          +import javax.net.ssl.SSLEngine;
          +import javax.net.ssl.SSLException;
          +import javax.net.ssl.X509ExtendedTrustManager;
          +import java.net.InetAddress;
          +import java.net.Socket;
          +import java.net.UnknownHostException;
          +import java.security.cert.CertificateException;
          +import java.security.cert.X509Certificate;
          +
          +/**
          + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier.
          + *
          + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a
          + * reverse DNS lookup and verify using the hostname.
          + */
          +public class ZKTrustManager extends X509ExtendedTrustManager {
          +
          + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class);
          +
          + private X509ExtendedTrustManager x509ExtendedTrustManager;
          + private boolean hostnameVerificationEnabled;
          + private boolean shouldVerifyClientHostname;
          +
          + private DefaultHostnameVerifier hostnameVerifier;
          +
          + /**
          + * Instantiate a new ZKTrustManager.
          + *
          + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic
          + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames.
          + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client
          + * connecting to this machine will be verified in addition to the servers that this
          + * instance connects to. If false, and hostnameVerificationEnabled is true, only
          + * the hostnames of servers that this instance connects to will be verified. If
          + * hostnameVerificationEnabled is false, this argument is ignored.
          + */
          + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname)

          { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + }

          +
          + @Override
          + public X509Certificate[] getAcceptedIssuers()

          { + return x509ExtendedTrustManager.getAcceptedIssuers(); + }

          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname)

          { + performHostVerification(socket.getInetAddress(), chain[0]); + }
          + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
          + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + }

          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled && shouldVerifyClientHostname) {
          + try

          { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + }
          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
          + if (hostnameVerificationEnabled) {
          + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + }

          catch (UnknownHostException e)

          { + throw new CertificateException("failed to verify host", e); + }

          + }
          + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
          + }
          +
          + @Override
          + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + }

          +
          + @Override
          + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException

          { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + }

          +
          + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException {
          + try {
          + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate);
          — End diff –

          I agree. It could also be argued that authorization is handled by this patch in its current state as well, by only accepting peers that have trusted certificates. In other words, the CA limits which machines get certificates, which limits which machines get to join the quorum. So check 2 is not "needed" if the CA is trusted.

          I also think that check 2 has value for deployments that want to reuse the same truststore for multiple services and for deployments that are not using SSL/TLS at all. So I think it is a check worth doing and it is deserving of its own patch. Would be interested in knowing what other members of the community think.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r112559332 — Diff: src/java/main/org/apache/zookeeper/common/ZKTrustManager.java — @@ -0,0 +1,137 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.common; + +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.X509ExtendedTrustManager; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier. + * + * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a + * reverse DNS lookup and verify using the hostname. + */ +public class ZKTrustManager extends X509ExtendedTrustManager { + + private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class); + + private X509ExtendedTrustManager x509ExtendedTrustManager; + private boolean hostnameVerificationEnabled; + private boolean shouldVerifyClientHostname; + + private DefaultHostnameVerifier hostnameVerifier; + + /** + * Instantiate a new ZKTrustManager. + * + * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic + * @param hostnameVerificationEnabled If true, this TrustManager should verify hostnames. + * @param shouldVerifyClientHostname If true, and hostnameVerificationEnabled is true, the hostname of a client + * connecting to this machine will be verified in addition to the servers that this + * instance connects to. If false, and hostnameVerificationEnabled is true, only + * the hostnames of servers that this instance connects to will be verified. If + * hostnameVerificationEnabled is false, this argument is ignored. + */ + public ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean hostnameVerificationEnabled, boolean shouldVerifyClientHostname) { + this.x509ExtendedTrustManager = x509ExtendedTrustManager; + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + this.shouldVerifyClientHostname = shouldVerifyClientHostname; + + hostnameVerifier = new DefaultHostnameVerifier(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return x509ExtendedTrustManager.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + if (hostnameVerificationEnabled) { + performHostVerification(socket.getInetAddress(), chain[0]); + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled && shouldVerifyClientHostname) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + if (hostnameVerificationEnabled) { + try { + performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); + } catch (UnknownHostException e) { + throw new CertificateException("failed to verify host", e); + } + } + x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + x509ExtendedTrustManager.checkServerTrusted(chain, authType); + } + + private void performHostVerification(InetAddress inetAddress, X509Certificate certificate) throws CertificateException { + try { + hostnameVerifier.verify(inetAddress.getHostAddress(), certificate); — End diff – I agree. It could also be argued that authorization is handled by this patch in its current state as well, by only accepting peers that have trusted certificates. In other words, the CA limits which machines get certificates, which limits which machines get to join the quorum. So check 2 is not "needed" if the CA is trusted. I also think that check 2 has value for deployments that want to reuse the same truststore for multiple services and for deployments that are not using SSL/TLS at all. So I think it is a check worth doing and it is deserving of its own patch. Would be interested in knowing what other members of the community think.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 49 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/570//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/570//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/570//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 49 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/570//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/570//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/570//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 46 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/568//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/568//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/568//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 46 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/568//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/568//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/568//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 46 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/569//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/569//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/569//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 46 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/569//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/569//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/569//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/573//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/573//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/573//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/573//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/573//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/573//console This message is automatically generated.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r113276213

          — Diff: src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java —
          @@ -63,6 +66,10 @@ public void shutdown() {
          }

          public static class MainThread implements Runnable {
          + public File getConfFile() {
          — End diff –

          Can't find a call to this getConfFile. Is this needed?

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r113276213 — Diff: src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java — @@ -63,6 +66,10 @@ public void shutdown() { } public static class MainThread implements Runnable { + public File getConfFile() { — End diff – Can't find a call to this getConfFile. Is this needed?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r113278361

          — Diff: src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java —
          @@ -63,6 +66,10 @@ public void shutdown() {
          }

          public static class MainThread implements Runnable {
          + public File getConfFile() {
          — End diff –

          https://github.com/apache/zookeeper/pull/184/files#diff-41ff8b5e930c780549068c1e027290a4R449

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r113278361 — Diff: src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java — @@ -63,6 +66,10 @@ public void shutdown() { } public static class MainThread implements Runnable { + public File getConfFile() { — End diff – https://github.com/apache/zookeeper/pull/184/files#diff-41ff8b5e930c780549068c1e027290a4R449
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114086628

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java —
          @@ -64,6 +66,8 @@

          protected InetSocketAddress clientPortAddress;
          protected InetSocketAddress secureClientPortAddress;
          + protected boolean sslQuorum = false;
          + protected boolean shouldUsePortUnification = false;
          — End diff –

          This variables and special handling code can be removed, for a configuration property that both available as system properties and available as part of zoo.cfg. In such case the processing code will fall back as https://github.com/apache/zookeeper/blob/master/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java#L296 and is consistent with the existing code style for configuration processing.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114086628 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java — @@ -64,6 +66,8 @@ protected InetSocketAddress clientPortAddress; protected InetSocketAddress secureClientPortAddress; + protected boolean sslQuorum = false; + protected boolean shouldUsePortUnification = false; — End diff – This variables and special handling code can be removed, for a configuration property that both available as system properties and available as part of zoo.cfg. In such case the processing code will fall back as https://github.com/apache/zookeeper/blob/master/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java#L296 and is consistent with the existing code style for configuration processing.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114086635

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java —
          @@ -290,6 +294,10 @@ public void parseProperties(Properties zkProp)
          } else

          { throw new ConfigException("Invalid option " + value + " for reconfigEnabled flag. Choose 'true' or 'false.'"); }

          + } else if (key.equals("sslQuorum")){
          — End diff –

          Similarly these special handling code can be removed.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114086635 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java — @@ -290,6 +294,10 @@ public void parseProperties(Properties zkProp) } else { throw new ConfigException("Invalid option " + value + " for reconfigEnabled flag. Choose 'true' or 'false.'"); } + } else if (key.equals("sslQuorum")){ — End diff – Similarly these special handling code can be removed.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114086663

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java —
          @@ -178,6 +178,8 @@ public void runFromConfig(QuorumPeerConfig config)
          quorumPeer.initConfigInZKDatabase();
          quorumPeer.setCnxnFactory(cnxnFactory);
          quorumPeer.setSecureCnxnFactory(secureCnxnFactory);
          + quorumPeer.setSslQuorum(config.isSslQuorum());
          — End diff –

          This can be removed too.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114086663 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java — @@ -178,6 +178,8 @@ public void runFromConfig(QuorumPeerConfig config) quorumPeer.initConfigInZKDatabase(); quorumPeer.setCnxnFactory(cnxnFactory); quorumPeer.setSecureCnxnFactory(secureCnxnFactory); + quorumPeer.setSslQuorum(config.isSslQuorum()); — End diff – This can be removed too.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114086721

          — Diff: src/java/main/org/apache/zookeeper/common/ZKConfig.java —
          @@ -107,14 +99,33 @@ private void init() {

          • this configuration.
            */
            protected void handleBackwardCompatibility() {
          • properties.put(SSL_KEYSTORE_LOCATION, System.getProperty(SSL_KEYSTORE_LOCATION));
          • properties.put(SSL_KEYSTORE_PASSWD, System.getProperty(SSL_KEYSTORE_PASSWD));
          • properties.put(SSL_TRUSTSTORE_LOCATION, System.getProperty(SSL_TRUSTSTORE_LOCATION));
          • properties.put(SSL_TRUSTSTORE_PASSWD, System.getProperty(SSL_TRUSTSTORE_PASSWD));
          • properties.put(SSL_AUTHPROVIDER, System.getProperty(SSL_AUTHPROVIDER));
            properties.put(JUTE_MAXBUFFER, System.getProperty(JUTE_MAXBUFFER));
            properties.put(KINIT_COMMAND, System.getProperty(KINIT_COMMAND));
            properties.put(JGSS_NATIVE, System.getProperty(JGSS_NATIVE));
            +
            + ClientX509Util clientX509Util = new ClientX509Util();
            + putSSLProperties(clientX509Util);
            + properties.put(clientX509Util.getSslAuthProviderProperty(),
            + System.getProperty(clientX509Util.getSslAuthProviderProperty()));
            +
            + putSSLProperties(new QuorumX509Util());
              • End diff –

          Do we need this call here? This function handles backward compatibility between old way of extracting configs from system properties and the introduce of ZKConfig class (which was done after client-server SSL was added). Quorum ssl does not exist before, so there is no need to handle backward compatibility for it.

          If the purpose is to populate various system properties of quorum ssl then maybe find another place to make the initialization call?

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114086721 — Diff: src/java/main/org/apache/zookeeper/common/ZKConfig.java — @@ -107,14 +99,33 @@ private void init() { this configuration. */ protected void handleBackwardCompatibility() { properties.put(SSL_KEYSTORE_LOCATION, System.getProperty(SSL_KEYSTORE_LOCATION)); properties.put(SSL_KEYSTORE_PASSWD, System.getProperty(SSL_KEYSTORE_PASSWD)); properties.put(SSL_TRUSTSTORE_LOCATION, System.getProperty(SSL_TRUSTSTORE_LOCATION)); properties.put(SSL_TRUSTSTORE_PASSWD, System.getProperty(SSL_TRUSTSTORE_PASSWD)); properties.put(SSL_AUTHPROVIDER, System.getProperty(SSL_AUTHPROVIDER)); properties.put(JUTE_MAXBUFFER, System.getProperty(JUTE_MAXBUFFER)); properties.put(KINIT_COMMAND, System.getProperty(KINIT_COMMAND)); properties.put(JGSS_NATIVE, System.getProperty(JGSS_NATIVE)); + + ClientX509Util clientX509Util = new ClientX509Util(); + putSSLProperties(clientX509Util); + properties.put(clientX509Util.getSslAuthProviderProperty(), + System.getProperty(clientX509Util.getSslAuthProviderProperty())); + + putSSLProperties(new QuorumX509Util()); End diff – Do we need this call here? This function handles backward compatibility between old way of extracting configs from system properties and the introduce of ZKConfig class (which was done after client-server SSL was added). Quorum ssl does not exist before, so there is no need to handle backward compatibility for it. If the purpose is to populate various system properties of quorum ssl then maybe find another place to make the initialization call?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114086738

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Leader.java —
          @@ -216,18 +218,35 @@ public boolean isQuorumSynced(QuorumVerifier qv) {

          private final ServerSocket ss;

          • Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException {
            + Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException, X509Exception {
            this.self = self;
            try {
          • if (self.getQuorumListenOnAllIPs()) {
          • ss = new ServerSocket(self.getQuorumAddress().getPort());
            + if (self.shouldUsePortUnification()) {
              • End diff –

          I think some level of refactoring that makes the constructor less clogged would be valuable here..

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114086738 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Leader.java — @@ -216,18 +218,35 @@ public boolean isQuorumSynced(QuorumVerifier qv) { private final ServerSocket ss; Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException { + Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException, X509Exception { this.self = self; try { if (self.getQuorumListenOnAllIPs()) { ss = new ServerSocket(self.getQuorumAddress().getPort()); + if (self.shouldUsePortUnification()) { End diff – I think some level of refactoring that makes the constructor less clogged would be valuable here..
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114166007

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -254,6 +260,9 @@ protected void connectToLeader(InetSocketAddress addr)
          }

          sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime));
          + if (self.isSslQuorum())

          { + ((SSLSocket) sock).startHandshake(); + }

          sock.setTcpNoDelay(nodelay);
          break;
          } catch (IOException e) {
          — End diff –

          Is it possible to get a more typed exception here (like SSLException) when hand shake failed? This serves several purposes: if we know the SSL cert is invalid there is no point retrying; from supportability point of view when hand shake failed, it's possible that what finally gets logged does not reflect the cause of the failure due to how these exceptions are handled (for example we may see in log that initLimit was exceeded while the real culprit is invalid cert).

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114166007 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -254,6 +260,9 @@ protected void connectToLeader(InetSocketAddress addr) } sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime)); + if (self.isSslQuorum()) { + ((SSLSocket) sock).startHandshake(); + } sock.setTcpNoDelay(nodelay); break; } catch (IOException e) { — End diff – Is it possible to get a more typed exception here (like SSLException) when hand shake failed? This serves several purposes: if we know the SSL cert is invalid there is no point retrying; from supportability point of view when hand shake failed, it's possible that what finally gets logged does not reflect the cause of the failure due to how these exceptions are handled (for example we may see in log that initLimit was exceeded while the real culprit is invalid cert).
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114166502

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -160,43 +213,120 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke
          }
          throw new KeyManagerException("Couldn't find X509KeyManager");

          • } catch (Exception e) { - throw new KeyManagerException(e); + }

            catch (IOException|CertificateException|UnrecoverableKeyException|NoSuchAlgorithmException|KeyStoreException
            + keyManagerCreationException)

            { + throw new KeyManagerException(keyManagerCreationException); }

            finally {
            if (inputStream != null) {
            try

            { inputStream.close(); - } catch (IOException e) {}
            + } catch (IOException ioException) { + LOG.info("Failed to close key store input stream", ioException); + }
            }
            }
            }

            - public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword)
            + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword,
            + boolean crlEnabled, boolean ocspEnabled,
            + final boolean hostnameVerificationEnabled,
            + final boolean shouldVerifyClientHostname)
            throws TrustManagerException {
            FileInputStream inputStream = null;
            try {
            - char[] trustStorePasswordChars = trustStorePassword.toCharArray();
            File trustStoreFile = new File(trustStoreLocation);
            KeyStore ts = KeyStore.getInstance("JKS");
            inputStream = new FileInputStream(trustStoreFile);
            - ts.load(inputStream, trustStorePasswordChars);
            - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            - tmf.init(ts);
            + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + } else { + ts.load(inputStream, null); + }

            - for (TrustManager tm : tmf.getTrustManagers()) {
            - if (tm instanceof X509TrustManager) {
            - return (X509TrustManager) tm;
            + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector());
            + if (crlEnabled || ocspEnabled) {
            + pbParams.setRevocationEnabled(true);
            + System.setProperty("com.sun.net.ssl.checkRevocation", "true");
            + System.setProperty("com.sun.security.enableCRLDP", "true");
            + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + }
            +
            + } else { + pbParams.setRevocationEnabled(false); + }
            +
            + // Revocation checking is only supported with the PKIX algorithm
            + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
            + tmf.init(new CertPathTrustManagerParameters(pbParams));
            +
            + for (final TrustManager tm : tmf.getTrustManagers()) {
            + if (tm instanceof X509ExtendedTrustManager) { + return new ZKTrustManager((X509ExtendedTrustManager) tm, hostnameVerificationEnabled, shouldVerifyClientHostname); }
            }
            throw new TrustManagerException("Couldn't find X509TrustManager");
            - } catch (Exception e) { - throw new TrustManagerException(e); + } catch (IOException|CertificateException|NoSuchAlgorithmException|InvalidAlgorithmParameterException|KeyStoreException
            + trustManagerCreationException) { + throw new TrustManagerException(trustManagerCreationException); } finally {
            if (inputStream != null) {
            try { inputStream.close(); - }

            catch (IOException e) {}
            + } catch (IOException ioException)

            { + LOG.info("failed to close TrustStore input stream", ioException); + }

            }
            }
            }
            -}
            \ No newline at end of file
            +
            + public SSLSocket createSSLSocket() throws X509Exception, IOException

            { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(); + configureSSLSocket(sslSocket); + + return sslSocket; + }

            +
            + public SSLSocket createSSLSocket(Socket socket) throws X509Exception, IOException

            { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(socket, null, socket.getPort(), true); + configureSSLSocket(sslSocket); + + return sslSocket; + }

            +
            + private void configureSSLSocket(SSLSocket sslSocket) {
            + SSLParameters sslParameters = sslSocket.getSSLParameters();
            + sslParameters.setNeedClientAuth(true);

              • End diff –

          Just curious, does setNeedClientAuth apply to client socket (as opposed to server socket)?

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114166502 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -160,43 +213,120 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke } throw new KeyManagerException("Couldn't find X509KeyManager"); } catch (Exception e) { - throw new KeyManagerException(e); + } catch (IOException|CertificateException|UnrecoverableKeyException|NoSuchAlgorithmException|KeyStoreException + keyManagerCreationException) { + throw new KeyManagerException(keyManagerCreationException); } finally { if (inputStream != null) { try { inputStream.close(); - } catch (IOException e) {} + } catch (IOException ioException) { + LOG.info("Failed to close key store input stream", ioException); + } } } } - public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword) + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword, + boolean crlEnabled, boolean ocspEnabled, + final boolean hostnameVerificationEnabled, + final boolean shouldVerifyClientHostname) throws TrustManagerException { FileInputStream inputStream = null; try { - char[] trustStorePasswordChars = trustStorePassword.toCharArray(); File trustStoreFile = new File(trustStoreLocation); KeyStore ts = KeyStore.getInstance("JKS"); inputStream = new FileInputStream(trustStoreFile); - ts.load(inputStream, trustStorePasswordChars); - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + } else { + ts.load(inputStream, null); + } - for (TrustManager tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - return (X509TrustManager) tm; + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); + if (crlEnabled || ocspEnabled) { + pbParams.setRevocationEnabled(true); + System.setProperty("com.sun.net.ssl.checkRevocation", "true"); + System.setProperty("com.sun.security.enableCRLDP", "true"); + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + } + + } else { + pbParams.setRevocationEnabled(false); + } + + // Revocation checking is only supported with the PKIX algorithm + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(new CertPathTrustManagerParameters(pbParams)); + + for (final TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509ExtendedTrustManager) { + return new ZKTrustManager((X509ExtendedTrustManager) tm, hostnameVerificationEnabled, shouldVerifyClientHostname); } } throw new TrustManagerException("Couldn't find X509TrustManager"); - } catch (Exception e) { - throw new TrustManagerException(e); + } catch (IOException|CertificateException|NoSuchAlgorithmException|InvalidAlgorithmParameterException|KeyStoreException + trustManagerCreationException) { + throw new TrustManagerException(trustManagerCreationException); } finally { if (inputStream != null) { try { inputStream.close(); - } catch (IOException e) {} + } catch (IOException ioException) { + LOG.info("failed to close TrustStore input stream", ioException); + } } } } -} \ No newline at end of file + + public SSLSocket createSSLSocket() throws X509Exception, IOException { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(); + configureSSLSocket(sslSocket); + + return sslSocket; + } + + public SSLSocket createSSLSocket(Socket socket) throws X509Exception, IOException { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(socket, null, socket.getPort(), true); + configureSSLSocket(sslSocket); + + return sslSocket; + } + + private void configureSSLSocket(SSLSocket sslSocket) { + SSLParameters sslParameters = sslSocket.getSSLParameters(); + sslParameters.setNeedClientAuth(true); End diff – Just curious, does setNeedClientAuth apply to client socket (as opposed to server socket)?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user hanm commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114167558

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java —
          @@ -438,9 +447,18 @@ synchronized private boolean connectOne(long sid, InetSocketAddress electionAddr
          Socket sock = null;
          try {
          LOG.debug("Opening channel to server " + sid);

          • sock = new Socket();
          • setSockOpts(sock);
          • sock.connect(electionAddr, cnxTO);
            + if (self.isSslQuorum()) {
            + SSLSocket sslSock = x509Util.createSSLSocket();
            + setSockOpts(sslSock);
            + sslSock.connect(electionAddr, cnxTO);
            + sslSock.startHandshake();
              • End diff –

          This is a synchronous call and will block connectOne until the hand shake finishes. Current cnx manager does blocking IO when handling connections so while hand shake is going on other peers will not be able to connect to this server. This might not be a big problem with carefully tuned initLimit / syncLimit, but I thought we might want mention this somewhere in doc or wiki.

          An alternative would be making hand shake async so cnx manager does not block, but I am currently leaning towards not do this and instead use ZOOKEEPER-900/901 to fix the root cause.

          Show
          githubbot ASF GitHub Bot added a comment - Github user hanm commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114167558 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java — @@ -438,9 +447,18 @@ synchronized private boolean connectOne(long sid, InetSocketAddress electionAddr Socket sock = null; try { LOG.debug("Opening channel to server " + sid); sock = new Socket(); setSockOpts(sock); sock.connect(electionAddr, cnxTO); + if (self.isSslQuorum()) { + SSLSocket sslSock = x509Util.createSSLSocket(); + setSockOpts(sslSock); + sslSock.connect(electionAddr, cnxTO); + sslSock.startHandshake(); End diff – This is a synchronous call and will block connectOne until the hand shake finishes. Current cnx manager does blocking IO when handling connections so while hand shake is going on other peers will not be able to connect to this server. This might not be a big problem with carefully tuned initLimit / syncLimit, but I thought we might want mention this somewhere in doc or wiki. An alternative would be making hand shake async so cnx manager does not block, but I am currently leaning towards not do this and instead use ZOOKEEPER-900 /901 to fix the root cause.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114167751

          — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java —
          @@ -160,43 +213,120 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke
          }
          throw new KeyManagerException("Couldn't find X509KeyManager");

          • } catch (Exception e) { - throw new KeyManagerException(e); + }

            catch (IOException|CertificateException|UnrecoverableKeyException|NoSuchAlgorithmException|KeyStoreException
            + keyManagerCreationException)

            { + throw new KeyManagerException(keyManagerCreationException); }

            finally {
            if (inputStream != null) {
            try

            { inputStream.close(); - } catch (IOException e) {}
            + } catch (IOException ioException) { + LOG.info("Failed to close key store input stream", ioException); + }
            }
            }
            }

            - public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword)
            + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword,
            + boolean crlEnabled, boolean ocspEnabled,
            + final boolean hostnameVerificationEnabled,
            + final boolean shouldVerifyClientHostname)
            throws TrustManagerException {
            FileInputStream inputStream = null;
            try {
            - char[] trustStorePasswordChars = trustStorePassword.toCharArray();
            File trustStoreFile = new File(trustStoreLocation);
            KeyStore ts = KeyStore.getInstance("JKS");
            inputStream = new FileInputStream(trustStoreFile);
            - ts.load(inputStream, trustStorePasswordChars);
            - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            - tmf.init(ts);
            + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + } else { + ts.load(inputStream, null); + }

            - for (TrustManager tm : tmf.getTrustManagers()) {
            - if (tm instanceof X509TrustManager) {
            - return (X509TrustManager) tm;
            + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector());
            + if (crlEnabled || ocspEnabled) {
            + pbParams.setRevocationEnabled(true);
            + System.setProperty("com.sun.net.ssl.checkRevocation", "true");
            + System.setProperty("com.sun.security.enableCRLDP", "true");
            + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + }
            +
            + } else { + pbParams.setRevocationEnabled(false); + }
            +
            + // Revocation checking is only supported with the PKIX algorithm
            + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
            + tmf.init(new CertPathTrustManagerParameters(pbParams));
            +
            + for (final TrustManager tm : tmf.getTrustManagers()) {
            + if (tm instanceof X509ExtendedTrustManager) { + return new ZKTrustManager((X509ExtendedTrustManager) tm, hostnameVerificationEnabled, shouldVerifyClientHostname); }
            }
            throw new TrustManagerException("Couldn't find X509TrustManager");
            - } catch (Exception e) { - throw new TrustManagerException(e); + } catch (IOException|CertificateException|NoSuchAlgorithmException|InvalidAlgorithmParameterException|KeyStoreException
            + trustManagerCreationException) { + throw new TrustManagerException(trustManagerCreationException); } finally {
            if (inputStream != null) {
            try { inputStream.close(); - }

            catch (IOException e) {}
            + } catch (IOException ioException)

            { + LOG.info("failed to close TrustStore input stream", ioException); + }

            }
            }
            }
            -}
            \ No newline at end of file
            +
            + public SSLSocket createSSLSocket() throws X509Exception, IOException

            { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(); + configureSSLSocket(sslSocket); + + return sslSocket; + }

            +
            + public SSLSocket createSSLSocket(Socket socket) throws X509Exception, IOException

            { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(socket, null, socket.getPort(), true); + configureSSLSocket(sslSocket); + + return sslSocket; + }

            +
            + private void configureSSLSocket(SSLSocket sslSocket) {
            + SSLParameters sslParameters = sslSocket.getSSLParameters();
            + sslParameters.setNeedClientAuth(true);

              • End diff –

          Good catch, this can be removed. https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLSocket.html#setNeedClientAuth-boolean-

          `Configures the socket to require client authentication. This option is only useful for sockets in the server mode.`

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114167751 — Diff: src/java/main/org/apache/zookeeper/common/X509Util.java — @@ -160,43 +213,120 @@ public static X509KeyManager createKeyManager(String keyStoreLocation, String ke } throw new KeyManagerException("Couldn't find X509KeyManager"); } catch (Exception e) { - throw new KeyManagerException(e); + } catch (IOException|CertificateException|UnrecoverableKeyException|NoSuchAlgorithmException|KeyStoreException + keyManagerCreationException) { + throw new KeyManagerException(keyManagerCreationException); } finally { if (inputStream != null) { try { inputStream.close(); - } catch (IOException e) {} + } catch (IOException ioException) { + LOG.info("Failed to close key store input stream", ioException); + } } } } - public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword) + public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword, + boolean crlEnabled, boolean ocspEnabled, + final boolean hostnameVerificationEnabled, + final boolean shouldVerifyClientHostname) throws TrustManagerException { FileInputStream inputStream = null; try { - char[] trustStorePasswordChars = trustStorePassword.toCharArray(); File trustStoreFile = new File(trustStoreLocation); KeyStore ts = KeyStore.getInstance("JKS"); inputStream = new FileInputStream(trustStoreFile); - ts.load(inputStream, trustStorePasswordChars); - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); + if (trustStorePassword != null) { + char[] trustStorePasswordChars = trustStorePassword.toCharArray(); + ts.load(inputStream, trustStorePasswordChars); + } else { + ts.load(inputStream, null); + } - for (TrustManager tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - return (X509TrustManager) tm; + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); + if (crlEnabled || ocspEnabled) { + pbParams.setRevocationEnabled(true); + System.setProperty("com.sun.net.ssl.checkRevocation", "true"); + System.setProperty("com.sun.security.enableCRLDP", "true"); + if (ocspEnabled) { + Security.setProperty("ocsp.enable", "true"); + } + + } else { + pbParams.setRevocationEnabled(false); + } + + // Revocation checking is only supported with the PKIX algorithm + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(new CertPathTrustManagerParameters(pbParams)); + + for (final TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509ExtendedTrustManager) { + return new ZKTrustManager((X509ExtendedTrustManager) tm, hostnameVerificationEnabled, shouldVerifyClientHostname); } } throw new TrustManagerException("Couldn't find X509TrustManager"); - } catch (Exception e) { - throw new TrustManagerException(e); + } catch (IOException|CertificateException|NoSuchAlgorithmException|InvalidAlgorithmParameterException|KeyStoreException + trustManagerCreationException) { + throw new TrustManagerException(trustManagerCreationException); } finally { if (inputStream != null) { try { inputStream.close(); - } catch (IOException e) {} + } catch (IOException ioException) { + LOG.info("failed to close TrustStore input stream", ioException); + } } } } -} \ No newline at end of file + + public SSLSocket createSSLSocket() throws X509Exception, IOException { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(); + configureSSLSocket(sslSocket); + + return sslSocket; + } + + public SSLSocket createSSLSocket(Socket socket) throws X509Exception, IOException { + SSLSocket sslSocket = (SSLSocket) getDefaultSSLContext().getSocketFactory().createSocket(socket, null, socket.getPort(), true); + configureSSLSocket(sslSocket); + + return sslSocket; + } + + private void configureSSLSocket(SSLSocket sslSocket) { + SSLParameters sslParameters = sslSocket.getSSLParameters(); + sslParameters.setNeedClientAuth(true); End diff – Good catch, this can be removed. https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLSocket.html#setNeedClientAuth-boolean- `Configures the socket to require client authentication. This option is only useful for sockets in the server mode.`
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114168758

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java —
          @@ -64,6 +66,8 @@

          protected InetSocketAddress clientPortAddress;
          protected InetSocketAddress secureClientPortAddress;
          + protected boolean sslQuorum = false;
          + protected boolean shouldUsePortUnification = false;
          — End diff –

          these options are not available as system properties and can only be set in the config file.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114168758 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java — @@ -64,6 +66,8 @@ protected InetSocketAddress clientPortAddress; protected InetSocketAddress secureClientPortAddress; + protected boolean sslQuorum = false; + protected boolean shouldUsePortUnification = false; — End diff – these options are not available as system properties and can only be set in the config file.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114168844

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java —
          @@ -290,6 +294,10 @@ public void parseProperties(Properties zkProp)
          } else

          { throw new ConfigException("Invalid option " + value + " for reconfigEnabled flag. Choose 'true' or 'false.'"); }

          + } else if (key.equals("sslQuorum")){
          — End diff –

          See https://github.com/apache/zookeeper/pull/184#discussion_r114168758

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114168844 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java — @@ -290,6 +294,10 @@ public void parseProperties(Properties zkProp) } else { throw new ConfigException("Invalid option " + value + " for reconfigEnabled flag. Choose 'true' or 'false.'"); } + } else if (key.equals("sslQuorum")){ — End diff – See https://github.com/apache/zookeeper/pull/184#discussion_r114168758
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114168897

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java —
          @@ -178,6 +178,8 @@ public void runFromConfig(QuorumPeerConfig config)
          quorumPeer.initConfigInZKDatabase();
          quorumPeer.setCnxnFactory(cnxnFactory);
          quorumPeer.setSecureCnxnFactory(secureCnxnFactory);
          + quorumPeer.setSslQuorum(config.isSslQuorum());
          — End diff –

          See https://github.com/apache/zookeeper/pull/184#discussion_r114168758

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114168897 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java — @@ -178,6 +178,8 @@ public void runFromConfig(QuorumPeerConfig config) quorumPeer.initConfigInZKDatabase(); quorumPeer.setCnxnFactory(cnxnFactory); quorumPeer.setSecureCnxnFactory(secureCnxnFactory); + quorumPeer.setSslQuorum(config.isSslQuorum()); — End diff – See https://github.com/apache/zookeeper/pull/184#discussion_r114168758
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114172228

          — Diff: src/java/main/org/apache/zookeeper/common/ZKConfig.java —
          @@ -107,14 +99,33 @@ private void init() {

          • this configuration.
            */
            protected void handleBackwardCompatibility() {
          • properties.put(SSL_KEYSTORE_LOCATION, System.getProperty(SSL_KEYSTORE_LOCATION));
          • properties.put(SSL_KEYSTORE_PASSWD, System.getProperty(SSL_KEYSTORE_PASSWD));
          • properties.put(SSL_TRUSTSTORE_LOCATION, System.getProperty(SSL_TRUSTSTORE_LOCATION));
          • properties.put(SSL_TRUSTSTORE_PASSWD, System.getProperty(SSL_TRUSTSTORE_PASSWD));
          • properties.put(SSL_AUTHPROVIDER, System.getProperty(SSL_AUTHPROVIDER));
            properties.put(JUTE_MAXBUFFER, System.getProperty(JUTE_MAXBUFFER));
            properties.put(KINIT_COMMAND, System.getProperty(KINIT_COMMAND));
            properties.put(JGSS_NATIVE, System.getProperty(JGSS_NATIVE));
            +
            + ClientX509Util clientX509Util = new ClientX509Util();
            + putSSLProperties(clientX509Util);
            + properties.put(clientX509Util.getSslAuthProviderProperty(),
            + System.getProperty(clientX509Util.getSslAuthProviderProperty()));
            +
            + putSSLProperties(new QuorumX509Util());
              • End diff –

          Good point. I wanted to handle the configuration for client<>server and server<>server ssl as similarly as possible. We share a good amount of code between the two and configuration is often read by the same code, so I thought it would be easier to just keep them together.

          See https://github.com/apache/zookeeper/pull/184/files#diff-17cf39d4a9007c59b76832475b08b667R145 for an example. If you feel strongly about this we can work on changing. it.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114172228 — Diff: src/java/main/org/apache/zookeeper/common/ZKConfig.java — @@ -107,14 +99,33 @@ private void init() { this configuration. */ protected void handleBackwardCompatibility() { properties.put(SSL_KEYSTORE_LOCATION, System.getProperty(SSL_KEYSTORE_LOCATION)); properties.put(SSL_KEYSTORE_PASSWD, System.getProperty(SSL_KEYSTORE_PASSWD)); properties.put(SSL_TRUSTSTORE_LOCATION, System.getProperty(SSL_TRUSTSTORE_LOCATION)); properties.put(SSL_TRUSTSTORE_PASSWD, System.getProperty(SSL_TRUSTSTORE_PASSWD)); properties.put(SSL_AUTHPROVIDER, System.getProperty(SSL_AUTHPROVIDER)); properties.put(JUTE_MAXBUFFER, System.getProperty(JUTE_MAXBUFFER)); properties.put(KINIT_COMMAND, System.getProperty(KINIT_COMMAND)); properties.put(JGSS_NATIVE, System.getProperty(JGSS_NATIVE)); + + ClientX509Util clientX509Util = new ClientX509Util(); + putSSLProperties(clientX509Util); + properties.put(clientX509Util.getSslAuthProviderProperty(), + System.getProperty(clientX509Util.getSslAuthProviderProperty())); + + putSSLProperties(new QuorumX509Util()); End diff – Good point. I wanted to handle the configuration for client< >server and server< >server ssl as similarly as possible. We share a good amount of code between the two and configuration is often read by the same code, so I thought it would be easier to just keep them together. See https://github.com/apache/zookeeper/pull/184/files#diff-17cf39d4a9007c59b76832475b08b667R145 for an example. If you feel strongly about this we can work on changing. it.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114175213

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java —
          @@ -438,9 +447,18 @@ synchronized private boolean connectOne(long sid, InetSocketAddress electionAddr
          Socket sock = null;
          try {
          LOG.debug("Opening channel to server " + sid);

          • sock = new Socket();
          • setSockOpts(sock);
          • sock.connect(electionAddr, cnxTO);
            + if (self.isSslQuorum()) {
            + SSLSocket sslSock = x509Util.createSSLSocket();
            + setSockOpts(sslSock);
            + sslSock.connect(electionAddr, cnxTO);
            + sslSock.startHandshake();
              • End diff –

          So I believe in 3.4 we have a workaround for this for SASL https://github.com/apache/zookeeper/blob/branch-3.4/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java#L583

          I agree that this is something we should document and we should fix the root cause elsewhere.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114175213 — Diff: src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java — @@ -438,9 +447,18 @@ synchronized private boolean connectOne(long sid, InetSocketAddress electionAddr Socket sock = null; try { LOG.debug("Opening channel to server " + sid); sock = new Socket(); setSockOpts(sock); sock.connect(electionAddr, cnxTO); + if (self.isSslQuorum()) { + SSLSocket sslSock = x509Util.createSSLSocket(); + setSockOpts(sslSock); + sslSock.connect(electionAddr, cnxTO); + sslSock.startHandshake(); End diff – So I believe in 3.4 we have a workaround for this for SASL https://github.com/apache/zookeeper/blob/branch-3.4/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java#L583 I agree that this is something we should document and we should fix the root cause elsewhere.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user afine commented on a diff in the pull request:

          https://github.com/apache/zookeeper/pull/184#discussion_r114181665

          — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java —
          @@ -254,6 +260,9 @@ protected void connectToLeader(InetSocketAddress addr)
          }

          sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime));
          + if (self.isSslQuorum())

          { + ((SSLSocket) sock).startHandshake(); + }

          sock.setTcpNoDelay(nodelay);
          break;
          } catch (IOException e) {
          — End diff –

          > if we know the SSL cert is invalid there is no point retrying

          Not sure I agree. I can imagine cases where the certificate revocation system is down and comes up at a later point in time.

          >it's possible that what finally gets logged does not reflect the cause of the failure due to how these exceptions are handled

          We seem to be logging pretty reasonably here: https://github.com/apache/zookeeper/blob/branch-3.5/src/java/main/org/apache/zookeeper/server/quorum/Follower.java#L95

          In addition, there is some JVM system properties for ssl logging that we can document to make sure certificate issues are as clear as possible.

          Show
          githubbot ASF GitHub Bot added a comment - Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/184#discussion_r114181665 — Diff: src/java/main/org/apache/zookeeper/server/quorum/Learner.java — @@ -254,6 +260,9 @@ protected void connectToLeader(InetSocketAddress addr) } sockConnect(sock, addr, Math.min(self.tickTime * self.syncLimit, remainingInitLimitTime)); + if (self.isSslQuorum()) { + ((SSLSocket) sock).startHandshake(); + } sock.setTcpNoDelay(nodelay); break; } catch (IOException e) { — End diff – > if we know the SSL cert is invalid there is no point retrying Not sure I agree. I can imagine cases where the certificate revocation system is down and comes up at a later point in time. >it's possible that what finally gets logged does not reflect the cause of the failure due to how these exceptions are handled We seem to be logging pretty reasonably here: https://github.com/apache/zookeeper/blob/branch-3.5/src/java/main/org/apache/zookeeper/server/quorum/Follower.java#L95 In addition, there is some JVM system properties for ssl logging that we can document to make sure certificate issues are as clear as possible.
          Hide
          hadoopqa Hadoop QA added a comment -

          -1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          -1 core tests. The patch failed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/663//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/663//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/663//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - -1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. -1 core tests. The patch failed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/663//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/663//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/663//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/664//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/664//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/664//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/664//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/664//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/664//console This message is automatically generated.
          Hide
          hadoopqa Hadoop QA added a comment -

          +1 overall. GitHub Pull Request Build

          +1 @author. The patch does not contain any @author tags.

          +1 tests included. The patch appears to include 43 new or modified tests.

          +1 javadoc. The javadoc tool did not generate any warning messages.

          +1 javac. The applied patch does not increase the total number of javac compiler warnings.

          +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings.

          +1 release audit. The applied patch does not increase the total number of release audit warnings.

          +1 core tests. The patch passed core unit tests.

          +1 contrib tests. The patch passed contrib unit tests.

          Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/669//testReport/
          Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/669//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
          Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/669//console

          This message is automatically generated.

          Show
          hadoopqa Hadoop QA added a comment - +1 overall. GitHub Pull Request Build +1 @author. The patch does not contain any @author tags. +1 tests included. The patch appears to include 43 new or modified tests. +1 javadoc. The javadoc tool did not generate any warning messages. +1 javac. The applied patch does not increase the total number of javac compiler warnings. +1 findbugs. The patch does not introduce any new Findbugs (version 3.0.1) warnings. +1 release audit. The applied patch does not increase the total number of release audit warnings. +1 core tests. The patch passed core unit tests. +1 contrib tests. The patch passed contrib unit tests. Test results: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/669//testReport/ Findbugs warnings: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/669//artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html Console output: https://builds.apache.org/job/PreCommit-ZOOKEEPER-github-pr-build/669//console This message is automatically generated.

            People

            • Assignee:
              abrahamfine Abraham Fine
              Reporter:
              breed Benjamin Reed
            • Votes:
              4 Vote for this issue
              Watchers:
              15 Start watching this issue

              Dates

              • Created:
                Updated:

                Development