mirror of https://github.com/apache/nifi.git
NIFI-2528 Added RestrictedSSLContextService interface with implementation. Changed ListenHTTP to allow only new StandardRestrictedSSLContextService.
This closes #1986. Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
parent
e62417ea6b
commit
d28e61c5dd
|
@ -34,6 +34,7 @@ import org.apache.nifi.processor.Relationship;
|
|||
import org.apache.nifi.processor.util.StandardValidators;
|
||||
import org.apache.nifi.processors.standard.servlets.ContentAcknowledgmentServlet;
|
||||
import org.apache.nifi.processors.standard.servlets.ListenHTTPServlet;
|
||||
import org.apache.nifi.ssl.RestrictedSSLContextService;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.stream.io.LeakyBucketStreamThrottler;
|
||||
import org.apache.nifi.stream.io.StreamThrottler;
|
||||
|
@ -118,7 +119,7 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
|||
.name("SSL Context Service")
|
||||
.description("The Controller Service to use in order to obtain an SSL Context")
|
||||
.required(false)
|
||||
.identifiesControllerService(SSLContextService.class)
|
||||
.identifiesControllerService(RestrictedSSLContextService.class)
|
||||
.build();
|
||||
public static final PropertyDescriptor HEADERS_AS_ATTRIBUTES_REGEX = new PropertyDescriptor.Builder()
|
||||
.name("HTTP Headers to receive as Attributes (Regex)")
|
||||
|
@ -227,6 +228,10 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
|||
contextFactory.setKeyStoreType(keyStoreType);
|
||||
}
|
||||
|
||||
if (sslContextService != null) {
|
||||
contextFactory.setProtocol(sslContextService.getSslAlgorithm());
|
||||
}
|
||||
|
||||
// thread pool for the jetty instance
|
||||
final QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
threadPool.setName(String.format("%s (%s) Web Server", getClass().getSimpleName(), getIdentifier()));
|
||||
|
@ -249,6 +254,7 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
|||
httpConfiguration.addCustomizer(new SecureRequestCustomizer());
|
||||
|
||||
// build the connector
|
||||
|
||||
connector = new ServerConnector(server, new SslConnectionFactory(contextFactory, "http/1.1"), new HttpConnectionFactory(httpConfiguration));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,11 +20,13 @@ import org.apache.nifi.processor.ProcessContext;
|
|||
import org.apache.nifi.processor.ProcessSessionFactory;
|
||||
import org.apache.nifi.remote.io.socket.NetworkUtils;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.ssl.StandardRestrictedSSLContextService;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.ssl.StandardSSLContextService;
|
||||
import org.apache.nifi.util.MockFlowFile;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -35,10 +37,15 @@ import java.net.URL;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import static org.apache.nifi.processors.standard.ListenHTTP.RELATIONSHIP_SUCCESS;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
||||
public class TestListenHTTP {
|
||||
private static final String SSL_CONTEXT_SERVICE_IDENTIFIER = "ssl-context";
|
||||
|
||||
private static final String HTTP_POST_METHOD = "POST";
|
||||
private static final String HTTP_BASE_PATH = "basePath";
|
||||
|
@ -64,9 +71,13 @@ public class TestListenHTTP {
|
|||
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
proc.shutdownHttpServer();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPOSTRequestsReceivedWithoutEL() throws Exception {
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
|
||||
|
@ -75,30 +86,79 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testPOSTRequestsReceivedWithEL() throws Exception {
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
|
||||
runner.assertValid();
|
||||
|
||||
testPOSTRequestsReceived();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReceivedWithoutEL() throws Exception {
|
||||
SSLContextService sslContextService = configureProcessorSslContextService();
|
||||
runner.setProperty(sslContextService, StandardRestrictedSSLContextService.RESTRICTED_SSL_ALGORITHM, "TLSv1.2");
|
||||
runner.enableControllerService(sslContextService);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.assertValid();
|
||||
|
||||
testPOSTRequestsReceived();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReceivedWithEL() throws Exception {
|
||||
SSLContextService sslContextService = configureProcessorSslContextService();
|
||||
runner.setProperty(sslContextService, StandardRestrictedSSLContextService.RESTRICTED_SSL_ALGORITHM, "TLSv1.2");
|
||||
runner.enableControllerService(sslContextService);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
|
||||
runner.assertValid();
|
||||
|
||||
testPOSTRequestsReceived();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecureInvalidSSLConfiguration() throws Exception {
|
||||
SSLContextService sslContextService = configureInvalidProcessorSslContextService();
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, "TLSv1.2");
|
||||
runner.enableControllerService(sslContextService);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
|
||||
runner.assertNotValid();
|
||||
}
|
||||
|
||||
private int executePOST(String message) throws Exception {
|
||||
final SSLContextService sslContextService = runner.getControllerService(SSL_CONTEXT_SERVICE_IDENTIFIER, SSLContextService.class);
|
||||
final boolean secure = (sslContextService != null);
|
||||
final String scheme = secure ? "https" : "http";
|
||||
final URL url = new URL(scheme + "://localhost:" + availablePort + "/" + HTTP_BASE_PATH);
|
||||
HttpURLConnection connection;
|
||||
|
||||
URL url= new URL("http://localhost:" + availablePort + "/" + HTTP_BASE_PATH);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
if(secure) {
|
||||
final HttpsURLConnection sslCon = (HttpsURLConnection) url.openConnection();
|
||||
final SSLContext sslContext = sslContextService.createSSLContext(SSLContextService.ClientAuth.WANT);
|
||||
sslCon.setSSLSocketFactory(sslContext.getSocketFactory());
|
||||
connection = sslCon;
|
||||
|
||||
} else {
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
connection.setRequestMethod(HTTP_POST_METHOD);
|
||||
connection.setDoOutput(true);
|
||||
|
||||
final DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
|
||||
|
||||
con.setRequestMethod(HTTP_POST_METHOD);
|
||||
con.setDoOutput(true);
|
||||
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
|
||||
if (message!=null) {
|
||||
wr.writeBytes(message);
|
||||
}
|
||||
wr.flush();
|
||||
wr.close();
|
||||
|
||||
return con.getResponseCode();
|
||||
|
||||
return connection.getResponseCode();
|
||||
}
|
||||
|
||||
private void testPOSTRequestsReceived() throws Exception {
|
||||
final List<String> messages = new ArrayList<>();
|
||||
messages.add("payload 1");
|
||||
|
@ -122,7 +182,7 @@ public class TestListenHTTP {
|
|||
|
||||
final ProcessSessionFactory processSessionFactory = runner.getProcessSessionFactory();
|
||||
final ProcessContext context = runner.getProcessContext();
|
||||
proc.createHttpServer(context);
|
||||
proc.createHttpServer(context);
|
||||
|
||||
Runnable sendMessagestoWebServer = () -> {
|
||||
try {
|
||||
|
@ -151,18 +211,30 @@ public class TestListenHTTP {
|
|||
}
|
||||
|
||||
private SSLContextService configureProcessorSslContextService() throws InitializationException {
|
||||
final SSLContextService sslContextService = new StandardSSLContextService();
|
||||
runner.addControllerService("ssl-context", sslContextService);
|
||||
final SSLContextService sslContextService = new StandardRestrictedSSLContextService();
|
||||
runner.addControllerService(SSL_CONTEXT_SERVICE_IDENTIFIER, sslContextService);
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/localhost-ts.jks");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "localtest");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, "JKS");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, "src/test/resources/localhost-ks.jks");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, "localtest");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, "JKS");
|
||||
runner.enableControllerService(sslContextService);
|
||||
|
||||
runner.setProperty(ListenHTTP.SSL_CONTEXT_SERVICE, "ssl-context");
|
||||
runner.setProperty(ListenHTTP.SSL_CONTEXT_SERVICE, SSL_CONTEXT_SERVICE_IDENTIFIER);
|
||||
return sslContextService;
|
||||
}
|
||||
|
||||
private SSLContextService configureInvalidProcessorSslContextService() throws InitializationException {
|
||||
final SSLContextService sslContextService = new StandardSSLContextService();
|
||||
runner.addControllerService(SSL_CONTEXT_SERVICE_IDENTIFIER, sslContextService);
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/localhost-ts.jks");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "localtest");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, "JKS");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, "src/test/resources/localhost-ks.jks");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, "localtest");
|
||||
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, "JKS");
|
||||
|
||||
runner.setProperty(ListenHTTP.SSL_CONTEXT_SERVICE, SSL_CONTEXT_SERVICE_IDENTIFIER);
|
||||
return sslContextService;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.nifi.ssl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.Tags;
|
||||
import org.apache.nifi.components.PropertyDescriptor;
|
||||
import org.apache.nifi.components.ValidationContext;
|
||||
import org.apache.nifi.processor.util.StandardValidators;
|
||||
|
||||
/**
|
||||
* This class is functionally the same as {@link StandardSSLContextService}, but it restricts the allowable
|
||||
* values that can be selected for TLS/SSL protocols.
|
||||
*/
|
||||
@Tags({"tls", "ssl", "secure", "certificate", "keystore", "truststore", "jks", "p12", "pkcs12", "pkcs"})
|
||||
@CapabilityDescription("Restricted implementation of the SSLContextService. Provides the ability to configure "
|
||||
+ "keystore and/or truststore properties once and reuse that configuration throughout the application, "
|
||||
+ "but only allows a restricted set of TLS/SSL protocols to be chosen (no SSL protocols are supported). The set of protocols selectable will "
|
||||
+ "evolve over time as new protocols emerge and older protocols are deprecated. This service is recommended "
|
||||
+ "over StandardSSLContextService if a component doesn't expect to communicate with legacy systems since it is "
|
||||
+ "unlikely that legacy systems will support these protocols.")
|
||||
public class StandardRestrictedSSLContextService extends StandardSSLContextService implements RestrictedSSLContextService {
|
||||
|
||||
public static final PropertyDescriptor RESTRICTED_SSL_ALGORITHM = new PropertyDescriptor.Builder()
|
||||
.name("SSL Protocol")
|
||||
.displayName("TLS Protocol")
|
||||
.defaultValue("TLS")
|
||||
.required(false)
|
||||
.allowableValues(RestrictedSSLContextService.buildAlgorithmAllowableValues())
|
||||
.description("The algorithm to use for this SSL context. By default, this will choose the highest supported TLS protocol version.")
|
||||
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
private static final List<PropertyDescriptor> properties;
|
||||
|
||||
static {
|
||||
List<PropertyDescriptor> props = new ArrayList<>();
|
||||
props.add(KEYSTORE);
|
||||
props.add(KEYSTORE_PASSWORD);
|
||||
props.add(KEY_PASSWORD);
|
||||
props.add(KEYSTORE_TYPE);
|
||||
props.add(TRUSTSTORE);
|
||||
props.add(TRUSTSTORE_PASSWORD);
|
||||
props.add(TRUSTSTORE_TYPE);
|
||||
props.add(RESTRICTED_SSL_ALGORITHM);
|
||||
properties = Collections.unmodifiableList(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSslAlgorithm() {
|
||||
return configContext.getProperty(RESTRICTED_SSL_ALGORITHM).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSSLProtocolForValidation(final ValidationContext validationContext) {
|
||||
return validationContext.getProperty(RESTRICTED_SSL_ALGORITHM).getValue();
|
||||
}
|
||||
}
|
|
@ -18,20 +18,15 @@ package org.apache.nifi.ssl;
|
|||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.Tags;
|
||||
import org.apache.nifi.annotation.lifecycle.OnEnabled;
|
||||
import org.apache.nifi.components.AllowableValue;
|
||||
import org.apache.nifi.components.PropertyDescriptor;
|
||||
import org.apache.nifi.components.PropertyValue;
|
||||
import org.apache.nifi.components.ValidationContext;
|
||||
|
@ -48,7 +43,10 @@ import org.apache.nifi.security.util.SslContextFactory;
|
|||
|
||||
@Tags({"ssl", "secure", "certificate", "keystore", "truststore", "jks", "p12", "pkcs12", "pkcs", "tls"})
|
||||
@CapabilityDescription("Standard implementation of the SSLContextService. Provides the ability to configure "
|
||||
+ "keystore and/or truststore properties once and reuse that configuration throughout the application")
|
||||
+ "keystore and/or truststore properties once and reuse that configuration throughout the application. "
|
||||
+ "This service can be used to communicate with both legacy and modern systems. If you only need to "
|
||||
+ "communicate with non-legacy systems, then the StandardRestrictedSSLContextService is recommended as it only "
|
||||
+ "allows a specific set of SSL protocols to be chosen.")
|
||||
public class StandardSSLContextService extends AbstractControllerService implements SSLContextService {
|
||||
|
||||
public static final String STORE_TYPE_JKS = "JKS";
|
||||
|
@ -110,14 +108,14 @@ public class StandardSSLContextService extends AbstractControllerService impleme
|
|||
.displayName("TLS Protocol")
|
||||
.defaultValue("TLS")
|
||||
.required(false)
|
||||
.allowableValues(buildAlgorithmAllowableValues())
|
||||
.allowableValues(SSLContextService.buildAlgorithmAllowableValues())
|
||||
.description("The algorithm to use for this TLS/SSL context")
|
||||
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
private static final List<PropertyDescriptor> properties;
|
||||
private ConfigurationContext configContext;
|
||||
protected ConfigurationContext configContext;
|
||||
private boolean isValidated;
|
||||
|
||||
// TODO: This can be made configurable if necessary
|
||||
|
@ -252,8 +250,12 @@ public class StandardSSLContextService extends AbstractControllerService impleme
|
|||
return VALIDATION_CACHE_EXPIRATION;
|
||||
}
|
||||
|
||||
protected String getSSLProtocolForValidation(final ValidationContext validationContext) {
|
||||
return validationContext.getProperty(SSL_ALGORITHM).getValue();
|
||||
}
|
||||
|
||||
private void verifySslConfig(final ValidationContext validationContext) throws ProcessException {
|
||||
final String protocol = validationContext.getProperty(SSL_ALGORITHM).getValue();
|
||||
final String protocol = getSSLProtocolForValidation(validationContext);
|
||||
try {
|
||||
final PropertyValue keyPasswdProp = validationContext.getProperty(KEY_PASSWORD);
|
||||
final char[] keyPassword = keyPasswdProp.isSet() ? keyPasswdProp.getValue().toCharArray() : null;
|
||||
|
@ -295,7 +297,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
|
|||
|
||||
@Override
|
||||
public SSLContext createSSLContext(final ClientAuth clientAuth) throws ProcessException {
|
||||
final String protocol = configContext.getProperty(SSL_ALGORITHM).getValue();
|
||||
final String protocol = getSslAlgorithm();
|
||||
try {
|
||||
final PropertyValue keyPasswdProp = configContext.getProperty(KEY_PASSWORD);
|
||||
final char[] keyPassword = keyPasswdProp.isSet() ? keyPasswdProp.getValue().toCharArray() : null;
|
||||
|
@ -458,36 +460,6 @@ public class StandardSSLContextService extends AbstractControllerService impleme
|
|||
KEYSTORE, TRUSTSTORE
|
||||
}
|
||||
|
||||
private static AllowableValue[] buildAlgorithmAllowableValues() {
|
||||
final Set<String> supportedProtocols = new HashSet<>();
|
||||
|
||||
/*
|
||||
* Prepopulate protocols with generic instance types commonly used
|
||||
* see: http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
|
||||
*/
|
||||
supportedProtocols.add("SSL");
|
||||
supportedProtocols.add("TLS");
|
||||
|
||||
// Determine those provided by the JVM on the system
|
||||
try {
|
||||
supportedProtocols.addAll(Arrays.asList(SSLContext.getDefault().createSSLEngine().getSupportedProtocols()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignored as default is used
|
||||
}
|
||||
|
||||
final int numProtocols = supportedProtocols.size();
|
||||
|
||||
// Sort for consistent presentation in configuration views
|
||||
final List<String> supportedProtocolList = new ArrayList<>(supportedProtocols);
|
||||
Collections.sort(supportedProtocolList);
|
||||
|
||||
final List<AllowableValue> protocolAllowableValues = new ArrayList<>();
|
||||
for (final String protocol : supportedProtocolList) {
|
||||
protocolAllowableValues.add(new AllowableValue(protocol));
|
||||
}
|
||||
return protocolAllowableValues.toArray(new AllowableValue[numProtocols]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SSLContextService[id=" + getIdentifier() + "]";
|
||||
|
|
|
@ -12,4 +12,5 @@
|
|||
# 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.
|
||||
org.apache.nifi.ssl.StandardSSLContextService
|
||||
org.apache.nifi.ssl.StandardSSLContextService
|
||||
org.apache.nifi.ssl.StandardRestrictedSSLContextService
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.nifi.ssl;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.IsNull.notNullValue;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.apache.nifi.components.AllowableValue;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RestrictedSSLContextServiceTest {
|
||||
|
||||
@Test
|
||||
public void testTLSAlgorithms() {
|
||||
final Set<String> expected = new HashSet<>();
|
||||
expected.add("TLS");
|
||||
expected.add("TLSv1.2");
|
||||
|
||||
final AllowableValue[] allowableValues = RestrictedSSLContextService.buildAlgorithmAllowableValues();
|
||||
assertThat(allowableValues, notNullValue());
|
||||
assertThat(allowableValues.length, equalTo(expected.size()));
|
||||
for(final AllowableValue value : allowableValues) {
|
||||
assertTrue(expected.contains(value.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,9 @@
|
|||
*/
|
||||
package org.apache.nifi.ssl;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.IsNull.notNullValue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -23,9 +26,17 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.nifi.components.AllowableValue;
|
||||
import org.apache.nifi.components.ValidationContext;
|
||||
import org.apache.nifi.components.ValidationResult;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
|
@ -311,4 +322,22 @@ public class SSLContextServiceTest {
|
|||
Assert.fail("Should not have thrown a exception " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSSLAlgorithms() throws NoSuchAlgorithmException {
|
||||
final AllowableValue[] allowableValues = SSLContextService.buildAlgorithmAllowableValues();
|
||||
|
||||
// we expect TLS, SSL, and all available configured JVM protocols
|
||||
final Set<String> expected = new HashSet<>();
|
||||
expected.add("SSL");
|
||||
expected.add("TLS");
|
||||
final String[] supportedProtocols = SSLContext.getDefault().createSSLEngine().getSupportedProtocols();
|
||||
expected.addAll(Arrays.asList(supportedProtocols));
|
||||
|
||||
assertThat(allowableValues, notNullValue());
|
||||
assertThat(allowableValues.length, equalTo(expected.size()));
|
||||
for(final AllowableValue value : allowableValues) {
|
||||
assertTrue(expected.contains(value.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.nifi.ssl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.nifi.components.AllowableValue;
|
||||
|
||||
/**
|
||||
* Simple extension of the regular {@link SSLContextService} to allow for restricted implementations
|
||||
* of that interface.
|
||||
*/
|
||||
public interface RestrictedSSLContextService extends SSLContextService {
|
||||
|
||||
/**
|
||||
* Build a restricted set of allowable TLS protocol algorithms.
|
||||
*
|
||||
* @return the computed set of allowable values
|
||||
*/
|
||||
static AllowableValue[] buildAlgorithmAllowableValues() {
|
||||
final Set<String> supportedProtocols = new HashSet<>();
|
||||
|
||||
/*
|
||||
* Prepopulate protocols with generic instance types commonly used
|
||||
* see: http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
|
||||
*/
|
||||
supportedProtocols.add("TLS");
|
||||
|
||||
/*
|
||||
* Add specifically supported TLS versions
|
||||
*/
|
||||
supportedProtocols.add("TLSv1.2");
|
||||
|
||||
final int numProtocols = supportedProtocols.size();
|
||||
|
||||
// Sort for consistent presentation in configuration views
|
||||
final List<String> supportedProtocolList = new ArrayList<>(supportedProtocols);
|
||||
Collections.sort(supportedProtocolList);
|
||||
|
||||
final List<AllowableValue> protocolAllowableValues = new ArrayList<>();
|
||||
for (final String protocol : supportedProtocolList) {
|
||||
protocolAllowableValues.add(new AllowableValue(protocol));
|
||||
}
|
||||
return protocolAllowableValues.toArray(new AllowableValue[numProtocols]);
|
||||
}
|
||||
}
|
|
@ -16,10 +16,19 @@
|
|||
*/
|
||||
package org.apache.nifi.ssl;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.Tags;
|
||||
import org.apache.nifi.components.AllowableValue;
|
||||
import org.apache.nifi.controller.ControllerService;
|
||||
import org.apache.nifi.processor.exception.ProcessException;
|
||||
|
||||
|
@ -60,4 +69,39 @@ public interface SSLContextService extends ControllerService {
|
|||
public boolean isKeyStoreConfigured();
|
||||
|
||||
String getSslAlgorithm();
|
||||
|
||||
/**
|
||||
* Build a set of allowable TLS/SSL protocol algorithms based on JVM configuration.
|
||||
*
|
||||
* @return the computed set of allowable values
|
||||
*/
|
||||
static AllowableValue[] buildAlgorithmAllowableValues() {
|
||||
final Set<String> supportedProtocols = new HashSet<>();
|
||||
|
||||
/*
|
||||
* Prepopulate protocols with generic instance types commonly used
|
||||
* see: http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
|
||||
*/
|
||||
supportedProtocols.add("TLS");
|
||||
supportedProtocols.add("SSL");
|
||||
|
||||
// Determine those provided by the JVM on the system
|
||||
try {
|
||||
supportedProtocols.addAll(Arrays.asList(SSLContext.getDefault().createSSLEngine().getSupportedProtocols()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignored as default is used
|
||||
}
|
||||
|
||||
final int numProtocols = supportedProtocols.size();
|
||||
|
||||
// Sort for consistent presentation in configuration views
|
||||
final List<String> supportedProtocolList = new ArrayList<>(supportedProtocols);
|
||||
Collections.sort(supportedProtocolList);
|
||||
|
||||
final List<AllowableValue> protocolAllowableValues = new ArrayList<>();
|
||||
for (final String protocol : supportedProtocolList) {
|
||||
protocolAllowableValues.add(new AllowableValue(protocol));
|
||||
}
|
||||
return protocolAllowableValues.toArray(new AllowableValue[numProtocols]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue