Skip to content

Commit

Permalink
Merge pull request #260 from apache/tls-for-its
Browse files Browse the repository at this point in the history
Update AbstractContainerIT to allow for HTTPS connections Using a pre-generated keystore (master)
  • Loading branch information
bdemers authored Oct 17, 2020
2 parents c632a62 + b68739b commit 1ad362a
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,15 @@
import com.github.mjeanroy.junit.servers.jetty.EmbeddedJetty;
import com.github.mjeanroy.junit.servers.jetty.EmbeddedJettyConfiguration;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.resource.FileResource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
Expand All @@ -39,7 +46,13 @@

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

import static com.github.mjeanroy.junit.servers.commons.Strings.isNotBlank;
import static org.eclipse.jetty.util.resource.Resource.newResource;
Expand All @@ -50,8 +63,13 @@ public abstract class AbstractContainerIT {

protected static EmbeddedJetty jetty;

protected static int tlsPort;

protected final WebClient webClient = new WebClient();

protected static final File TEST_KEYSTORE_PATH = setupKeyStore();
protected static final String TEST_KEYSTORE_PASSWORD = "password";

@BeforeClass
public static void startContainer() throws Exception {

Expand Down Expand Up @@ -98,6 +116,7 @@ protected WebAppContext createdWebAppContext() throws Exception {

Server server = getDelegate();

// web app
ctx.setParentLoaderPriority(true);
ctx.setWar(webapp);
ctx.setServer(server);
Expand All @@ -109,6 +128,26 @@ protected WebAppContext createdWebAppContext() throws Exception {
}
};

Server server = jetty.getDelegate();

// TLS
tlsPort = getFreePort();

final SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(TEST_KEYSTORE_PATH.getAbsolutePath());
sslContextFactory.setKeyStorePassword(TEST_KEYSTORE_PASSWORD);
sslContextFactory.setKeyManagerPassword(TEST_KEYSTORE_PASSWORD);

HttpConfiguration https = new HttpConfiguration();
https.addCustomizer(new SecureRequestCustomizer());

final ServerConnector httpsConnector = new ServerConnector(
server,
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(https));
httpsConnector.setPort(tlsPort);
server.addConnector(httpsConnector);

jetty.start();

assertTrue(jetty.isStarted());
Expand All @@ -118,6 +157,10 @@ protected static String getBaseUri() {
return "http://localhost:" + jetty.getPort() + "/";
}

protected static String getTlsBaseUri() {
return "https://localhost:" + tlsPort + "/";
}

protected static String getWarDir() {
File[] warFiles = new File("target").listFiles(new FilenameFilter() {
@Override
Expand Down Expand Up @@ -150,4 +193,30 @@ public static void stopContainer() {
jetty.stop();
}
}

private static int getFreePort() {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
} catch (IOException e) {
throw new IllegalStateException("Failed to allocate free port", e);
}
}

// Dealing with a keystore is NOT fun, it's easier to script one with the keytool
// see src/main/resources/createKeyStore.sh for more info
private static File setupKeyStore() {
try {
Path outKeyStoreFile = File.createTempFile("test-keystore", "jks").toPath();
URL keyStoreResource = Thread.currentThread().getContextClassLoader().getResource("test-keystore.jks");
Files.copy(keyStoreResource.openStream(), outKeyStoreFile, StandardCopyOption.REPLACE_EXISTING);
File keyStoreFile = outKeyStoreFile.toFile();

// clients will pick up the ssl keystore this way, so just set SSL properties
System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
System.setProperty("javax.net.ssl.trustStorePassword", TEST_KEYSTORE_PASSWORD);
return keyStoreFile;
} catch (IOException e) {
throw new IllegalStateException("Failed to create test keystore", e);
}
}
}
65 changes: 65 additions & 0 deletions integration-tests/support/src/main/resources/createKeyStore.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
#
# 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.
#
set -e

# This script will generate a self signed certificate, store it in a Java keystore and PEM format.
# The generated certificate is good for 10 years, and should NOT need to be recreated until then (unless
# changes to the certificate are needed).
# Last run with Java 1.8.0_252
#
# Usage: For JVM based applications, the resulting keystore MUST be configured to in order for clients to accept TLS
# connections. Typical usage requires setting of the Java system property 'javax.net.ssl.trustStore' to the file path
# of the keystore. Either by adding a command line parameter: `-Djavax.net.ssl.trustStore=/path/to/keystore` or
# programmatically: `System.setProperty("javax.net.ssl.trustStore", "/path/to/keystore")`

dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
keystore_file="${dir}/test-keystore.jks"
file_prefix="${dir}/test-keystore"
rm -f ${file_prefix}*

echo "generate new keystore"
keytool -genkey \
-keystore "${file_prefix}.jks" \
-alias "localhost" \
-keyalg RSA \
-keysize 2048 \
-validity 3650 \
-dname "C=US; ST=Unknown; L=Springfield; O=Unknown; OU=Unknown; CN=localhost" \
-ext SAN=dns:localhost \
-keypass password \
-storepass password \
-noprompt

echo "self sign"
keytool -selfcert \
-alias "localhost" \
-keystore "${file_prefix}.jks" \
-validity 3650 \
-storepass password \
-noprompt

echo "export to pem"
keytool -export \
-alias "localhost" \
-keystore "${file_prefix}.jks" \
-rfc \
-file "${file_prefix}.pem" \
-storepass password \
-noprompt
Binary file not shown.
22 changes: 22 additions & 0 deletions integration-tests/support/src/main/resources/test-keystore.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDjzCCAnegAwIBAgIEDGro0DANBgkqhkiG9w0BAQsFADBtMRIwEAYDVQQDEwls
b2NhbGhvc3QxEDAOBgNVBAsTB1Vua25vd24xEDAOBgNVBAoTB1Vua25vd24xFDAS
BgNVBAcTC1NwcmluZ2ZpZWxkMRAwDgYDVQQIEwdVbmtub3duMQswCQYDVQQGEwJV
UzAeFw0yMDEwMTUxNTE4MThaFw0zMDEwMTMxNTE4MThaMG0xEjAQBgNVBAMTCWxv
Y2FsaG9zdDEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEUMBIG
A1UEBxMLU3ByaW5nZmllbGQxEDAOBgNVBAgTB1Vua25vd24xCzAJBgNVBAYTAlVT
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmlRyaRblUwPFtvMR+NZa
hW27UEidgyOIXeXS+iwOXbc49B9ADA6utnQybiaKWG5IXMCD1dsPd3Pqx/5Bqmhd
1QQJCQxjx1McieEhXuhON7wqpYlmt+sIVUvPNHFldyuv/CqqZLBLWYj1uaba15E7
oLPVT+XBRY/uvWR4q6HtweNcBU0m183eaFSzYwpUuhHCgI5V3+qC40eDoCPd6jrA
S6BGldvR+Ysbe5fGQUmL7IUks6YO/AGMRZjmR5pKGE5dmoCPsuusnmHvm8iKfkh0
KzMDcxpY0ZlwNXIsNfIuIBBBAkGZG2RVmJeOJG6Bv6DCSt3ypOLbb0vHiEm7wLrL
YwIDAQABozcwNTAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFNDDMtIb
fscg7DRkbQV6ZuKNG7YlMA0GCSqGSIb3DQEBCwUAA4IBAQCLJ6743hPQNx1Sop0v
0Fm+2dm66Xj77aGfB9Xq64/BsQP+imWYuAv0oQq2tgCtXSMBhyfBKQKfEbQcd+m5
WsiSfkxpvcCWR7Ttc7GuElG6Bb+CqLxLDZuEogIOsVM7MgEfqC5zp94vLhSrLmrl
HzZzisVL/PH0wMpAuD0IRWgdYyPWavBipVHCJYWJRehQon9D+qi1EuwSOZYvq9af
YSfZtkndXrVfpmnO9+xrszO5Yu9qgZfvRLhpal/cmsBpRVyZIWdUj4B1wfPtKT3/
x+85LN5wNyiRkROf1M0S6mb2Ac7zMJrNI5hKfKM2OOLC/g/ec3NOWH8/7k9FlcRv
alPL
-----END CERTIFICATE-----
2 changes: 2 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
<exclude>**/*.json</exclude>
<exclude>**/spring.factories</exclude>
<exclude>**/spring.provides</exclude>
<exclude>**/*.iml</exclude>
</excludes>
</configuration>
</plugin>
Expand Down Expand Up @@ -1299,6 +1300,7 @@
<exclude>**/*.json</exclude>
<exclude>**/spring.factories</exclude>
<exclude>**/spring.provides</exclude>
<exclude>**/*.iml</exclude>
</excludes>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@
import java.io.IOException;
import java.net.MalformedURLException;

public class ContainerIntegrationIT extends AbstractContainerIT {
public class WebAppContainerIntegrationIT extends AbstractContainerIT {

protected final WebClient webClient = new WebClient();

@Before
public void logOut() throws IOException {
// Make sure we are logged out
final HtmlPage homePage = webClient.getPage(getBaseUri());
final HtmlPage homePage = webClient.getPage(getTlsBaseUri());
try {
homePage.getAnchorByHref("/logout").click();
}
Expand All @@ -50,7 +50,7 @@ public void logOut() throws IOException {
@Test
public void logIn() throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException {

HtmlPage page = webClient.getPage(getBaseUri() + "login.jsp");
HtmlPage page = webClient.getPage(getTlsBaseUri() + "login.jsp");
HtmlForm form = page.getFormByName("loginform");
form.<HtmlInput>getInputByName("username").setValueAttribute("root");
form.<HtmlInput>getInputByName("password").setValueAttribute("secret");
Expand Down

0 comments on commit 1ad362a

Please sign in to comment.