You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by si...@apache.org on 2020/02/10 17:35:04 UTC
[pulsar-manager] branch master updated: Support https forward (#258)
This is an automated email from the ASF dual-hosted git repository.
sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-manager.git
The following commit(s) were added to refs/heads/master by this push:
new 73f185c Support https forward (#258)
73f185c is described below
commit 73f185c7852d45cbfe6ed386972ffe9058ffffe5
Author: Guangning <gu...@apache.org>
AuthorDate: Tue Feb 11 01:34:56 2020 +0800
Support https forward (#258)
Fixed
#14
#224
#253
### Motivation
Support https forward.
### Modifications
* Add https forward
* Update readme
* Add option for TLS configurations
* Default enable the user management
---
README.md | 13 ++-
front-end/src/router/index.js | 9 +-
.../management/subscriptions/subscription.vue | 9 ++
src/README.md | 40 ++++++++
.../manager/controller/TopicsController.java | 14 +++
.../org/apache/pulsar/manager/utils/HttpUtil.java | 101 +++++++++++++++++++--
.../pulsar/manager/zuul/EnvironmentForward.java | 1 -
.../manager/zuul/HttpsClientConfiguration.java | 76 ++++++++++++++++
src/main/resources/application.properties | 10 +-
9 files changed, 255 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
index 837abaf..f9dc381 100644
--- a/README.md
+++ b/README.md
@@ -140,12 +140,21 @@ The pulsar-manager can monitor topics and subscriptions.
3. Access Pulsar manager website.
- Use the account and the password to log in to the Pulsar manager website as below.
-
+If you are deploying Pulsar Manager 0.1.0 using the released container, you can log in the Pulsar Manager UI using the following credentials.
+
* Account: `pulsar`
* Password: `pulsar`
+
+If you are deploying Pulsar Manager using the latest code, you can create a super-user using the following command.
+
+ ```$xslt
+ curl -H "Content-Type: application/json" -X PUT http://backend-service:7750/pulsar-manager/users/superuser -d '{"name": "admin", "password": "apachepulsar", "description": "test", "email": "username@test.org"}'
+ ```
+ `backend-service`: The IP address or domain name of the backend service.
+
* Pulsar manager website: http://localhost:9527/
+
### Default Test database HerdDB
#### Introduction
diff --git a/front-end/src/router/index.js b/front-end/src/router/index.js
index e3f878c..64f6339 100644
--- a/front-end/src/router/index.js
+++ b/front-end/src/router/index.js
@@ -206,7 +206,8 @@ export const asyncRouterMap = [
title: 'TenantInfo',
noCache: true,
roles: ['super', 'admin']
- }
+ },
+ hidden: true
},
{
path: 'tenants/tenantInfo/public?tab=namespaces',
@@ -333,7 +334,7 @@ export const asyncRouterMap = [
noCache: true,
roles: ['super']
},
- hidden: false
+ hidden: true
},
{
path: '/roles',
@@ -344,7 +345,7 @@ export const asyncRouterMap = [
noCache: true,
roles: ['super', 'admin']
},
- hidden: false
+ hidden: true
},
{
path: '/roleBinding',
@@ -355,7 +356,7 @@ export const asyncRouterMap = [
noCache: true,
roles: ['super', 'admin']
},
- hidden: false
+ hidden: true
},
{
path: '/tokens',
diff --git a/front-end/src/views/management/subscriptions/subscription.vue b/front-end/src/views/management/subscriptions/subscription.vue
index afb4a32..2feab85 100644
--- a/front-end/src/views/management/subscriptions/subscription.vue
+++ b/front-end/src/views/management/subscriptions/subscription.vue
@@ -478,6 +478,15 @@ export default {
this.postForm.subscription,
this.form.peekNumMessages).then(response => {
if (!response.data) return
+ if (response.data.hasOwnProperty('error')) {
+ this.$notify({
+ title: 'error',
+ message: response.data.error,
+ type: 'error',
+ duration: 3000
+ })
+ return
+ }
if (!response.data.data) return
if (response.data.data.hasOwnProperty('error')) {
this.$notify({
diff --git a/src/README.md b/src/README.md
index 858f52d..a296c13 100644
--- a/src/README.md
+++ b/src/README.md
@@ -45,6 +45,46 @@ spring.datasource.password=postgres
./gradlew -x build -x test
```
+### Enable Https forward
+
+First, please get the client certificate file for connecting to your Pulsar cluster. Please check out this [document](http://pulsar.apache.org/docs/en/security-tls-transport/) on how to create the client certificate file for Pulsar clients. Assume your client certificate is stored in `certs/ca.cert.pem`.
+
+Secondly, following the instructions below to generate a keystore file to be used by Pulsar Manager. Please replace `[/path/to/client/certs]` with the path to your client certificate file.
+
+```$xslt
+keytool -import -trustcacerts -keystore keystore-file -alias test-keystore -file [/path/to/client/certs]
+```
+
+Thirdly, configure Pulsar Manager to enable TLS by adding the following configuration settings.
+
+```$xslt
+tls.enabled=false
+tls.keystore=keystore-file
+tls.keystore.password=keystore-file-password
+tls.hostname.verifier=false
+```
+
+| Name | Default |Description
+| ------- | ------- | ------- |
+| tls.enabled | false | If broker enable tls configuration, set to true, otherwise set to false |
+| tls.keystore | "" | Keystore file path, need to use tool keytool to convert pem file to b file |
+| tls.keystore.password | "" | Keystore file password |
+| tls.hostname.verifier | false | Turn on hostname check, default false to test |
+
+### Enable peek message
+
+If you want to enable feature `peek-message`, please set the following option to true in file `application.properties`.
+
+```$xslt
+pulsar.peek.message=true
+```
+
+If your Pulsar cluster enable TLS, you also need to add the path of the ca certificate for option `tls.pulsar.admin.ca-certs` in file `application.properties`.
+
+```$xslt
+tls.pulsar.admin.ca-certs=ca-client-path
+```
+
### Enable JWT Auth
If you want to turn on JWT authentication, configure the following parameters:
diff --git a/src/main/java/org/apache/pulsar/manager/controller/TopicsController.java b/src/main/java/org/apache/pulsar/manager/controller/TopicsController.java
index 56531fb..fd1db13 100644
--- a/src/main/java/org/apache/pulsar/manager/controller/TopicsController.java
+++ b/src/main/java/org/apache/pulsar/manager/controller/TopicsController.java
@@ -66,6 +66,15 @@ public class TopicsController {
@Value("${pulsar.peek.message}")
private boolean peekMessage;
+ @Value("${tls.enabled}")
+ private boolean tlsEnabled;
+
+ @Value("${tls.hostname.verifier}")
+ private boolean tlsHostnameVerifier;
+
+ @Value("${tls.pulsar.admin.ca-certs}")
+ private String tlsPulsarAdminCaCerts;
+
@Autowired
public TopicsController(
TopicsService topicsService,
@@ -156,6 +165,11 @@ public class TopicsController {
// to do check permission for non super, waiting for https://github.com/apache/pulsar-manager/pull/238
try {
PulsarAdminBuilder pulsarAdminBuilder = PulsarAdmin.builder();
+ if (tlsEnabled) {
+ pulsarAdminBuilder
+ .tlsTrustCertsFilePath(tlsPulsarAdminCaCerts)
+ .enableTlsHostnameVerification(tlsHostnameVerifier);
+ }
if (token != null && token.length() > 0) {
pulsarAdminBuilder.authentication(AuthenticationFactory.token(token));
}
diff --git a/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java b/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java
index cf0ba45..1350ae8 100644
--- a/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java
+++ b/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java
@@ -20,25 +20,48 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Component;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* http client class, to get info from pulsar broker
*/
+@Component
public class HttpUtil {
+ private static boolean tlsEnabled;
+
+ private static String tlsKeystore;
+
+ private static String tlsKeystorePassword;
+
+ private static boolean tlsHostnameVerifier;
+
private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);
- private static final CloseableHttpClient httpClient;
+ private static CloseableHttpClient httpClient;
private static int CONNECTION_TIMEOUT = 28 * 1000;
@@ -46,13 +69,67 @@ public class HttpUtil {
public static PoolingHttpClientConnectionManager cm = null;
- static {
- cm = new PoolingHttpClientConnectionManager();
- cm.setDefaultMaxPerRoute(10);
- cm.setMaxTotal(100);
- RequestConfig config = RequestConfig.custom().setConnectTimeout(CONNECTION_TIMEOUT)
- .setSocketTimeout(SO_TIMEOUT).build();
- httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(config).build();
+ @Value("${tls.enabled}")
+ public void setTlsEnabled(boolean tlsEnabled) {
+ HttpUtil.tlsEnabled = tlsEnabled;
+ }
+
+ @Value("${tls.keystore}")
+ public void setTlsKeystore(String brokerKeystore) {
+ HttpUtil.tlsKeystore = brokerKeystore;
+ }
+
+ @Value("${tls.keystore.password}")
+ public void setTlsKeystorePassword(String brokerKeystorePassword) {
+ HttpUtil.tlsKeystorePassword = brokerKeystorePassword;
+ }
+
+ @Value("${tls.hostname.verifier}")
+ public void setTlsHostnameVerifier(boolean tlsHostnameVerifier) {
+ HttpUtil.tlsHostnameVerifier = tlsHostnameVerifier;
+ }
+
+ public static void initHttpClient() {
+ try {
+ if (tlsEnabled) {
+ Resource resource = new FileSystemResource(tlsKeystore);
+ File trustStoreFile = resource.getFile();
+ HostnameVerifier hostnameVerifier = (s, sslSession) -> {
+ // Custom logic to verify host name, tlsHostnameVerifier is false for test
+ if (!tlsHostnameVerifier) {
+ return true;
+ } else {
+ HostnameVerifier hv= HttpsURLConnection.getDefaultHostnameVerifier();
+ return hv.verify(s, sslSession);
+ }
+ };
+
+ SSLContext sslcontext = SSLContexts.custom()
+ .loadTrustMaterial(
+ trustStoreFile,
+ tlsKeystorePassword.toCharArray(),
+ new TrustSelfSignedStrategy())
+ .build();
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, hostnameVerifier);
+ cm = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
+ .register("http", PlainConnectionSocketFactory.getSocketFactory())
+ .register("https", sslsf)
+ .build());
+ } else {
+ cm = new PoolingHttpClientConnectionManager();
+ }
+
+ cm.setDefaultMaxPerRoute(10);
+ cm.setMaxTotal(100);
+ RequestConfig config = RequestConfig.custom().setConnectTimeout(CONNECTION_TIMEOUT)
+ .setSocketTimeout(SO_TIMEOUT).build();
+ httpClient = HttpClients.custom()
+ .setConnectionManager(cm).setDefaultRequestConfig(config).build();
+
+ } catch (Exception e) {
+ log.error("Failed init http client error message: {}, error stack trace: {}", e.getMessage(), e.getCause());
+ }
}
public static String doGet(String url, Map<String, String> header){
@@ -88,6 +165,9 @@ public class HttpUtil {
for (Map.Entry<String, String> entry: header.entrySet()) {
request.setHeader(entry.getKey(), entry.getValue());
}
+ if (httpClient == null ) {
+ initHttpClient();
+ }
response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
@@ -98,7 +178,8 @@ public class HttpUtil {
request.abort();
}
} catch (Throwable cause) {
- log.error("http request exception:{}", cause.getMessage());
+ log.error("http request exception message: {}, http request error stack: {}",
+ cause.getMessage(), cause.getCause());
} finally {
try{
if (response != null) {
diff --git a/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java b/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java
index 0965f3d..d1fb323 100644
--- a/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java
+++ b/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java
@@ -123,7 +123,6 @@ public class EnvironmentForward extends ZuulFilter {
return null;
}
String serviceUrl = environmentCacheService.getServiceUrl(request);
- System.out.println(serviceUrl);
return forwardRequest(ctx, request, serviceUrl);
}
diff --git a/src/main/java/org/apache/pulsar/manager/zuul/HttpsClientConfiguration.java b/src/main/java/org/apache/pulsar/manager/zuul/HttpsClientConfiguration.java
new file mode 100644
index 0000000..0881764
--- /dev/null
+++ b/src/main/java/org/apache/pulsar/manager/zuul/HttpsClientConfiguration.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed 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.pulsar.manager.zuul;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import java.io.File;
+
+@Configuration
+public class HttpsClientConfiguration {
+
+ @Value("${tls.enabled}")
+ private boolean tlsEnabled;
+
+ @Value("${tls.keystore}")
+ private String tlsKeystore;
+
+ @Value("${tls.keystore.password}")
+ private String tlsKeystorePassword;
+
+ @Value("${tls.hostname.verifier}")
+ private boolean tlsHostnameVerifier;
+
+ @Bean
+ public CloseableHttpClient httpClient() throws Exception {
+ if (tlsEnabled) {
+ Resource resource = new FileSystemResource(tlsKeystore);
+ File trustStoreFile = resource.getFile();
+ SSLContext sslcontext = SSLContexts.custom()
+ .loadTrustMaterial(trustStoreFile, tlsKeystorePassword.toCharArray(),
+ new TrustSelfSignedStrategy())
+ .build();
+ HostnameVerifier hostnameVerifier = (s, sslSession) -> {
+ // Custom logic to verify host name, tlsHostnameVerifier is false for test
+ if (!tlsHostnameVerifier) {
+ return true;
+ } else {
+ HostnameVerifier hv= HttpsURLConnection.getDefaultHostnameVerifier();
+ return hv.verify(s, sslSession);
+ }
+ };
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
+ sslcontext,
+ hostnameVerifier);
+
+ return HttpClients.custom()
+ .setSSLSocketFactory(sslsf)
+ .build();
+ }
+ return HttpClients.custom().build();
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 777891e..1e6be65 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -80,7 +80,7 @@ jwt.sessionTime=2592000
pulsar-manager.account=pulsar
pulsar-manager.password=pulsar
# If true, the database is used for user management
-user.management.enable=false
+user.management.enable=true
# Optional -> SECRET, PRIVATE, default -> PRIVATE, empty -> disable auth
# SECRET mode -> bin/pulsar tokens create --secret-key file:///path/to/my-secret.key --subject test-user
@@ -135,5 +135,13 @@ spring.thymeleaf.mode=HTML5
default.environment.name=
default.environment.service_url=
+# enable tls encryption
+# keytool -import -alias test-keystore -keystore ca-certs -file certs/ca.cert.pem
+tls.enabled=false
+tls.keystore=keystore-file-path
+tls.keystore.password=keystore-password
+tls.hostname.verifier=false
+tls.pulsar.admin.ca-certs=ca-client-path
+
# support peek message, default false
pulsar.peek.message=false