| /* |
| * 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.registry.web; |
| |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.eclipse.jetty.server.HttpConfiguration; |
| import org.eclipse.jetty.server.HttpConnectionFactory; |
| import org.eclipse.jetty.server.SecureRequestCustomizer; |
| import org.eclipse.jetty.server.ServerConnector; |
| import org.eclipse.jetty.server.SslConnectionFactory; |
| import org.eclipse.jetty.util.ssl.SslContextFactory; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.boot.autoconfigure.web.ServerProperties; |
| import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; |
| import org.springframework.boot.web.server.Ssl; |
| import org.springframework.boot.web.server.WebServerFactoryCustomizer; |
| import org.springframework.stereotype.Component; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * This customizer fixes integration tests. The customizer is the only way we can pass config from Spring Boot to Jetty. |
| * It sets the endpointIdentificationAlgorithm to null, which stops the Jetty server attempting to validate a hostname in the client certificate's SAN. |
| **/ |
| @Component |
| public class JettyITServerCustomizer implements WebServerFactoryCustomizer<JettyServletWebServerFactory> { |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(JettyITServerCustomizer.class); |
| |
| @Autowired |
| private ServerProperties serverProperties; |
| |
| private static final int HEADER_BUFFER_SIZE = 16 * 1024; // 16kb |
| |
| @Override |
| public void customize(final JettyServletWebServerFactory factory) { |
| LOGGER.info("Customizing Jetty server for integration tests..."); |
| |
| factory.addServerCustomizers((server) -> { |
| final Ssl sslProperties = serverProperties.getSsl(); |
| if (sslProperties != null) { |
| createSslContextFactory(sslProperties); |
| ServerConnector con = (ServerConnector) server.getConnectors()[0]; |
| int existingConnectorPort = con.getLocalPort(); |
| |
| // create the http configuration |
| final HttpConfiguration httpConfiguration = new HttpConfiguration(); |
| httpConfiguration.setRequestHeaderSize(HEADER_BUFFER_SIZE); |
| httpConfiguration.setResponseHeaderSize(HEADER_BUFFER_SIZE); |
| |
| // add some secure config |
| final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration); |
| httpsConfiguration.setSecureScheme("https"); |
| httpsConfiguration.setSecurePort(existingConnectorPort); |
| httpsConfiguration.addCustomizer(new SecureRequestCustomizer()); |
| |
| // build the connector with the endpoint identification algorithm set to null |
| final ServerConnector httpsConnector = new ServerConnector(server, |
| new SslConnectionFactory(createSslContextFactory(sslProperties), "http/1.1"), |
| new HttpConnectionFactory(httpsConfiguration)); |
| server.removeConnector(con); |
| server.addConnector(httpsConnector); |
| } |
| }); |
| |
| LOGGER.info("JettyServer is customized"); |
| } |
| |
| private SslContextFactory createSslContextFactory(Ssl properties) { |
| // Calling SslContextFactory.Server() calls setEndpointIdentificationAlgorithm(null). |
| // This ensures that Jetty server does not attempt to validate a hostname in the client certificate's SAN. |
| final SslContextFactory.Server contextFactory = new SslContextFactory.Server(); |
| |
| // if needClientAuth is false then set want to true so we can optionally use certs |
| if(properties.getClientAuth() == Ssl.ClientAuth.NEED) { |
| LOGGER.info("Setting Jetty's SSLContextFactory needClientAuth to true"); |
| contextFactory.setNeedClientAuth(true); |
| } else { |
| LOGGER.info("Setting Jetty's SSLContextFactory wantClientAuth to true"); |
| contextFactory.setWantClientAuth(true); |
| } |
| |
| /* below code sets JSSE system properties when values are provided */ |
| // keystore properties |
| if (StringUtils.isNotBlank(properties.getKeyStore())) { |
| contextFactory.setKeyStorePath(properties.getKeyStore()); |
| } |
| if (StringUtils.isNotBlank(properties.getKeyStoreType())) { |
| contextFactory.setKeyStoreType(properties.getKeyStoreType()); |
| } |
| final String keystorePassword = properties.getKeyStorePassword(); |
| final String keyPassword = properties.getKeyPassword(); |
| if (StringUtils.isNotBlank(keystorePassword)) { |
| // if no key password was provided, then assume the keystore password is the same as the key password. |
| final String defaultKeyPassword = (StringUtils.isBlank(keyPassword)) ? keystorePassword : keyPassword; |
| contextFactory.setKeyManagerPassword(keystorePassword); |
| contextFactory.setKeyStorePassword(defaultKeyPassword); |
| } else if (StringUtils.isNotBlank(keyPassword)) { |
| // since no keystore password was provided, there will be no keystore integrity check |
| contextFactory.setKeyStorePassword(keyPassword); |
| } |
| |
| // truststore properties |
| if (StringUtils.isNotBlank(properties.getTrustStore())) { |
| contextFactory.setTrustStorePath(properties.getTrustStore()); |
| } |
| if (StringUtils.isNotBlank(properties.getTrustStoreType())) { |
| contextFactory.setTrustStoreType(properties.getTrustStoreType()); |
| } |
| if (StringUtils.isNotBlank(properties.getTrustStorePassword())) { |
| contextFactory.setTrustStorePassword(properties.getTrustStorePassword()); |
| } |
| |
| return contextFactory; |
| } |
| |
| } |