Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## 2.6.0 (UNRELEASED)
- [ISSUE-144](https://github.com/SourceLabOrg/kafka-webview/issues/144) Make providing a TrustStore file when setting up a SSL enabled cluster optional. You might not want/need this option if your JVM is already configured to accept the SSL certificate served by the cluster, or if the cluster's certificate can be validated by a publically accessible CA.
- [PR-215](https://github.com/SourceLabOrg/kafka-webview/pull/215) Improve errors displayed when using the `test cluster` functionality.

## 2.5.1 (05/19/2020)
- [ISSUE-209](https://github.com/SourceLabOrg/kafka-webview/issues/209) Expose HealthCheck and App Info endpoints without requiring authentication.
- Docker image now exposes port 9090 for Actuator end points.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

package org.sourcelab.kafka.webview.ui.controller.configuration.cluster;

import org.apache.kafka.common.errors.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sourcelab.kafka.webview.ui.controller.BaseController;
Expand Down Expand Up @@ -136,6 +137,7 @@ public String editClusterForm(
clusterForm.setSsl(cluster.isSslEnabled());
clusterForm.setKeyStoreFilename(cluster.getKeyStoreFile());
clusterForm.setTrustStoreFilename(cluster.getTrustStoreFile());
clusterForm.setUseTrustStore(clusterForm.hasTrustStoreFilename());

// Set SASL options
final SaslProperties saslProperties = saslUtility.decodeProperties(cluster);
Expand Down Expand Up @@ -181,11 +183,14 @@ public String clusterUpdate(
if (clusterForm.getSsl()) {
// If we're creating a new cluster
if (!clusterForm.exists()) {
// Ensure that we have files uploaded
if (clusterForm.getTrustStoreFile() == null || clusterForm.getTrustStoreFile().isEmpty()) {
bindingResult.addError(new FieldError(
"clusterForm", "trustStoreFile", null, true, null, null, "Select a TrustStore JKS to upload")
);
// Ensure that we have files uploaded for the truststore,
// but only if they elected to upload a truststore at all.
if (clusterForm.getUseTrustStore()) {
if (clusterForm.getTrustStoreFile() == null || clusterForm.getTrustStoreFile().isEmpty()) {
bindingResult.addError(new FieldError(
"clusterForm", "trustStoreFile", null, true, null, null, "Select a TrustStore JKS to upload")
);
}
}

// Only require KeyStore if NOT using SASL
Expand Down Expand Up @@ -233,8 +238,23 @@ public String clusterUpdate(
// Flip flag to true
cluster.setSslEnabled(true);

// Determine if we should update keystores
if (!clusterForm.exists() || (clusterForm.getTrustStoreFile() != null && !clusterForm.getTrustStoreFile().isEmpty())) {
// If they've selected to NOT use a trust store.
if (!clusterForm.getUseTrustStore()) {
// Delete previous trust store if exists
if (cluster.getTrustStoreFile() != null) {
uploadManager.deleteKeyStore(cluster.getTrustStoreFile());
}
// Clear out properties
cluster.setTrustStoreFile(null);
cluster.setTrustStorePassword(null);
}

/*
* Determine if we should update truststore. We Update it in the following scenarios:
* - If the cluster is being newly created.
* - If they uploaded a trust store, and there previously was a truststore.
*/
else if (!clusterForm.exists() || (clusterForm.getTrustStoreFile() != null && !clusterForm.getTrustStoreFile().isEmpty())) {
// Delete previous trust store if updating
if (cluster.getTrustStoreFile() != null) {
uploadManager.deleteKeyStore(cluster.getTrustStoreFile());
Expand Down Expand Up @@ -262,6 +282,7 @@ public String clusterUpdate(
}
}

// Determine if we should update keystores
if (!clusterForm.exists() || (clusterForm.getKeyStoreFile() != null && !clusterForm.getKeyStoreFile().isEmpty())) {
// Delete previous key store if updating, or if SASL is enabled.
if (clusterForm.getSasl() || cluster.getKeyStoreFile() != null) {
Expand Down Expand Up @@ -421,7 +442,10 @@ public String testCluster(@PathVariable final Long id, final RedirectAttributes
}
} catch (final Exception e) {
// Collect all reasons.
final String reason = e.getMessage();
String reason = e.getMessage();
if (e instanceof TimeoutException) {
reason = reason + " (This may indicate an authentication or connection problem)";
}

// Set error msg
redirectAttributes.addFlashAttribute(
Expand Down Expand Up @@ -450,5 +474,12 @@ private void setupBreadCrumbs(final Model model, final String name, final String
} else {
manager.addCrumb("Clusters", null);
}

// Add default trust store property.
String defaultTrustStore = System.getProperty("javax.net.ssl.trustStore", "<JRE_HOME>/lib/security/cacerts");
if (defaultTrustStore != null && defaultTrustStore.trim().isEmpty()) {
defaultTrustStore = "<JRE_HOME>/lib/security/cacerts";
}
model.addAttribute("defaultTrustStore", defaultTrustStore);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class ClusterForm {

// SSL Options
private Boolean ssl = false;
private Boolean useTrustStore = false;

private MultipartFile trustStoreFile;
private String trustStoreFilename;
Expand Down Expand Up @@ -141,6 +142,22 @@ public String getTrustStoreFilename() {
}
}

public Boolean getUseTrustStore() {
return useTrustStore;
}

public void setUseTrustStore(final Boolean useTrustStore) {
this.useTrustStore = useTrustStore;
}

/**
* Is there a configured TrustStore file?
* @return true if so, false if not.
*/
public boolean hasTrustStoreFilename() {
return trustStoreFilename != null && !trustStoreFilename.isEmpty();
}

public void setTrustStoreFilename(final String trustStoreFilename) {
this.trustStoreFilename = trustStoreFilename;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,11 @@ private void applySslSettings(final ClusterConfig clusterConfig, final Map<Strin
config.put(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, keyStoreRootPath + "/" + clusterConfig.getKeyStoreFile());
config.put(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG, clusterConfig.getKeyStorePassword());
}
config.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, keyStoreRootPath + "/" + clusterConfig.getTrustStoreFile());
config.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, clusterConfig.getTrustStorePassword());
// Only put Trust properties if one is defined
if (clusterConfig.getTrustStoreFile() != null) {
config.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, keyStoreRootPath + "/" + clusterConfig.getTrustStoreFile());
config.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, clusterConfig.getTrustStorePassword());
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,11 +600,16 @@ private List<ConfigItem> describeResource(final ConfigResource configResource) {
}
}

private RuntimeException handleExecutionException(final ExecutionException e) {
if (e.getCause() != null && e.getCause() instanceof RuntimeException) {
return (RuntimeException) e.getCause();
/**
* Handle ExecutionException errors when raised.
* @param executionException The exception raised.
* @return Appropriate exception instance to throw.
*/
private RuntimeException handleExecutionException(final ExecutionException executionException) {
if (executionException.getCause() != null && executionException.getCause() instanceof RuntimeException) {
return (RuntimeException) executionException.getCause();
}
return new RuntimeException(e.getMessage(), e);
return new RuntimeException(executionException.getMessage(), executionException);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@

package org.sourcelab.kafka.webview.ui.manager.kafka.dto;

import net.bytebuddy.implementation.bytecode.Throw;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
var isSaslChecked = jQuery('#sasl').is(':checked');
jQuery('#ssl-keystore-options').toggle(!isSaslChecked);
});
jQuery('#useTrustStore').click(function() {
var isChecked = jQuery('#useTrustStore').is(':checked');
jQuery('#ssl-truststore-options').toggle(isChecked);
});
jQuery('#sasl').click(function() {
var isChecked = jQuery('#sasl').is(':checked');

Expand Down Expand Up @@ -114,37 +118,64 @@ <h6>SSL Settings</h6>

<!-- SSL Options -->
<div id="ssl-options" th:styleappend="${clusterForm.getSsl()} ? 'display: block;' : 'display: none;'">
<!-- Trust Store file -->
<!-- Do you require upload of additional truststore? -->
<div class="form-group row">
<label class="col-md-3 form-control-label" for="trustStoreFile">
Trust Store
<div
th:if="${clusterForm.getTrustStoreFilename()} != null"
th:text="'Current: ' + ${clusterForm.getTrustStoreFilename()}">
</div>
<label class="col-md-3 form-control-label" for="useTrustStore">
Upload truststore?
</label>
<div class="col-md-9">
<input
id="trustStoreFile" name="trustStoreFile" class="form-control" type="file"
placeholder="Select TrustStore JKS"
id="useTrustStore" name="useTrustStore" class="" type="checkbox"
th:errorclass="is-invalid"
th:value="*{trustStoreFile}">
<div class="invalid-feedback" th:if="${#fields.hasErrors('trustStoreFile')}" th:errors="*{trustStoreFile}"/>
th:field="*{useTrustStore}">

<small class="form-text text-muted">
Only required if the certificate served by your cluster is not registered with an accessible certificate authority (CA), or
if the cluster's certificate has not already been registered in your
JVM's default truststore<span th:if="${defaultTrustStore} == null">.</span>
<span th:if="${defaultTrustStore} != null"> at <i>[[${defaultTrustStore}]]</i></span>
</small>
</div>
</div>

<!-- Trust Store Password -->
<div class="form-group row">
<label class="col-md-3 form-control-label" for="trustStorePassword">
Trust Store Password
</label>
<div class="col-md-9">
<input
id="trustStorePassword" name="trustStorePassword" class="form-control" type="password"
placeholder="TrustStore password"
th:errorclass="is-invalid"
th:value="*{trustStorePassword}">
<div class="invalid-feedback" th:if="${#fields.hasErrors('trustStorePassword')}" th:errors="*{trustStorePassword}"/>
<!-- Only display if trust store is configured -->
<div id="ssl-truststore-options" th:styleappend="${clusterForm.hasTrustStoreFilename()} ? 'display: block;' : 'display: none;'">

<!-- Trust Store file -->
<div class="form-group row">
<label class="col-md-3 form-control-label" for="trustStoreFile">
Trust Store
<div
th:if="${clusterForm.getTrustStoreFilename()} != null"
th:text="'Current: ' + ${clusterForm.getTrustStoreFilename()}">
</div>
</label>
<div class="col-md-9">
<input
id="trustStoreFile" name="trustStoreFile" class="form-control" type="file"
placeholder="Select TrustStore JKS"
th:errorclass="is-invalid"
th:value="*{trustStoreFile}">
<div class="invalid-feedback" th:if="${#fields.hasErrors('trustStoreFile')}" th:errors="*{trustStoreFile}"/>
<small class="form-text text-muted" th:if="${clusterForm.getTrustStoreFilename()} != null">
Leave empty to continue using previously uploaded TrustStore.
</small>
</div>
</div>

<!-- Trust Store Password -->
<div class="form-group row">
<label class="col-md-3 form-control-label" for="trustStorePassword">
Trust Store Password
</label>
<div class="col-md-9">
<input
id="trustStorePassword" name="trustStorePassword" class="form-control" type="password"
placeholder="TrustStore password"
th:errorclass="is-invalid"
th:value="*{trustStorePassword}">
<div class="invalid-feedback" th:if="${#fields.hasErrors('trustStorePassword')}" th:errors="*{trustStorePassword}"/>
</div>
</div>
</div>

Expand Down
Loading