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

IDS-10052: Keystore Certificates Validation #191

Merged
merged 12 commits into from
Feb 1, 2024
14 changes: 14 additions & 0 deletions .github/workflows/camunda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ jobs:
chmod +x generate-certs.sh
./generate-certs.sh

- name: Set up Keystore storepass
run: |
mkdir ~/.cws/
chmod 700 ~/.cws/
echo ${{ secrets.KEYSTORE_PASSWORD }} > ~/.cws/creds
chmod 600 ~/.cws/creds

- name: Download Logstash
uses: carlosperate/download-file-action@v1
with:
Expand Down Expand Up @@ -169,6 +176,13 @@ jobs:
chmod +x generate-certs.sh
./generate-certs.sh

- name: Set up Keystore storepass
run: |
mkdir ~/.cws/
chmod 700 ~/.cws/
echo ${{ secrets.KEYSTORE_PASSWORD }} > ~/.cws/creds
chmod 600 ~/.cws/creds

- name: Download Logstash
uses: carlosperate/download-file-action@v1
with:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/ldap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ jobs:
chmod +x generate-certs.sh
./generate-certs.sh

- name: Set up Keystore storepass
run: |
mkdir ~/.cws/
chmod 700 ~/.cws/
echo ${{ secrets.KEYSTORE_PASSWORD }} > ~/.cws/creds
chmod 600 ~/.cws/creds

- name: Download Logstash
uses: carlosperate/download-file-action@v1
with:
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ install/logging/logstash-*.zip
/jacoco-reports
/test-screenshots

