Skip to content

Commit

Permalink
feat(tls): add configuration options for supplying bare TLS server key (
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewazores authored Oct 23, 2024
1 parent 756a515 commit 2aa4989
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 11 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@ and how it advertises itself to a Cryostat server instance. Properties that requ
- [ ] `cryostat.agent.webserver.tls.keystore.pass.charset` [`String`]: the character set used by the HTTPS server keystore's password. Default `utf-8`.
- [ ] `cryostat.agent.webserver.tls.keystore.file` [`String`]: the filepath to the HTTPS server keystore
- [ ] `cryostat.agent.webserver.tls.keystore.type` [`String`]: the type of keystore used for the Agent's HTTPS server. Default `PKCS12`.
- [ ] `cryostat.agent.webserver.tls.key.alias` [`String`]: the alias used for the keystore entry to contain this key for the HTTPS server.
- [ ] `cryostat.agent.webserver.tls.key.path` [`String`]: the filepath to the TLS key used by the HTTPS server.
- [ ] `cryostat.agent.webserver.tls.key.charset` [`String`]: the string encoding of the TLS key file. Default `utf-8`.
- [ ] `cryostat.agent.webserver.tls.key.encoding` [`String`]: the certificate encoding of the TLS key file. Default `PKCS1`.
- [ ] `cryostat.agent.webserver.tls.key.type` [`String`]: the key type of the TLS key. Default `RSA`.
- [ ] `cryostat.agent.webserver.tls.key.pass.file` [`String`]: the path to a file containing the password to unlock the TLS key.
- [ ] `cryostat.agent.webserver.tls.key.pass-charset` [`String`]: the string encoding of the file containing the TLS key password. Default `utf-8`.
- [ ] `cryostat.agent.webserver.tls.key.pass` [`String`]: the TLS key password value. Providing the password as a mounted file is preferred.
- [ ] `cryostat.agent.webserver.tls.cert.alias` [`String`]: the alias for the certificate stored in the HTTPS server keystore. Default `serverCert`.
- [ ] `cryostat.agent.webserver.tls.cert.file` [`String`]: the filepath to the certificate to be stored by the HTTPS server keystore
- [ ] `cryostat.agent.webserver.tls.cert.type` [`String`]: the type of certificate that the HTTPS server keystore will present. Default `X.509`.
Expand Down
74 changes: 74 additions & 0 deletions src/main/java/io/cryostat/agent/ConfigModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@ public abstract class ConfigModule {
"cryostat.agent.webserver.tls.keystore.file";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEYSTORE_TYPE =
"cryostat.agent.webserver.tls.keystore.type";

public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ALIAS =
"cryostat.agent.webserver.tls.key.alias";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PATH =
"cryostat.agent.webserver.tls.key.path";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_CHARSET =
"cryostat.agent.webserver.tls.key.charset";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ENCODING =
"cryostat.agent.webserver.tls.key.encoding";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_TYPE =
"cryostat.agent.webserver.tls.key.type";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_FILE =
"cryostat.agent.webserver.tls.key.pass.file";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_CHARSET =
"cryostat.agent.webserver.tls.key.pass-charset";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS =
"cryostat.agent.webserver.tls.key.pass";

public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_CERT_ALIAS =
"cryostat.agent.webserver.tls.cert.alias";
public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_CERT_FILE =
Expand Down Expand Up @@ -608,6 +626,62 @@ public static String provideCryostatAgentWebserverTlsKeyStoreType(Config config)
return config.getValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEYSTORE_TYPE, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ALIAS)
public static String provideCryostatAgentWebserverTlsKeyAlias(Config config) {
return config.getValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ALIAS, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PATH)
public static Optional<String> provideCryostatAgentWebserverTlsKeyPath(Config config) {
return config.getOptionalValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PATH, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_CHARSET)
public static String provideCryostatAgentWebserverTlsKeyCharset(Config config) {
return config.getValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_CHARSET, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ENCODING)
public static String provideCryostatAgentWebserverTlsKeyEncoding(Config config) {
return config.getValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ENCODING, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_TYPE)
public static String provideCryostatAgentWebserverTlsKeyType(Config config) {
return config.getValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_TYPE, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_FILE)
public static Optional<String> provideCryostatAgentWebserverTlsKeyPassFile(Config config) {
return config.getOptionalValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_FILE, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_CHARSET)
public static String provideCryostatAgentWebserverTlsKeyPassCharset(Config config) {
return config.getValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_CHARSET, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS)
public static Optional<String> provideCryostatAgentWebserverTlsKeyPass(Config config) {
return config.getOptionalValue(CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS, String.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_WEBSERVER_TLS_CERT_ALIAS)
Expand Down
76 changes: 70 additions & 6 deletions src/main/java/io/cryostat/agent/MainModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -459,31 +459,46 @@ public static Optional<SSLContext> provideServerSslContext(
String passFileCharset,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEYSTORE_FILE)
Optional<String> keyStoreFilePath,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ALIAS) String keyAlias,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PATH) Optional<String> keyFilePath,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS) Optional<String> keyPass,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_FILE)
Optional<String> keyPassFile,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PASS_CHARSET)
String keyPassCharset,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_PATH) Optional<String> keyPath,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_ENCODING) String keyEncoding,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_CHARSET) String keyCharset,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEY_TYPE) String keyType,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_KEYSTORE_TYPE) String keyStoreType,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_CERT_ALIAS) String certAlias,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_CERT_FILE)
Optional<String> certFilePath,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_TLS_CERT_TYPE) String certType) {
boolean ssl =
keyStorePassFile.isPresent()
&& keyStoreFilePath.isPresent()
(keyStoreFilePath.isPresent() || keyFilePath.isPresent())
&& keyStorePassFile.isPresent()
&& certFilePath.isPresent();
if (!ssl) {
if (keyStorePassFile.isPresent()
|| keyFilePath.isPresent()
|| keyStoreFilePath.isPresent()
|| certFilePath.isPresent()) {
throw new IllegalArgumentException(
"The file paths for the keystore, keystore password, and certificate must"
+ " ALL be provided to set up HTTPS connections. Otherwise, make sure"
+ " they are all unset to use an HTTP server.");
"The file paths for the keystore or key file, keystore password, and"
+ " certificate must ALL be provided to set up HTTPS connections."
+ " Otherwise, make sure they are all unset to use an HTTP server.");
}
return Optional.empty();
}

