Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update AbstractContainerIT to allow for HTTPS connections Using a pre-generated keystore (master) #260

Merged
merged 1 commit into from
Oct 17, 2020
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
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