*.cnf
*.cnf
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,20 @@ See the [wiki](https://github.com/NASA-AMMOS/common-workflow-service/wiki) for m
- **Logstash 8.8.0+**: Download Logstash for your platform. Uncompress it (only if it is a .tar.gz) and then ZIP back it up with the filename 'logstash-8.8.0.zip' and place in `install/logging/`. This is a temporary workaround while we clean up our installation process. You can find the zip download [here](https://www.elastic.co/downloads/logstash).
- **Elasticsearch 8.8.0+**: CWS requires an externally-configured elasticsearch cluster to be set up. You can use an SSL Secure Elasticsearch with or without authentication, or an Insecure HTTP Elasticsearch.
- The "Elasticsearch Setup" instruction below provides a contained Dockerized way of running Elasticsearch. This serves as an alternative to installing Elasticsearch.
- Tomcat **keystore and truststore files** (needed for CWS web console to work properly):
- Tomcat **keystore, truststore, storepass files** (needed for CWS web console to work properly). To generate an open-source **.keystore** and **cws_truststore.jks** use the script `./generate-certs.sh` [here](https://github.com/NASA-AMMOS/common-workflow-service/tree/develop/cws-certs)
- You will need to add your own Tomcat keystore file to this path: `install/.keystore`
- You will need to add your own truststore file to this path: `install/tomcat_lib/cws_truststore.jks`
- See: https://tomcat.apache.org/tomcat-9.0-doc/ssl-howto.html
- **Java 11 JDK**: CWS only runs on JDK 11 now, but planning for JDK 17 soon.
- For Homebrew users:
- Install OpenJDK 11 using: `brew install openjdk@11`
- Check the exact version installed using `/usr/libexec/java_home -V`
- Add to your Shell startup (e.g. .zprofile): `export JAVA_HOME=$(/usr/libexec/java_home -v X.X.X)`
- Replace the X.X.X version above with the OpenJDK 11 output from the `/usr/libexec/java_home -V` command.
- **Store Your Keystore Password**: You will need to add your own creds file, which carries the keystore password, to this path: `~/.cws/creds`
- Set the permissions for the **~/.cws/** directory and **creds** file as Owner-Only.
- **~/.cws/** directory: `chmod 700 ~/.cws/`
- **~/.cws/creds** file: `chmod 600 ~/.cws/creds`
- **Java 11 JDK**: CWS only runs on JDK 11 now, but planning for JDK 17 soon.
- For Homebrew users:
- Install OpenJDK 11 using: `brew install openjdk@11`
- Check the exact version installed using `/usr/libexec/java_home -V`
- Add to your Shell startup (e.g. .zprofile): `export JAVA_HOME=$(/usr/libexec/java_home -v X.X.X)`
- Replace the X.X.X version above with the OpenJDK 11 output from the `/usr/libexec/java_home -V` command.


### **Development Environment Configuration**
Expand Down
105 changes: 103 additions & 2 deletions cws-installer/src/main/java/jpl/cws/task/CwsInstaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,19 @@
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import java.io.FileInputStream;
import java.util.Enumeration;
import java.io.IOException;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.Math;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
Expand All @@ -65,8 +71,14 @@
import java.util.TimeZone;
import javax.naming.AuthenticationNotSupportedException;
import javax.naming.AuthenticationException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;

import javax.tools.ToolProvider;

Expand Down Expand Up @@ -167,6 +179,8 @@ public class CwsInstaller {
private static String cws_shutdown_port;
private static String cws_tomcat_ajp_port;

private static String cws_keystore_storepass;

private static String cws_smtp_hostname;
private static String cws_smtp_port;

Expand Down Expand Up @@ -253,6 +267,7 @@ public static void main(String args[]) {
setupNotificationEmails();
setupTokenExpirationHours();
setupPorts();
getKeystorePassword();
setupTaskAssigmentEmails();
setupSMTP();
setupElasticsearch();
Expand Down Expand Up @@ -1048,6 +1063,41 @@ private static void setupLimitToRemoveAbandonedWorkersByDays() {
}


private static void getKeystorePassword() {
cws_keystore_storepass = getPreset("cws_keystore_storepass");

if (cws_keystore_storepass == null) {
Path filePath;
filePath = Paths.get("~/.cws/creds");
String storepassFilePath = filePath.toString();
storepassFilePath = storepassFilePath.replaceFirst("^~", System.getProperty("user.home"));
File storepassReadFile = new File(storepassFilePath);
boolean fileExists = storepassReadFile.exists();

if (fileExists == true) {
if (!storepassReadFile.canRead()) {
print("ERROR: creds in path '" + "~/.cws/creds" + "' is NOT readable by system user.");
print(" ");
print("WARNING: Read and fulfill the Keystore/Truststore prerequisites before continuing installation: ");
print(" https://github.com/NASA-AMMOS/common-workflow-service?tab=readme-ov-file#prerequisites");
exit(1);
}
} else {
print("ERROR: creds does NOT exist in path '" + "~/.cws/creds" + "' ");
print(" ");
print("WARNING: Make sure to place creds in the correct path and satisfy the following Keystore/Truststore prerequisites: ");
print(" https://github.com/NASA-AMMOS/common-workflow-service?tab=readme-ov-file#prerequisites");
exit(1);
}

try {
cws_keystore_storepass = Files.readString(Paths.get(storepassFilePath)).trim();
} catch (IOException e) {
e.printStackTrace();
}
}
}

private static void setupPorts() {
// PROMPT USER FOR CWS WEB PORT
cws_tomcat_connector_port = getPreset("cws_web_port");
Expand Down Expand Up @@ -1766,6 +1816,9 @@ private static void validateConfig() {
// Check that user provided Elasticsearch service is up and healthy
warningCount += validateElasticsearch();

// Check that keystore and truststore is valid, not expired
warningCount += validateKeystoreTruststore();

if (installWorker && !installConsole) {
// Validate the AMQ host/port for worker only installations.
warningCount += validateAmqConfig();
Expand Down Expand Up @@ -2371,6 +2424,54 @@ private static int validateElasticsearch() {
}
}

/**
* Validates the .keystore file in tomcat_lab. Checks for correct file name and expiration
*/
private static int validateKeystoreTruststore() {
print("checking that user provided valid .keystore file and certificate chain...");
Path filePath;
filePath = Paths.get(cws_tomcat_conf + SEP + ".keystore");
String keystoreFilePath = filePath.toString();
long ONE_DAY_MS = 24 * 60 * 60 * 1000; // 24 hours or 1 day
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keystoreFilePath), cws_keystore_storepass.toCharArray());
Enumeration aliases = ks.aliases();
while(aliases.hasMoreElements()) {
String keystoreRoot = (String) aliases.nextElement();
Date expirationDate = ((X509Certificate) ks.getCertificate(keystoreRoot)).getNotAfter();
Date currentTime = new Date();
long daysInterval = expirationDate.getTime() - currentTime.getTime();
long numDays = daysInterval / (ONE_DAY_MS);
if (numDays <= 0) {
print(" [WARNING]");
print(" The Certificate Chain in Keystore '" + keystoreFilePath + "' is expired. ");
print(" Expiration Date: " + expirationDate);
print("");
return 1;
} else if (numDays > 0 && numDays < 90) {
print(" [OK]");
print(" NOTICE: Make sure to renew the certificates within the .keystore certificate chain soon.");
print(" Certificate(s): '" + keystoreFilePath + "' ");
print(" Expiration Date: " + expirationDate);
print(" Days Until expiration: " + numDays + " days");
print("");
return 0;
} else {
print(" [OK]");
print("");
}
}
} catch (Exception e) {
print(" [WARNING]");
print(" The path '" + cws_tomcat_conf + SEP + "' may not contain .keystore file or holds an invalid keystore.");
print("");
e.printStackTrace();
return 1;
}
return 0; // OK
}

/**
* Validates that some sort of time syncing service
* such as NTP or chrony is running on this installation machine.
Expand Down Expand Up @@ -3249,4 +3350,4 @@ private static void setPreset(String key, String value) {
}
}

}
}
Loading