Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

#67 add TLS support #80

Merged
merged 7 commits into from
Sep 4, 2021
Merged

Conversation

j3t
Copy link
Member

@j3t j3t commented Jul 13, 2021

  • Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • Ensure that the pull request title represents the desired changelog entry
  • Please describe what you did
  • Link to relevant issues in GitHub or Jira
  • Link to relevant pull requests, esp. upstream and downstream changes
  • Ensure you have provided tests - that demonstrates feature works or fixes the issue

I have added TLS support so that NomadServer which are secured by client/server certificates can used as well.
corresponding Issue: #67

@j3t j3t changed the title Feature 67 add TLS support #67 add TLS support Jul 13, 2021
@j3t j3t force-pushed the feature-67-add-tls-support branch 5 times, most recently from 9fe3664 to 21e26e2 Compare July 14, 2021 11:01
@j3t
Copy link
Member Author

j3t commented Jul 14, 2021

Could please one of the maintainers review this PR? BTW, I cannot link the corresponding issue, could you please take care of that as well?

@multani multani linked an issue Jul 14, 2021 that may be closed by this pull request
@multani
Copy link

multani commented Jul 14, 2021

Hi @j3t!
Thanks a lot for the PR, I'll take a look in the coming days 👋

@j3t j3t force-pushed the feature-67-add-tls-support branch 3 times, most recently from 0780f75 to a4e984e Compare July 15, 2021 09:14
@j3t
Copy link
Member Author

j3t commented Jul 15, 2021

Thanks @multani! I have added some more tests.

