Skip to content

Commit d02c538

Browse files
Add Integration tests with Custom SSL IdentityStore/TrustSore (#2282)
* Add Integration tests with Custom SSL IdentityStore/TrustSore Co-authored-by: [email protected] <[email protected]>
1 parent 226c0fb commit d02c538

File tree

6 files changed

+420
-2
lines changed

6 files changed

+420
-2
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// Copyright (c) 2021, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
package oracle.weblogic.kubernetes;
5+
6+
import java.nio.file.Paths;
7+
import java.util.Arrays;
8+
import java.util.List;
9+
10+
import oracle.weblogic.kubernetes.annotations.IntegrationTest;
11+
import oracle.weblogic.kubernetes.annotations.Namespaces;
12+
import oracle.weblogic.kubernetes.logging.LoggingFacade;
13+
import org.awaitility.core.ConditionFactory;
14+
import org.junit.jupiter.api.BeforeAll;
15+
import org.junit.jupiter.api.DisplayName;
16+
import org.junit.jupiter.api.MethodOrderer;
17+
import org.junit.jupiter.api.Order;
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.TestMethodOrder;
20+
21+
import static java.util.concurrent.TimeUnit.MINUTES;
22+
import static java.util.concurrent.TimeUnit.SECONDS;
23+
import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT;
24+
import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT;
25+
import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION;
26+
import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME;
27+
import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG;
28+
import static oracle.weblogic.kubernetes.TestConstants.OCIR_SECRET_NAME;
29+
import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT;
30+
import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR;
31+
import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR;
32+
import static oracle.weblogic.kubernetes.actions.TestActions.scaleCluster;
33+
import static oracle.weblogic.kubernetes.assertions.TestAssertions.domainExists;
34+
import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createDomainResourceWithLogHome;
35+
import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createDomainSecret;
36+
import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createJobToChangePermissionsOnPvHostPath;
37+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists;
38+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createConfigMapAndVerify;
39+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createOcirRepoSecret;
40+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createPV;
41+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createPVC;
42+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createSecretForBaseImages;
43+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.installAndVerifyOperator;
44+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runClientInsidePod;
45+
import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runJavacInsidePod;
46+
import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod;
47+
import static oracle.weblogic.kubernetes.utils.SslUtils.generateJksStores;
48+
import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger;
49+
import static org.awaitility.Awaitility.with;
50+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
51+
import static org.junit.jupiter.api.Assertions.assertNotNull;
52+
import static org.junit.jupiter.api.Assertions.assertTrue;
53+
54+
/**
55+
* This test class verifies usage of CustomIdentityCustomTrust on PV.
56+
* Create a MII domain with an attached persistent volume.
57+
* Configure custom identity and custom trust on server template
58+
* Enable SSL on server template with port 8002 (default 7002 does not work)
59+
* Put the IdentityKeyStore.jks and TrustKeyStore.jks on /shared directory
60+
* after administration server pod is started so that it can be accessible
61+
* from all managed server pods
62+
* Once all servers are started get the JNDI initial context using cluster
63+
* service URL with t3s protocol.
64+
* Repeat the same after scaling the cluster
65+
*/
66+
67+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
68+
@DisplayName("Test verifies usage of CustomIdentityCustomTrust on PV")
69+
@IntegrationTest
70+
class ItMiiCustomSslStore {
71+
72+
private static String opNamespace = null;
73+
private static String domainNamespace = null;
74+
private static ConditionFactory withStandardRetryPolicy = null;
75+
private static int replicaCount = 2;
76+
private static final String domainUid = "mii-custom-ssl";
77+
private static String pvName = domainUid + "-pv";
78+
private static String pvcName = domainUid + "-pvc";
79+
private static final String adminServerPodName = domainUid + "-admin-server";
80+
private static final String managedServerPrefix = domainUid + "-managed-server";
81+
private static LoggingFacade logger = null;
82+
private static String cpUrl;
83+
84+
/**
85+
* Install Operator.
86+
* Create domain resource definition.
87+
* @param namespaces list of namespaces created by the IntegrationTestWatcher by the
88+
* JUnit engine parameter resolution mechanism
89+
*/
90+
@BeforeAll
91+
public static void initAll(@Namespaces(2) List<String> namespaces) {
92+
logger = getLogger();
93+
// create standard, reusable retry/backoff policy
94+
withStandardRetryPolicy = with().pollDelay(2, SECONDS)
95+
.and().with().pollInterval(10, SECONDS)
96+
.atMost(5, MINUTES).await();
97+
98+
// get a new unique opNamespace
99+
logger.info("Creating unique namespace for Operator");
100+
assertNotNull(namespaces.get(0), "Namespace list is null");
101+
opNamespace = namespaces.get(0);
102+
103+
logger.info("Creating unique namespace for Domain");
104+
assertNotNull(namespaces.get(1), "Namespace list is null");
105+
domainNamespace = namespaces.get(1);
106+
107+
// Create the repo secret to pull the image
108+
// this secret is used only for non-kind cluster
109+
createOcirRepoSecret(domainNamespace);
110+
111+
// install and verify operator
112+
installAndVerifyOperator(opNamespace, domainNamespace);
113+
114+
// create secret for admin credentials
115+
logger.info("Create secret for admin credentials");
116+
String adminSecretName = "weblogic-credentials";
117+
assertDoesNotThrow(() -> createDomainSecret(adminSecretName,
118+
ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, domainNamespace),
119+
String.format("createSecret failed for %s", adminSecretName));
120+
121+
// create encryption secret
122+
logger.info("Create encryption secret");
123+
String encryptionSecretName = "encryptionsecret";
124+
assertDoesNotThrow(() -> createDomainSecret(encryptionSecretName, "weblogicenc",
125+
"weblogicenc", domainNamespace),
126+
String.format("createSecret failed for %s", encryptionSecretName));
127+
128+
String configMapName = "mii-ssl-configmap";
129+
createConfigMapAndVerify(
130+
configMapName, domainUid, domainNamespace,
131+
Arrays.asList(MODEL_DIR + "/mii.ssl.yaml"));
132+
133+
// this secret is used only for non-kind cluster
134+
createSecretForBaseImages(domainNamespace);
135+
136+
// create PV, PVC for logs/data
137+
createPV(pvName, domainUid, ItMiiCustomSslStore.class.getSimpleName());
138+
createPVC(pvName, pvcName, domainUid, domainNamespace);
139+
140+
// create job to change permissions on PV hostPath
141+
createJobToChangePermissionsOnPvHostPath(pvName, pvcName, domainNamespace);
142+
143+
// create the domain CR with a pre-defined configmap
144+
createDomainResourceWithLogHome(domainUid, domainNamespace,
145+
MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG,
146+
adminSecretName, OCIR_SECRET_NAME, encryptionSecretName,
147+
replicaCount, pvName, pvcName, "cluster-1", configMapName, null, false, false);
148+
149+
// wait for the domain to exist
150+
logger.info("Check for domain custom resource in namespace {0}", domainNamespace);
151+
withStandardRetryPolicy
152+
.conditionEvaluationListener(
153+
condition -> logger.info("Waiting for domain {0} to be created in namespace {1} "
154+
+ "(elapsed time {2}ms, remaining time {3}ms)",
155+
domainUid,
156+
domainNamespace,
157+
condition.getElapsedTimeInMS(),
158+
condition.getRemainingTimeInMS()))
159+
.until(domainExists(domainUid, DOMAIN_VERSION, domainNamespace));
160+
161+
logger.info("Check admin service and pod {0} is created in namespace {1}",
162+
adminServerPodName, domainNamespace);
163+
checkPodReadyAndServiceExists(adminServerPodName, domainUid, domainNamespace);
164+
// Generate JKS Keystore using openssl before
165+
// managed server services and pods are ready
166+
generateJksStores();
167+
assertDoesNotThrow(() -> copyFileToPod(domainNamespace,
168+
adminServerPodName, "",
169+
Paths.get(RESULTS_ROOT, "IdentityKeyStore.jks"),
170+
Paths.get("/shared/IdentityKeyStore.jks")));
171+
assertDoesNotThrow(() -> copyFileToPod(domainNamespace,
172+
adminServerPodName, "",
173+
Paths.get(RESULTS_ROOT, "TrustKeyStore.jks"),
174+
Paths.get("/shared/TrustKeyStore.jks")));
175+
176+
for (int i = 1; i <= replicaCount; i++) {
177+
logger.info("Wait for managed server services and pods are created in namespace {0}",
178+
domainNamespace);
179+
checkPodReadyAndServiceExists(managedServerPrefix + i, domainUid, domainNamespace);
180+
}
181+
}
182+
183+
/**
184+
* Verify a standalone java client can access JNDI Context inside a pod.
185+
* The client uses t3s cluster URL with custom SSL TrustStore on commandline
186+
*/
187+
@Test
188+
@Order(1)
189+
@DisplayName("Verify JNDI Context can be accessed using t3s cluster URL")
190+
public void testMiiGetCustomSSLContext() {
191+
192+
// build the standalone Client on Admin pod after rolling restart
193+
String destLocation = "/u01/SslTestClient.java";
194+
assertDoesNotThrow(() -> copyFileToPod(domainNamespace,
195+
adminServerPodName, "",
196+
Paths.get(RESOURCE_DIR, "ssl", "SslTestClient.java"),
197+
Paths.get(destLocation)));
198+
runJavacInsidePod(adminServerPodName, domainNamespace, destLocation);
199+
200+
runClientOnAdminPod();
201+
202+
boolean psuccess = assertDoesNotThrow(() ->
203+
scaleCluster(domainUid, domainNamespace, "cluster-1", 3),
204+
String.format("replica patching to 3 failed for domain %s in namespace %s", domainUid, domainNamespace));
205+
assertTrue(psuccess,
206+
String.format("Cluster replica patching failed for domain %s in namespace %s", domainUid, domainNamespace));
207+
checkPodReadyAndServiceExists(managedServerPrefix + "3", domainUid, domainNamespace);
208+
209+
runClientOnAdminPod();
210+
}
211+
212+
// Run standalone client to get initial context using t3s cluster url
213+
private void runClientOnAdminPod() {
214+
215+
StringBuffer extOpts = new StringBuffer("");
216+
extOpts.append("-Dweblogic.security.SSL.ignoreHostnameVerification=true ");
217+
extOpts.append("-Dweblogic.security.SSL.trustedCAKeyStore=/shared/TrustKeyStore.jks ");
218+
extOpts.append("-Dweblogic.security.SSL.trustedCAKeyStorePassPhrase=changeit ");
219+
withStandardRetryPolicy
220+
.conditionEvaluationListener(
221+
condition -> logger.info("Wait for client to get Initial context "
222+
+ "(elapsed time {0}ms, remaining time {1}ms)",
223+
condition.getElapsedTimeInMS(),
224+
condition.getRemainingTimeInMS()))
225+
.until(runClientInsidePod(adminServerPodName, domainNamespace,
226+
"/u01", extOpts.toString() + " SslTestClient", "t3s://" + domainUid + "-cluster-cluster-1:8002"));
227+
}
228+
}

integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,9 @@ public static Domain createDomainResourceWithLogHome(
278278
LoggingFacade logger = getLogger();
279279

280280
List<String> securityList = new ArrayList<>();
281-
securityList.add(dbSecretName);
281+
if (dbSecretName != null) {
282+
securityList.add(dbSecretName);
283+
}
282284

283285
// create the domain CR
284286
Domain domain = new Domain()
@@ -305,7 +307,7 @@ public static Domain createDomainResourceWithLogHome(
305307
.serverPod(new ServerPod()
306308
.addEnvItem(new V1EnvVar()
307309
.name("JAVA_OPTIONS")
308-
.value("-Dweblogic.StdoutDebugEnabled=false"))
310+
.value("-Dweblogic.security.SSL.ignoreHostnameVerification=true"))
309311
.addEnvItem(new V1EnvVar()
310312
.name("USER_MEM_ARGS")
311313
.value("-Djava.security.egd=file:/dev/./urandom "))
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2021, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
package oracle.weblogic.kubernetes.utils;
5+
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.nio.file.Paths;
9+
import java.nio.file.StandardCopyOption;
10+
11+
import oracle.weblogic.kubernetes.actions.impl.primitive.Command;
12+
import oracle.weblogic.kubernetes.logging.LoggingFacade;
13+
14+
import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT;
15+
import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR;
16+
import static oracle.weblogic.kubernetes.actions.impl.primitive.Command.defaultCommandParams;
17+
import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger;
18+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
19+
import static org.junit.jupiter.api.Assertions.assertTrue;
20+
21+
/**
22+
* The SSL utility class for tests.
23+
*/
24+
public class SslUtils {
25+
26+
/**
27+
* Generate SSL KeyStore in JKS format.
28+
*/
29+
public static void generateJksStores() {
30+
LoggingFacade logger = getLogger();
31+
Path jksInstallPath =
32+
Paths.get(RESOURCE_DIR, "bash-scripts", "generate-selfsign-jks.sh");
33+
String installScript = jksInstallPath.toString();
34+
String command =
35+
String.format("%s %s", installScript, RESULTS_ROOT);
36+
logger.info("JKS Store creation command {0}", command);
37+
assertTrue(() -> Command.withParams(
38+
defaultCommandParams()
39+
.command(command)
40+
.redirect(false))
41+
.execute());
42+
43+
// Copy the scripts to RESULTS_ROOT
44+
assertDoesNotThrow(() -> Files.copy(
45+
Paths.get(RESOURCE_DIR, "bash-scripts", "generate-selfsign-jks.sh"),
46+
Paths.get(RESULTS_ROOT, "generate-selfsign-jks.sh"),
47+
StandardCopyOption.REPLACE_EXISTING),
48+
"Copy generate-selfsign-jks.sh to RESULTS_ROOT failed");
49+
}
50+
51+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/bin/bash
2+
# Copyright (c) 2021, Oracle and/or its affiliates.
3+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
4+
5+
6+
# Usage:
7+
#
8+
# $0 [install-dir]
9+
10+
# Define functions
11+
function generate_jks_stores {
12+
13+
( cd $workdir;
14+
15+
host=`hostname`
16+
17+
openssl req -newkey rsa:2048 -days 1 \
18+
-passout pass:changeit -passin pass:changeit \
19+
-x509 -keyout cakey.pem -out cacert.pem \
20+
-subj "/C=US/ST=NJ/L=Basking Ridge/O=QA/CN=${host}"
21+
22+
#cakey.pem is the private key
23+
#cacert.pem is the public certificate
24+
25+
openssl pkcs12 -export -in cacert.pem -inkey cakey.pem \
26+
-passout pass:changeit -passin pass:changeit \
27+
-out identity.p12 -name "mykey"
28+
29+
keytool -importkeystore -destkeystore IdentityKeyStore.jks \
30+
-deststorepass changeit -srckeystore identity.p12 \
31+
-srcstoretype PKCS12 -srcstorepass changeit
32+
33+
keytool -import -file cacert.pem -keystore TrustKeyStore.jks \
34+
-storepass changeit -noprompt
35+
36+
)
37+
38+
}
39+
40+
# MAIN
41+
workdir=${1:-`pwd`}
42+
43+
if [ ! -d ${workdir} ]; then
44+
mkdir -p $workdir
45+
fi
46+
47+
( cd $workdir;
48+
rm -rf *.pem *.der
49+
rm -rf TrustKeyStore.jks IdentityKeyStore.jks
50+
rm -rf *.p12
51+
)
52+
generate_jks_stores ${workdir}
53+
54+
( cd $workdir;
55+
rm -rf *.pem *.der
56+
rm -rf *.p12
57+
)
58+

0 commit comments

Comments
 (0)