InputStream keystore = null;
try (InputStream pass = new FileInputStream(keyStorePassFile.get());
InputStream keystore = new FileInputStream(keyStoreFilePath.get());
InputStream certFile = new FileInputStream(certFilePath.get())) {
SSLContext sslContext = SSLContext.getInstance(serverTlsVersion);
if (keyStoreFilePath.isPresent()) {
keystore = new FileInputStream(keyStoreFilePath.get());
}

// initialize keystore
String password = IOUtils.toString(pass, Charset.forName(passFileCharset));
Expand All @@ -503,6 +518,46 @@ public static Optional<SSLContext> provideServerSslContext(
}
ks.setCertificateEntry(certAlias, cert);

if (keyFilePath.isPresent()) {
Optional<CharBuffer> kp = readPass(keyPass, keyPassFile, keyPassCharset);
byte[] keyBytes = new byte[0];
try (BufferedInputStream keyIs =
new BufferedInputStream(
new FileInputStream(Path.of(keyPath.get()).toFile()))) {
KeyFactory keyFactory = KeyFactory.getInstance(keyType);
KeySpec keySpec;
// FIXME avoid allocating a String that holds the encoded key. This
// String may sit around on the heap until the JVM decides to do a GC
// and release it. It would be better to handle this using mutable
// structures (ex. byte[] or char[]) so that it can be explicitly
// cleared ASAP, or else use off-heap memory.
String s = new String(keyIs.readAllBytes(), Charset.forName(keyCharset));
String pem = s.replaceAll("-----.+KEY-----", "").replaceAll("\\s+", "");
switch (keyEncoding) {
case "PKCS1":
keyBytes = buildPkcs8KeyFromPkcs1Key(Base64.getDecoder().decode(pem));
keySpec = new PKCS8EncodedKeySpec(keyBytes, keyType);
break;
case "PKCS8":
keyBytes = Base64.getDecoder().decode(pem);
keySpec = new PKCS8EncodedKeySpec(keyBytes, keyType);
break;
default:
throw new IllegalArgumentException(
"Unimplemented key encoding: " + keyType);
}
PrivateKey key = keyFactory.generatePrivate(keySpec);
ks.setKeyEntry(
keyAlias,
key,
kp.map(CharBuffer::array).orElse(null),
new Certificate[] {cert});
} finally {
Arrays.fill(keyBytes, (byte) 0);
clearBuffer(kp);
}
}

// set up key manager factory
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Expand All @@ -520,10 +575,19 @@ public static Optional<SSLContext> provideServerSslContext(
} catch (KeyStoreException
| CertificateException
| UnrecoverableKeyException
| InvalidKeySpecException
| KeyManagementException
| IOException
| NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} finally {
if (keystore != null) {
try {
keystore.close();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
}

Expand Down
25 changes: 20 additions & 5 deletions src/main/resources/META-INF/microprofile-config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ cryostat.agent.baseuri=

cryostat.agent.api.writes-enabled=false

cryostat.agent.webserver.host=0.0.0.0
cryostat.agent.webserver.port=9977

cryostat.agent.webclient.connect.timeout-ms=1000
cryostat.agent.webclient.response.timeout-ms=1000
cryostat.agent.webclient.response.retry-count=3

cryostat.agent.webclient.tls.version=TLSv1.2
cryostat.agent.webclient.tls.trust-all=false
cryostat.agent.webclient.tls.verify-hostname=true
cryostat.agent.webclient.tls.truststore.type=JKS
cryostat.agent.webclient.tls.truststore.pass-charset=utf-8
cryostat.agent.webclient.connect.timeout-ms=1000
cryostat.agent.webclient.response.timeout-ms=1000
cryostat.agent.webclient.response.retry-count=3

cryostat.agent.webclient.tls.client-auth.cert.path=
cryostat.agent.webclient.tls.client-auth.cert.type=X.509
cryostat.agent.webclient.tls.client-auth.cert.alias=identity
Expand All @@ -27,16 +32,26 @@ cryostat.agent.webclient.tls.client-auth.keystore.pass-charset=utf-8
cryostat.agent.webclient.tls.client-auth.keystore.pass=
cryostat.agent.webclient.tls.client-auth.keystore.type=PKCS12
cryostat.agent.webclient.tls.client-auth.key-manager.type=SunX509
cryostat.agent.webserver.host=0.0.0.0
cryostat.agent.webserver.port=9977

cryostat.agent.webserver.tls.version=${cryostat.agent.webclient.tls.version}
cryostat.agent.webserver.tls.keystore.pass=
cryostat.agent.webserver.tls.keystore.pass-charset=utf-8
cryostat.agent.webserver.tls.keystore.file=
cryostat.agent.webserver.tls.keystore.type=PKCS12

cryostat.agent.webserver.tls.key.alias=serverKey
cryostat.agent.webserver.tls.key.path=
cryostat.agent.webserver.tls.key.charset=utf-8
cryostat.agent.webserver.tls.key.encoding=PKCS1
cryostat.agent.webserver.tls.key.type=RSA
cryostat.agent.webserver.tls.key.pass.file=
cryostat.agent.webserver.tls.key.pass-charset=utf-8
cryostat.agent.webserver.tls.key.pass=

cryostat.agent.webserver.tls.cert.alias=serverCert
cryostat.agent.webserver.tls.cert.file=
cryostat.agent.webserver.tls.cert.type=X.509

cryostat.agent.webserver.credentials.user=user
cryostat.agent.webserver.credentials.pass.hash-function=SHA-256
cryostat.agent.webserver.credentials.pass.length=24
Expand Down

0 comments on commit 2aa4989

Please sign in to comment.