@@ -335,7 +399,8 @@ public Node call() throws Exception {

// Support for Jenkins security
String jnlpSecret = "";
if (Jenkins.get().isUseSecurity()) {
if (Jenkins.get().isUseSecurity() || template.isDockerDriver()) {
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure about this condition at all. It depends very much on how the agent.jar is executed. When java -cp is used then the secret is mandatory but when -jar is used then it's optional I guess. It probably makes sense to refactor this part in the future so that either the cp or the jar approach is used but not both.

Copy link

Choose a reason for hiding this comment

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

I don't see how this is relevant to adding TLS to talk with the Nomad agent though?

Can you simply remove this and address it in a different issue?

pom.xml Show resolved Hide resolved
Copy link

@multani multani left a comment

Choose a reason for hiding this comment

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

@j3t Thanks for the PR!

Disclaimer: I'm not familiar at all with the Java PKI APIs.

I had a look and tested locally, the only issue I found was the "Test connection" in doTestConnection failed to report the TLS connection couldn't be established. Could you update it to return something useful if the plugin , without TLS correctly configured, tries to connect to a TLS-enabled Nomad server?

I quickly hacked something like this:

                Response response = clientBuilder.build().newCall(request).execute();
                ResponseBody body = response.body();
                String content ="";
                if (body != null) {
                    content = body.string();
                    body.close();
                }

                if (response.code() != 200) {
                    throw new Exception("HTTP Status " + response.code() + ": " + content);
                }

Apart from that, if I understood your PR correctly, you made the client certificate mandatory but not the server certificate / CA.
Also, not having the server certificate/CA doesn't validate the server identity, right?

If this is the case, you should flip around the setup you proposed then:

  • The server certificate must be fully validated, instead of being optional. I think this one is the most important: if you bother to build up a PKI and enable TLS on Nomad, you must have a way to validate the client->server TLS communications.
  • The client certificate should be optional: verification of the client certificate can be disabled on the server's side, so this is not strictly mandatory. It's pretty cool that you build it up in the PR in the first place, so keep it 👍

Also, I'd like the README to be extended a bit in the future (it's almost useless as it is, and it's actually not easy to get started with the plugin). Would you mind adding a small section on what would be possible when this gets merged and how to configure TLS with the plugin?

@@ -335,7 +399,8 @@ public Node call() throws Exception {

// Support for Jenkins security
String jnlpSecret = "";
if (Jenkins.get().isUseSecurity()) {
if (Jenkins.get().isUseSecurity() || template.isDockerDriver()) {
Copy link

Choose a reason for hiding this comment

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

I don't see how this is relevant to adding TLS to talk with the Nomad agent though?

Can you simply remove this and address it in a different issue?

}

/**
* Checks that mutual TLS is working as expected (client proves the identity of the server and vise versa).
Copy link

Choose a reason for hiding this comment

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

Suggested change
* Checks that mutual TLS is working as expected (client proves the identity of the server and vise versa).
* Checks that mutual TLS is working as expected (client proves the identity of the server and vice versa).

}

/**
* Checks that one way TLS is working as expected (server proves the identity of the client but not vise versa).
Copy link

Choose a reason for hiding this comment

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

Suggested change
* Checks that one way TLS is working as expected (server proves the identity of the client but not vise versa).
* Checks that one way TLS is working as expected (server proves the identity of the client but not vice versa).

Like I said above, not proving the identity of the server is a bit awkward at best: if you rely on TLS to protect your communication but not authenticating who you are talking to, you can send sensitive information to a rogue server that just has TLS enabled.

Actually, if the connection can't fully validate the TLS connection, I would fail the connection altogether.

}

/**
* Checks that one way TLS is working as expected (client proves the identity of the server but not vise versa).
Copy link

Choose a reason for hiding this comment

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

Suggested change
* Checks that one way TLS is working as expected (client proves the identity of the server but not vise versa).
* Checks that one way TLS is working as expected (client proves the identity of the server but not vice versa).

<f:entry title="Client certificate password (optional)" >
<f:password field="clientPassword" />
</f:entry>
<f:entry title="Server certificate path (optional)" description="Path to the PKCS12 server certificate (aka Truststore). Usually a common authority or a public key.">
Copy link

Choose a reason for hiding this comment

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

It should be worth mentioning that not setting this will not validate the certificate from the server.

I'd would even say not having this configure should not even be a valid configuration: if you have TLS enabled, you must validate the full TLS setup, otherwise it's a bit pointless.

What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

In my opinion we have to support the following use cases

  1. mutual TLS (client proves the identity of the server and vice versa)
  2. client auth only (server proves the identity of the client)
  3. server auth only (client proves the identity of the server)

The current solution supports them but is not clear. What is the expectation when TLS is enabled and both certificate fields are empty?

Suggestion: Differentiate between enable TLS (client-auth) and enable TLS (server-auth). Then the certificate is not optional anymore.

Copy link
Member Author

Choose a reason for hiding this comment

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

The server-auth is only important when the server is using self-signed certificates. Providing a server certificate, overrides JVM's default truststore.

}

/**
* Checks that TLS in general is working as expected (client not proves the identity of the server and vise versa).
Copy link

Choose a reason for hiding this comment

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

Suggested change
* Checks that TLS in general is working as expected (client not proves the identity of the server and vise versa).
* Checks that TLS in general is working as expected (client not proves the identity of the server and vice versa).

@@ -162,12 +207,12 @@ void stopWorker(String workerName, String nomadToken) {
args.add(cloud.getJenkinsUrl());
}

if (!cloud.getJenkinsTunnel().isEmpty()) {
if (cloud.getJenkinsTunnel() != null && !cloud.getJenkinsTunnel().isEmpty()) {
Copy link

Choose a reason for hiding this comment

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

This is not relevant to your PR.

args.add("-tunnel");
args.add(cloud.getJenkinsTunnel());
}

if (!template.getRemoteFs().isEmpty()) {
if (template.getRemoteFs() != null && !template.getRemoteFs().isEmpty()) {
Copy link

Choose a reason for hiding this comment

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

This is not relevant to your PR.

@@ -180,7 +225,7 @@ void stopWorker(String workerName, String nomadToken) {

String prefixCmd = template.getPrefixCmd();
// If an addtional command is defined - prepend it to jenkins worker invocation
if (!prefixCmd.isEmpty()) {
if (prefixCmd != null && !prefixCmd.isEmpty()) {
Copy link

Choose a reason for hiding this comment

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

This is not relevant to your PR.

Comment on lines 77 to 78
* Provides an {@link OkHttpClient} instance. The existing client is reused. <b>Note</b> The client initialization is synchronized
* which means this method is thread safe and can be called from different threads in parallel.
Copy link

Choose a reason for hiding this comment

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

I think this is enough:

Suggested change
* Provides an {@link OkHttpClient} instance. The existing client is reused. <b>Note</b> The client initialization is synchronized
* which means this method is thread safe and can be called from different threads in parallel.
* Provides an {@link OkHttpClient} instance. Create a new client or reuse the existing one.
* This method is threadsafe.

@j3t j3t force-pushed the feature-67-add-tls-support branch from a4e984e to bae6927 Compare July 25, 2021 10:59
Copy link
Member Author

@j3t j3t left a comment

Choose a reason for hiding this comment

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

Thanks for your valuable comments! I have improved the description for the client and server path and I also adjusted the test setup a little bit. Now, tests are separated by the client trust-store (Default CA and Custom CA). I hope that makes things more clear.

<f:entry title="Client certificate password (optional)" >
<f:password field="clientPassword" />
</f:entry>
<f:entry title="Server certificate path (optional)" description="Path to the PKCS12 server certificate (aka Truststore). Usually a common authority or a public key.">
Copy link
Member Author

Choose a reason for hiding this comment

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

In my opinion we have to support the following use cases

  1. mutual TLS (client proves the identity of the server and vice versa)
  2. client auth only (server proves the identity of the client)
  3. server auth only (client proves the identity of the server)

The current solution supports them but is not clear. What is the expectation when TLS is enabled and both certificate fields are empty?

Suggestion: Differentiate between enable TLS (client-auth) and enable TLS (server-auth). Then the certificate is not optional anymore.

<f:entry title="Client certificate password (optional)" >
<f:password field="clientPassword" />
</f:entry>
<f:entry title="Server certificate path (optional)" description="Path to the PKCS12 server certificate (aka Truststore). Usually a common authority or a public key.">
Copy link
Member Author

Choose a reason for hiding this comment

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

The server-auth is only important when the server is using self-signed certificates. Providing a server certificate, overrides JVM's default truststore.

Copy link

@multani multani left a comment

Choose a reason for hiding this comment

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

I finally took some time to give it another look. It looks quite cool on my side, there were a few more things to fix and I tested them and comited them in the branch directly (some typos, a security fix to prevent d451234 from happening again, some doc, etc.)

@j3t If you are good with the change, I'm OK to merge this and create a new release 👍

@multani multani closed this Sep 1, 2021
@multani multani reopened this Sep 1, 2021
@j3t
Copy link
Member Author

j3t commented Sep 1, 2021

Hi @multani, that's great! Unfortunately, I don't have time this week to test it manually but it looks good to me what I have seen so far. So, I would say it's fine. 👍

@j3t
Copy link
Member Author

j3t commented Sep 4, 2021

Just a small adjusted so that NomadApiClientTLSTest is not failing on my local machine and a typo. I have also tested it manually against our Nomad environment. It is working as expected. 👍

@multani
Copy link

multani commented Sep 4, 2021

@j3t Awesome!

Let me merge this and prepare a new release just after that 👍 This is a fine addition to the plugin ❤️

@multani multani merged commit f245444 into jenkinsci:master Sep 4, 2021
@multani
Copy link

multani commented Sep 4, 2021

It's out in v0.8.0 🎉

Note that's there some issues with Jenkins infrastructure at the moment, so I haven't been able to release it fully.

@multani
Copy link

multani commented Sep 9, 2021

It's now released, it should be available through the Update Center soon.

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

Successfully merging this pull request may close these issues.

Support configuring certificates for the connection to nomad
2 participants