From d8bfc54ac9bdd1eddb5b6810c310287880a0cb00 Mon Sep 17 00:00:00 2001 From: Prarthona Paul Date: Tue, 10 Oct 2023 15:58:03 -0400 Subject: [PATCH] [WFLY-18475] helloworld-mutual-ssl-secured Quickstart Common Enhancements CY2023Q3 --- ...start_helloworld-mutual-ssl-secured_ci.yml | 16 +++ helloworld-mutual-ssl-secured/README.adoc | 35 ++---- .../configure-client-certs.cli | 19 +++ .../configure-client-truststore.cli | 11 ++ .../configure-ssl.cli | 11 +- helloworld-mutual-ssl-secured/pom.xml | 106 +++++++++++++++- .../restore-client-certs.cli | 8 ++ .../restore-client-truststore.cli | 2 + .../restore-configuration.cli | 3 + .../helloworld_mutual_ssl/BasicRuntimeIT.java | 119 ++++++++++++++++++ .../helloworld_mutual_ssl/keystoreUtil.java | 59 +++++++++ 11 files changed, 358 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/quickstart_helloworld-mutual-ssl-secured_ci.yml create mode 100644 helloworld-mutual-ssl-secured/configure-client-certs.cli create mode 100644 helloworld-mutual-ssl-secured/configure-client-truststore.cli create mode 100644 helloworld-mutual-ssl-secured/restore-client-certs.cli create mode 100644 helloworld-mutual-ssl-secured/restore-client-truststore.cli create mode 100644 helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/BasicRuntimeIT.java create mode 100644 helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/keystoreUtil.java diff --git a/.github/workflows/quickstart_helloworld-mutual-ssl-secured_ci.yml b/.github/workflows/quickstart_helloworld-mutual-ssl-secured_ci.yml new file mode 100644 index 0000000000..bf0c1a6769 --- /dev/null +++ b/.github/workflows/quickstart_helloworld-mutual-ssl-secured_ci.yml @@ -0,0 +1,16 @@ +name: WildFly helloworld-mutual-ssl-secured Quickstart CI + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - 'helloworld-mutual-ssl-secured/**' + - '.github/workflows/quickstart_ci.yml' + +jobs: + call-quickstart_ci: + uses: ./.github/workflows/quickstart_ci.yml + with: + QUICKSTART_PATH: helloworld-mutual-ssl-secured + TEST_PROVISIONED_SERVER: true + TEST_OPENSHIFT: false \ No newline at end of file diff --git a/helloworld-mutual-ssl-secured/README.adoc b/helloworld-mutual-ssl-secured/README.adoc index 1dfbfdeab7..329aa96889 100644 --- a/helloworld-mutual-ssl-secured/README.adoc +++ b/helloworld-mutual-ssl-secured/README.adoc @@ -70,7 +70,7 @@ Notice that it sets the `first and last name` to `quickstartUser` and that this [source,options="nowrap"] ---- $>keytool -exportcert -keystore client.keystore -storetype pkcs12 -storepass secret -keypass secret -file client.crt -$>keytool -import -file client.crt -alias quickstartUser -keystore client.truststore -storepass secret +$>keytool -import -file client.crt -alias quickstartUser -keystore server.truststore -storepass secret Owner: CN=quickstartUser, OU=Sales, O=My Company, L=Sao Paulo, ST=Sao Paulo, C=BR Issuer: CN=quickstartUser, OU=Sales, O=My Company, L=Sao Paulo, ST=Sao Paulo, C=BR @@ -155,7 +155,7 @@ After stopping the server, open the `__{jbossHomeName}__/standalone/configuratio - + ---- @@ -231,7 +231,7 @@ It maps the `client_cert_domain` from the quickstart application to the `http-au [[test_the_server_ssl_configuration]] == Test the Server TLS Configuration -To test the TLS configuration, access: https://localhost:8443 +To test the TLS configuration, start {productName} and access: https://localhost:8443 If it is configured correctly, you should be asked to trust the server certificate. @@ -290,6 +290,8 @@ dzXZz0EjjWCPJk+LVEhEvH0GcWAp3x3irpNU4hRZLd0XomY0Z4NnUt7VMBNYDOxVxgT9qcLnEaEpIfYU ynfnMaOxI67FC2QzhfzERyKqHj47WuwN0xWbS/1gBypS2nUwvItyxaEQG2X5uQY8j8QoY9wcMzIIkP2Mk14gJGHUnA8= ---- +// Server Distribution Testing +include::../shared-doc/run-integration-tests-with-server-distribution.adoc[leveloffset=+2] // Undeploy the Quickstart include::../shared-doc/undeploy-the-quickstart.adoc[leveloffset=+1] @@ -320,7 +322,7 @@ $ cd __{jbossHomeName}__/standalone/configuration/ + NOTE: For Windows, use the `__{jbossHomeName}__\bin\standalone.bat` script. -. Remove the `clientCert.p12`, `client.crt`, and `client.truststore` files that were generated for this quickstart. +. Remove the `clientCert.p12`, `client.crt`, and `server.truststore` files that were generated for this quickstart. [[remove_the_client_certificate_from_your_browser]] == Remove the Client Certificate from Your Browser @@ -344,26 +346,11 @@ After you are done with this quickstart, remember to remove the certificate that . Select the *quickstartUser* certificate and click the *Delete* button. . The certificate has now been removed from the Mozilla Firefox browser. -// Run the Quickstart in Red Hat CodeReady Studio or Eclipse -include::../shared-doc/run-the-quickstart-in-jboss-developer-studio.adoc[leveloffset=+1] - -// Additional Red Hat CodeReady Studio instructions -* Make sure you configure the keystores and client certificates as described under xref:set_up_client_keystore_using_java_keytool[Set Up the Client Keystore Using Java Keytool]. -* Depending on the browser you choose, make sure you either xref:import_the_client_certificate_into_google_chrome[import the certificate into Google Chrome] or xref:import_the_client_certificate_into_mozilla_firefox[import the certificate into Mozilla Firefox]. -* Make sure you configure the server by running the JBoss CLI commands as described above under xref:configure_the_server[Configure the Server]. Stop the server at the end of that step. -* In {JBDSProductName}, choose *Window* –> *Web Browser*, then select the browser you chose to import the certificate. -* To deploy the application, right-click on the *{artifactId}* project and choose *Run As* –> *Run on Server*. -* Make sure you xref:restore_the_server_configuration[restore the {productName} server configuration] when you have completed testing this quickstart. - -// Debug the Application -include::../shared-doc/debug-the-application.adoc[leveloffset=+1] - -//************************************************* -// Product Release content only -//************************************************* -ifdef::ProductRelease[] +// Build and run sections for other environments/builds +ifndef::ProductRelease,EAPXPRelease[] +:server_provisioning_server_host: https://localhost:8443 +include::../shared-doc/build-and-run-the-quickstart-with-provisioned-server.adoc[leveloffset=+1] +endif::[] // Quickstart not compatible with OpenShift include::../shared-doc/openshift-incompatibility.adoc[leveloffset=+1] - -endif::[] diff --git a/helloworld-mutual-ssl-secured/configure-client-certs.cli b/helloworld-mutual-ssl-secured/configure-client-certs.cli new file mode 100644 index 0000000000..547a0d20f6 --- /dev/null +++ b/helloworld-mutual-ssl-secured/configure-client-certs.cli @@ -0,0 +1,19 @@ +# Configure a key-store in the Elytron subsystem. The path to the keystore file doesn’t actually have to exist yet. +/subsystem=elytron/key-store=clientKS:add(path=client.keystore.P12, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=PKCS12) + +# Generate a new key pair for the client. We'll use an RSA key of size 2048 and we'll use CN=quickstartUser +/subsystem=elytron/key-store=clientKS:generate-key-pair(alias=quickstartUser, algorithm=RSA, key-size=2048, validity=365, credential-reference={clear-text=secret}, distinguished-name="cn=quickstartUser") + +# Export the certificate to a file called clientCert.crt +/subsystem=elytron/key-store=clientKS:export-certificate(alias=quickstartUser, path=clientCert.crt, relative-to=jboss.server.config.dir, pem=true) + +# Create a the server's truststore +/subsystem=elytron/key-store=serverTS:add(path=server.truststore, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=PKCS12) + +# Import a certificate into the server's truststore +/subsystem=elytron/key-store=serverTS:import-certificate(alias=quickstartUser, path=clientCert.crt, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, validate=false) + +# Persist the changes we've made to the client's keystore and the server's truststore +/subsystem=elytron/key-store=serverTS:store() +/subsystem=elytron/key-store=clientKS:store() + diff --git a/helloworld-mutual-ssl-secured/configure-client-truststore.cli b/helloworld-mutual-ssl-secured/configure-client-truststore.cli new file mode 100644 index 0000000000..51b81a53e5 --- /dev/null +++ b/helloworld-mutual-ssl-secured/configure-client-truststore.cli @@ -0,0 +1,11 @@ +# Export the server's certificate to a file called serverCert.crt +/subsystem=elytron/key-store=applicationKS:export-certificate(alias=server, path=serverCert.crt, relative-to=jboss.server.config.dir, pem=true) + +# create a truststore for the client called client.truststore and add it to the server config directory +/subsystem=elytron/key-store=clientTS:add(path=client.truststore, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=PKCS12) + +# import that certificate into the client truststore +/subsystem=elytron/key-store=clientTS:import-certificate(alias=server, path=serverCert.crt, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, validate=false) + +# Use the store function to save the truststore file +/subsystem=elytron/key-store=clientTS:store() diff --git a/helloworld-mutual-ssl-secured/configure-ssl.cli b/helloworld-mutual-ssl-secured/configure-ssl.cli index bcb84b121e..45540dbf24 100644 --- a/helloworld-mutual-ssl-secured/configure-ssl.cli +++ b/helloworld-mutual-ssl-secured/configure-ssl.cli @@ -4,7 +4,7 @@ batch # Add the keystore and trust manager configuration in the elytron subsystem -/subsystem=elytron/key-store=qsTrustStore:add(path=client.truststore,relative-to=jboss.server.config.dir,type=JKS,credential-reference={clear-text=secret}) +/subsystem=elytron/key-store=qsTrustStore:add(path=server.truststore,relative-to=jboss.server.config.dir,type=PKCS12,credential-reference={clear-text=secret}) /subsystem=elytron/trust-manager=qsTrustManager:add(key-store=qsTrustStore) # Update the default server-ssl-context to reference the new trust-manager and require client auth @@ -29,10 +29,13 @@ batch # Add an application-security-domain in the undertow subsystem to map the client_cert_domain from the quickstart app to the http-authentication-factory /subsystem=undertow/application-security-domain=client_cert_domain:add(http-authentication-factory=quickstart-http-authentication) +#generate the key pair that the server would use for its keystore +/subsystem=elytron/key-store=applicationKS:generate-key-pair(alias=server, algorithm=RSA, key-size=2048, validity=365, credential-reference={clear-text=password}, distinguished-name="cn=localhost") + +/subsystem=elytron/key-store=applicationKS:store() + # Run the batch commands run-batch # Reload the server configuration -reload - - +#reload \ No newline at end of file diff --git a/helloworld-mutual-ssl-secured/pom.xml b/helloworld-mutual-ssl-secured/pom.xml index 6c3d721f5e..30c68d96a8 100644 --- a/helloworld-mutual-ssl-secured/pom.xml +++ b/helloworld-mutual-ssl-secured/pom.xml @@ -44,8 +44,12 @@ - - 30.0.0.Final + + 30.0.0.Final + + ${version.server} + 5.0.0.Final + 4.2.0.Final @@ -109,7 +113,7 @@ org.wildfly.bom wildfly-ee-with-tools - ${version.server.bom} + ${version.bom.ee} pom import @@ -139,5 +143,101 @@ jakarta.servlet-api provided + + + + junit + junit + test + + + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + + + + + + org.wildfly.plugins + wildfly-maven-plugin + ${version.plugin.wildfly} + + + + + + + provisioned-server + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + org.wildfly:wildfly-galleon-pack:${version.server} + + + + + cloud-server + undertow-https + + + + + + + + + + false + + + + ROOT.war + + + + + package + + + + + + + + + integration-testing + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/BasicRuntimeIT + + + + + + integration-test + verify + + + + + + + + diff --git a/helloworld-mutual-ssl-secured/restore-client-certs.cli b/helloworld-mutual-ssl-secured/restore-client-certs.cli new file mode 100644 index 0000000000..d18658df13 --- /dev/null +++ b/helloworld-mutual-ssl-secured/restore-client-certs.cli @@ -0,0 +1,8 @@ +#remove the keypairs and certificates from the keystore and truststore +/subsystem=elytron/key-store=serverTS:remove-alias(alias=quickstartUser) +/subsystem=elytron/key-store=clientKS:remove-alias(alias=quickstartUser) + +#remove the keystore and truststore +/subsystem=elytron/key-store=serverTS:remove +/subsystem=elytron/key-store=clientKS:remove + diff --git a/helloworld-mutual-ssl-secured/restore-client-truststore.cli b/helloworld-mutual-ssl-secured/restore-client-truststore.cli new file mode 100644 index 0000000000..f8e4957ba6 --- /dev/null +++ b/helloworld-mutual-ssl-secured/restore-client-truststore.cli @@ -0,0 +1,2 @@ +/subsystem=elytron/key-store=clientTS:remove-alias(alias=server) +/subsystem=elytron/key-store=clientTS:remove \ No newline at end of file diff --git a/helloworld-mutual-ssl-secured/restore-configuration.cli b/helloworld-mutual-ssl-secured/restore-configuration.cli index 0cf5606b9d..dff3152ceb 100644 --- a/helloworld-mutual-ssl-secured/restore-configuration.cli +++ b/helloworld-mutual-ssl-secured/restore-configuration.cli @@ -3,6 +3,9 @@ # Start batching commands batch +# Remove the keypair with the alias server from the application keystore +/subsystem=elytron/key-store=applicationKS:remove-alias(alias=server) + # Remove the application-security-domain mapping that was added for the quickstart /subsystem=undertow/application-security-domain=client_cert_domain:remove diff --git a/helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/BasicRuntimeIT.java b/helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/BasicRuntimeIT.java new file mode 100644 index 0000000000..f93055a18c --- /dev/null +++ b/helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/BasicRuntimeIT.java @@ -0,0 +1,119 @@ +/* + * Copyright 2023 JBoss by Red Hat. + * + * 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.jboss.as.quickstarts.helloworld_mutual_ssl; + +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.KeyStore; +import java.security.KeyStoreException; + +import org.apache.http.client.HttpClient; +import org.apache.http.config.Registry; +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.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultSchemePortResolver; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; +import javax.net.ssl.SSLContext; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.HttpResponse; + + +import static org.jboss.as.quickstarts.helloworld_mutual_ssl.keystoreUtil.loadKeyPairFromKeyStore; +import static org.junit.Assert.assertEquals; + +/** + * The very basic runtime integration testing. + * @author Prarthona Paul + * @author emartins + */ +public class BasicRuntimeIT { + + private static final String DEFAULT_SERVER_HOST = "https://localhost:8443/helloworld-mutual-ssl-secured"; + + @Test + public void testHTTPEndpointIsAvailable() throws IOException, URISyntaxException, KeyStoreException { + String serverHost = System.getenv("SERVER_HOST"); + if (serverHost == null) { + serverHost = System.getProperty("server.host"); + } + if (serverHost == null) { + serverHost = DEFAULT_SERVER_HOST; + } + String serverDir = System.getenv("SERVER_HOME"); + if (serverDir == null) { + if (System.getProperty("jboss.server.config.dir").contains("target/server")) { + serverDir = System.getProperty("user.dir") + "/" + System.getProperty("jboss.server.config.dir"); + } else { + serverDir = System.getProperty("jboss.server.config.dir"); + } + } + HttpGet request = new HttpGet(new URI(serverHost+"/")); + KeyStore trustStore = loadKeyPairFromKeyStore(serverDir, "application.keystore", "password", "server", "PKCS12"); + final HttpClient client = getHttpClientWithSSL(new File(serverDir + "/client.keystore.P12"), "secret", "PKCS12", new File(serverDir + "/client.truststore"), "password", "PKCS12"); + HttpResponse response = client.execute(request); + assertEquals(200, response.getStatusLine().getStatusCode()); + } + + public HttpClient getHttpClientWithSSL(File keyStoreFile, String keyStorePassword, String keyStoreProvider, + File trustStoreFile, String trustStorePassword, String trustStoreProvider) { + + try { + KeyStore trustStore = KeyStore.getInstance(trustStoreProvider); + try (FileInputStream fis = new FileInputStream(trustStoreFile)) { + trustStore.load(fis, trustStorePassword.toCharArray()); + } + SSLContextBuilder sslContextBuilder = SSLContexts.custom() + .setProtocol("TLS") + .loadTrustMaterial(trustStore, null); + if (keyStoreFile != null) { + KeyStore keyStore = KeyStore.getInstance(keyStoreProvider); + try (FileInputStream fis = new FileInputStream(keyStoreFile)) { + keyStore.load(fis, keyStorePassword.toCharArray()); + } + sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword.toCharArray(), null); + } + SSLContext sslContext = sslContextBuilder.build(); + SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); + + Registry registry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", socketFactory) + .build(); + + return HttpClientBuilder.create() + .setSSLSocketFactory(socketFactory) +// .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) + .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) + .setConnectionManager(new PoolingHttpClientConnectionManager(registry)) + .setSchemePortResolver(new DefaultSchemePortResolver()) + .build(); + + } catch (Exception e) { + throw new RuntimeException("Creating HttpClient with customized SSL failed. We are returning the default one instead.", e); + } + } +} \ No newline at end of file diff --git a/helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/keystoreUtil.java b/helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/keystoreUtil.java new file mode 100644 index 0000000000..4602ba8d23 --- /dev/null +++ b/helloworld-mutual-ssl-secured/src/test/java/org/jboss/as/quickstarts/helloworld_mutual_ssl/keystoreUtil.java @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2020 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * 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.jboss.as.quickstarts.helloworld_mutual_ssl; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; + +/** + * An interface to obtain the KeyPair from a keystore file. + * + * @author Prarthona Paul + */ + +public class keystoreUtil { + + public static KeyStore loadKeyPairFromKeyStore(String serverDir, String keyStoreFile, String storePassword, String keyAlias, String keyStoreType) throws KeyStoreException { + FileInputStream stream = findFile(serverDir + "/" + keyStoreFile); + try { + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + keyStore.load(stream, storePassword.toCharArray()); + Certificate cert = keyStore.getCertificate(keyAlias); + KeyStore trustStore = keyStore.getInstance(keyStoreType); + trustStore.load(null, null); + trustStore.setCertificateEntry("server", cert); + trustStore.store(new FileOutputStream(serverDir + "/" + "client.truststore"), storePassword.toCharArray()); + return trustStore; + } catch (Exception e) { + throw new KeyStoreException(e.getMessage()); + } + } + + public static FileInputStream findFile(String keystoreFile) { + try { + return new FileInputStream(keystoreFile); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file