From f57facdbcf53ae44a52c0b67e442caebf17c64eb Mon Sep 17 00:00:00 2001 From: exceptionfactory Date: Thu, 31 Mar 2022 19:06:23 -0500 Subject: [PATCH] NIFI-9858 Refactored nifi-system-test-suite using generated certificates - Added NiFiSystemKeyStoreProvider for configuration of Key Pair and self-signed Certificate - Updated standalone and clustered instance configuration properties - Removed expiring keystore.jks and truststore.jks This closes #5922 Signed-off-by: Paul Grey --- .../system/NiFiSystemKeyStoreProvider.java | 143 ++++++++++++++++++ .../SpawnedStandaloneNiFiInstanceFactory.java | 13 +- .../conf/clustered/node1/nifi.properties | 14 +- .../conf/clustered/node2/nifi.properties | 14 +- .../resources/conf/default/nifi.properties | 14 +- .../src/test/resources/keystore.jks | Bin 3095 -> 0 bytes .../src/test/resources/truststore.jks | Bin 911 -> 0 bytes 7 files changed, 166 insertions(+), 32 deletions(-) create mode 100644 nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemKeyStoreProvider.java delete mode 100644 nifi-system-tests/nifi-system-test-suite/src/test/resources/keystore.jks delete mode 100644 nifi-system-tests/nifi-system-test-suite/src/test/resources/truststore.jks diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemKeyStoreProvider.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemKeyStoreProvider.java new file mode 100644 index 0000000000..2a80103c80 --- /dev/null +++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemKeyStoreProvider.java @@ -0,0 +1,143 @@ +/* + * 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.tests.system; + +import org.apache.nifi.security.util.CertificateUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * System Key Store Provider generates a Key Pair and Certificate for KeyStore and TrustStore files + */ +public class NiFiSystemKeyStoreProvider { + private static final String HOSTNAME = "localhost"; + + private static final String[] DNS_NAMES = new String[]{HOSTNAME}; + + private static final String DISTINGUISHED_NAME = String.format("CN=%s", HOSTNAME); + + private static final String PASSWORD = NiFiSystemKeyStoreProvider.class.getSimpleName(); + + private static final int VALID_DURATION_DAYS = 1; + + private static final String KEY_ALGORITHM = "RSA"; + + private static final int KEY_SIZE = 4096; + + private static final String SIGNING_ALGORITHM = "SHA256withRSA"; + + private static final String KEYSTORE_FILE = "keystore.p12"; + + private static final String TRUSTSTORE_FILE = "truststore.p12"; + + private static final String KEYSTORE_TYPE = "PKCS12"; + + private static Path persistentKeyStorePath; + + private static Path persistentTrustStorePath; + + /** + * Configure KeyStores in provided directory and reuse existing files after initial generation + * + * @param keyStoreDirectory Directory where KeyStore and TrustStore should be stored + */ + public synchronized static void configureKeyStores(final File keyStoreDirectory) { + if (persistentKeyStorePath == null) { + createKeyStores(); + } + + if (persistentKeyStorePath == null) { + throw new IllegalStateException("KeyStore not provisioned"); + } + if (persistentTrustStorePath == null) { + throw new IllegalStateException("TrustStore not provisioned"); + } + + try { + Files.copy(persistentKeyStorePath, Paths.get(keyStoreDirectory.getAbsolutePath(), KEYSTORE_FILE)); + Files.copy(persistentTrustStorePath, Paths.get(keyStoreDirectory.getAbsolutePath(), TRUSTSTORE_FILE)); + } catch (final IOException e) { + throw new UncheckedIOException("KeyStore configuration failed", e); + } + } + + private static void createKeyStores() { + try { + final KeyPair keyPair = getKeyPair(); + final X509Certificate certificate = CertificateUtils.generateSelfSignedX509Certificate( + keyPair, + DISTINGUISHED_NAME, + SIGNING_ALGORITHM, + VALID_DURATION_DAYS, + DNS_NAMES + ); + + persistentTrustStorePath = writeTrustStore(certificate); + persistentTrustStorePath.toFile().deleteOnExit(); + + persistentKeyStorePath = writeKeyStore(certificate, keyPair.getPrivate()); + persistentKeyStorePath.toFile().deleteOnExit(); + } catch (final Exception e) { + throw new RuntimeException("KeyStore Creation Failed", e); + } + } + + private static Path writeKeyStore(final X509Certificate certificate, final PrivateKey privateKey) throws Exception { + final KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE); + keyStore.load(null); + + final X509Certificate[] certificates = new X509Certificate[]{certificate}; + keyStore.setKeyEntry(HOSTNAME, privateKey, PASSWORD.toCharArray(), certificates); + + final Path keyStorePath = Files.createTempFile(KEYSTORE_FILE, KEYSTORE_TYPE); + try (final OutputStream outputStream = new FileOutputStream(keyStorePath.toFile())) { + keyStore.store(outputStream, PASSWORD.toCharArray()); + } + return keyStorePath; + } + + private static Path writeTrustStore(final X509Certificate certificate) throws Exception { + final KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE); + trustStore.load(null); + trustStore.setCertificateEntry(HOSTNAME, certificate); + + final Path trustStorePath = Files.createTempFile(TRUSTSTORE_FILE, KEYSTORE_TYPE); + try (final OutputStream outputStream = new FileOutputStream(trustStorePath.toFile())) { + trustStore.store(outputStream, PASSWORD.toCharArray()); + } + return trustStorePath; + } + + private static KeyPair getKeyPair() throws NoSuchAlgorithmException { + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); + keyPairGenerator.initialize(KEY_SIZE); + return keyPairGenerator.generateKeyPair(); + } +} diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/SpawnedStandaloneNiFiInstanceFactory.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/SpawnedStandaloneNiFiInstanceFactory.java index f14e4d325a..6bb1ee4a6a 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/SpawnedStandaloneNiFiInstanceFactory.java +++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/SpawnedStandaloneNiFiInstanceFactory.java @@ -33,7 +33,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Collections; import java.util.Map; import java.util.Properties; @@ -127,14 +126,7 @@ public class SpawnedStandaloneNiFiInstanceFactory implements NiFiInstanceFactory if (!destinationCertsDir.exists()) { assertTrue(destinationCertsDir.mkdirs()); } - - // Copy keystore - final File destinationKeystore = new File(destinationCertsDir, "keystore.jks"); - Files.copy(Paths.get("src/test/resources/keystore.jks"), destinationKeystore.toPath()); - - // Copy truststore - final File destinationTruststore = new File(destinationCertsDir, "truststore.jks"); - Files.copy(Paths.get("src/test/resources/truststore.jks"), destinationTruststore.toPath()); + NiFiSystemKeyStoreProvider.configureKeyStores(destinationCertsDir); final File flowXmlGz = instanceConfiguration.getFlowXmlGz(); if (flowXmlGz != null) { @@ -203,9 +195,8 @@ public class SpawnedStandaloneNiFiInstanceFactory implements NiFiInstanceFactory try { Thread.sleep(1000L); } catch (InterruptedException ex) { + logger.debug("NiFi Startup sleep interrupted", ex); } - - continue; } } } diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node1/nifi.properties b/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node1/nifi.properties index 7066fba407..071f863a0f 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node1/nifi.properties +++ b/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node1/nifi.properties @@ -146,13 +146,13 @@ nifi.sensitive.props.key.protected= nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL nifi.sensitive.props.additional.keys= -nifi.security.keystore=certs/keystore.jks -nifi.security.keystoreType=JKS -nifi.security.keystorePasswd=passwordpassword -nifi.security.keyPasswd= -nifi.security.truststore=certs/truststore.jks -nifi.security.truststoreType=JKS -nifi.security.truststorePasswd=passwordpassword +nifi.security.keystore=certs/keystore.p12 +nifi.security.keystoreType=PKCS12 +nifi.security.keystorePasswd=NiFiSystemKeyStoreProvider +nifi.security.keyPasswd=NiFiSystemKeyStoreProvider +nifi.security.truststore=certs/truststore.p12 +nifi.security.truststoreType=PKCS12 +nifi.security.truststorePasswd=NiFiSystemKeyStoreProvider nifi.security.user.authorizer=managed-authorizer nifi.security.user.login.identity.provider= nifi.security.ocsp.responder.url= diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node2/nifi.properties b/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node2/nifi.properties index 2d6f7510ea..4420403e9c 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node2/nifi.properties +++ b/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/clustered/node2/nifi.properties @@ -146,13 +146,13 @@ nifi.sensitive.props.key.protected= nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL nifi.sensitive.props.additional.keys= -nifi.security.keystore=certs/keystore.jks -nifi.security.keystoreType=JKS -nifi.security.keystorePasswd=passwordpassword -nifi.security.keyPasswd= -nifi.security.truststore=certs/truststore.jks -nifi.security.truststoreType=JKS -nifi.security.truststorePasswd=passwordpassword +nifi.security.keystore=certs/keystore.p12 +nifi.security.keystoreType=PKCS12 +nifi.security.keystorePasswd=NiFiSystemKeyStoreProvider +nifi.security.keyPasswd=NiFiSystemKeyStoreProvider +nifi.security.truststore=certs/truststore.p12 +nifi.security.truststoreType=PKCS12 +nifi.security.truststorePasswd=NiFiSystemKeyStoreProvider nifi.security.user.authorizer=managed-authorizer nifi.security.user.login.identity.provider= nifi.security.ocsp.responder.url= diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/default/nifi.properties b/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/default/nifi.properties index e9c00352eb..27ad177583 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/default/nifi.properties +++ b/nifi-system-tests/nifi-system-test-suite/src/test/resources/conf/default/nifi.properties @@ -147,13 +147,13 @@ nifi.sensitive.props.key.protected= nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL nifi.sensitive.props.additional.keys= -nifi.security.keystore=certs/keystore.jks -nifi.security.keystoreType=JKS -nifi.security.keystorePasswd=passwordpassword -nifi.security.keyPasswd= -nifi.security.truststore=certs/truststore.jks -nifi.security.truststoreType=JKS -nifi.security.truststorePasswd=passwordpassword +nifi.security.keystore=certs/keystore.p12 +nifi.security.keystoreType=PKCS12 +nifi.security.keystorePasswd=NiFiSystemKeyStoreProvider +nifi.security.keyPasswd=NiFiSystemKeyStoreProvider +nifi.security.truststore=certs/truststore.p12 +nifi.security.truststoreType=PKCS12 +nifi.security.truststorePasswd=NiFiSystemKeyStoreProvider nifi.security.user.authorizer=managed-authorizer nifi.security.user.login.identity.provider= nifi.security.ocsp.responder.url= diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/resources/keystore.jks b/nifi-system-tests/nifi-system-test-suite/src/test/resources/keystore.jks deleted file mode 100644 index 34a197f3654d7c32bb1359b615f05fa78ee0c82d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3095 zcmd6oc{tSV8pr21n{{T&HnNQD4dFKHvSo{8glfXbzGRE+gF%)`-teNN zLXslcqU<3VdXc5XnR?Ilp5E&^=Q`)l^T&N%_xE}3>v{gT@6Y$%W^OYf5D1hNz|Ua! z^>p`C@OBGf9eBYOb88?F1Pr9XcR)0fQyvMx0XgVy06;+CH2A0G@S89~Vs?aTY$AML zDhzj_@s{3F^=NTjJKm79Ir{K{q0Vl{%x5OO-m=fF;`!1B?>Pi6=vh>E#c!0pW?q?Z z9O=WExTm(`3gKA`Ejd9|O*3i~w!wyN#BtrO=Fju161@d@LQR|B!+Xf4u)eoVL1W$D? ziO1cc*V~(RJ|>IvEpx|?rlDHx32|IzW9tbIlCa)ycwz0OHplbIiiKZRl~d6Tyn&8J z_dBpzOHLx*5Jc>ll%eXN@U27I0`&>f@?angOJu@vL-^;$6Mw>D2=7 z*%yDndcs}b-$QI6Q*@2I$4)Y%Lu;LvX3XDU7jRdmS7XtQ5^J8 zGg>xJT_4XpkMLsGU+YxkCyR*H*IhrR*o3Lh*DmC1CWU!(X7shS79EF}QFB%c)D)Tp z2wozDt$}*1po(PvOFyb4hAPL4p{C`F^UNHwVh}$_wL3liEa`^7ckY~iP&i|suNgL- zSTrrSV4|;gahFN#Zo2Z$=Nhk88-og=&mC^QG4kL0vTJ(d9w_zWl|I9o#`%v;3RkUJ zks0mrTAFy5*LKFcR&STGl|DQVzdyEqu^}U*A+_#pL=fDpe_Slk?qCCdjsJvR@>#DZ z`%Y|ZWqYhFSA0|_q26uPWV|wesQRw3z;auwv-RfofiZrkk3(vm;edf-z8=q$qumin ziQ-*KZ!m@e4ETk2aS1m>`eLW1fH#E}cZ7QX7-)CRSIbE>Td~WnHIDMim3waGPA|R6 zyLwfkIjB^|3?uL*jicwycdNuB5s=1q%#gA(Rlof=yEdJ}mfHT|#=85?)b1z0)0K4E zcx*Y&P!+Z8dLh#mV?BwN$a5c7TQm+-jchYiv?M27R};_YIURS$t@WgpXsp&tR9~VZ z`-E63Lp?zjaEP>yZX&%H-#;*Qcd`JN*@|xA_ez~f8-!;`lIm}IkndI`>BU{S9J)t3 z>Kb2h!mgbFe{s8NU^lL-y5!bg;4g-GvRJ{gFl zLQ+>>=ss<7uHOB`sO(<$K~K*Go{=kky?dl+)?Pqz)-|W%Nh7|k)w;t;r%KroW~jLa zA*;OBl}DvglTF>cr}xfbTN~*g&?<0EyV0AMmOs*ai1qW$NXOcBiQwVV(fu4XkohLEsuSASe_|2TG+dRA9X*6=CsK8MR3+urWl@emIvMTmw z_|3?lCW#;`s(>002rK}k!TdlP)Rqo|0#GRDPphnA)j&WLl0z=)aMU^q0JsnkP?CTG zxsWhxI2Skk7|EDK-~o62kT7lzA3v8fJ|2Dn6cC3+5j4O9pazius;hu@SQL@SlIs7I z|Jx%pK;qZWSr!eW0T>8K12~~H0Dz33la_4e5}o>~GF2n~DTdw*sQyI5;K;=m=@!#U zJ&`95Rep0lzeg!}rLHg_SMx#qTcoAWkX=-AY-)t8S-`QZ4_H{$CvNmDAqO_K9>g1f z(`W5MRsjY8cHnRr6t)3! z{Ls8`C%y13>8?mAm0MP=xlc0r(}2hKI5?@2`zyO%bb+ z03_GDP*bX4tWaKo)27JLE-#}-UZG9Q{61dDXo(%4laihER^b${MNfZsPN%PKIu&YR zZuCgdMxHX`$KHoOZawR2)f4KL*i!-iF7-g@k}k7psk2FO!uH1V?ctt^YLl|0gU@u# zx%hk@4!22jX+eV#E$Zv`$<9;EDTOl8xLa^3nN8C30s9lKs_8uBrU+EbB6yp2{82>s zKxLy|U@S5702L>ia6AI0=BA%&cV>-@c~FM!(s<8XA5S;`eR z{95Qk{vypisPB$te#pi18L4PDD&_D3;hD|Rk#}w7AAKs0y*_=Z0-2{>DW$Qjy)z5> zi#{_JeIyorgkR~?`v0bn;rQ@G?U3+*Os?(5D`)a!c>}c>!m*tEUQKUPi}8#P0ehk8 zB`0riV684Fx7`3P$Mc3^RM$=(T8$cYGrcQnyDvy1QLoVdOnLZx_Z=O8d%rOIl#}57 zIUVDt>b1U{U(4d%sf!G?AyLZL+Zo9M;m%K>b}N<^(1 zrLHKX+@am4)M-vGow%u;x~Y||o!GHYWm>(lh46apS>@FQm5w<+S0>k_Vn0{+Rd4i8 zD7Izo>UAOa+|@9yH-18vxoapd7&jZ-Ao(zbfX!+0wVn#c*9ukHXTI&292xW#i_yG9 zB8_etn6y}j2iWN-{YhUaNc}rIz&6PJCp!ob#^UK0GXE`j|CP+2WdCRU{_f5{xkCTG zE~7Ya{PNVY3qQT1fsu$A;E^1xwcU$)ik-P9!lahdF>v~2T3W*jrRTiS*H!+-J%vT% z7U=Ubw_UzpOzs$^R$UL)6wPrU3gqmUdES3Y!AE(nX*B}NPL6iA(|K)bt!m6Y>9M+$ zpy^Qao22)KOrh&%dE43rBLCbl`}XMsL9I#M;7Fr*9}&bKU&=?cl_t^iCpjv$qiXlD zsocMH(0OCjGOg2ggetrhg`F=>xne&kDq9G)*N_v7TBBz?>XOyWi#yYvehF08w=%Kc zoncTa#_%s_M7wir6$BSv5Pp>*+H>mzv6r|VN&rQlOFB|TP><8*GMIOS+#*-fgAFgq TO}|{qSc~N0c}Wr|-5dW8Q|SfO diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/resources/truststore.jks b/nifi-system-tests/nifi-system-test-suite/src/test/resources/truststore.jks deleted file mode 100644 index 4bc1b2050ba3d5dc80f663518fd520c4148e95b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 911 zcmezO_TO6u1_mY|W(3omd6{XMy2+_UB|woZ=67>8Gq6VJnHpF!FfjWXG%;AUeEW#Q&w@$+=^G!!xr0CAal zICJuo6LT{1i%SgT#CZ)Z4NMFyjSLMeObw#Md5w&KTni|dI76Bkm5?oGWMyD(V&rEq zXkz4IYGPz$SQ5WCVCxO(Yie`Cf1FE-IH>Jp{>;#C_Pk5h*+BeLbfh^RCrE60Vdr+( zVrw4PujSooweOag-;gc&wU*Ohtfq>14e)>KO^IR78YRi{WIVJ@r7Af4VZyq z2C^U?ABz}^$l6(di}$<~EHR3BYi%^+$K%DXk2)I2gQS&NBn-qFuqy!TS7i}15Mkrc zW@BV!WoKrBvlx-12$(*AQN+l=x8u8--Yd7WF`tq}7o6C+q?iAikn;7d;R?Km#h-1I zd0V@30YA%v>ZZv%KbKr6aQXE`^qu6wMGu1b3e?sn|1Fzv;$H2V{t9c^*>Of?eMK{j6`4D;`kTa zBVqH%zM|EoJ4c4~qZQHh0gWYt?3ag|a_kt&%3cp<|{ijX*)#8aY z(br|w7aAPf6w$lrNc*#lZ4=A`7oVDZ_-m=LV~BgS;54UY@=FTm@8V12{4>8|VUhH? zX|fkqJT