Quarkus - File Vault
Introduction
This project provides Quarkus CredentialsProvider and MicroProfile [ConfigSource](https://quarkus.io/guides/config-extending-support#custom-config-source) which extracts passwords and other sensitive data from Java KeyStore
.
Java KeyStore
is used as a file-based Vault
. Sensitive data can be imported to and securely stored in this Vault
as Java SecretKey
values. Imported certificates are also supported.
Installation
If you would like to use File Vault
as CredentialsProvider
then add the io.quarkiverse.file-vault:quarkus-file-vault
dependency in your pom.xml
:
<dependency>
<groupId>io.quarkiverse.file-vault</groupId>
<artifactId>quarkus-file-vault</artifactId>
</dependency>
If you would like to use File Vault
as ConfigSource
then add the io.quarkiverse.file-vault:quarkus-file-vault-config-source
dependency in your pom.xml
:
<dependency>
<groupId>io.quarkiverse.file-vault</groupId>
<artifactId>quarkus-file-vault-config-source</artifactId>
</dependency>
Getting Started
First a Java Keystore has to be created and the data imported to it using a Java keytool
and its -importpass
option, for example:
keytool -importpass -alias quarkus_test -keystore dbpasswords.p12 -storepass storepassword -storetype PKCS12
This command creates a keystore dbpasswords.p12
with a secret key whose alias is quarkus_test
.
Use File Vault CredentialsProvider
Once you have one or more keystore prepared you can use File Vault
as CredentialsProvider
with Quarkus extensions which integrate with CredentialsProvider
.
For example, here is how you can configure it with Agroal
:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test
quarkus.datasource.credentials-provider=quarkus.file.vault.provider.db1
quarkus.file.vault.provider.db1.path=dbpasswords.p12
quarkus.file.vault.provider.db1.secret=storepassword
quarkus.file.vault.provider.db1.alias=quarkus_test
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.sql-load-script=import.sql
In this example quarkus.datasource.credentials-provider
refers to FileVaultCredentialsProvider
as quarkus.file.vault.provider.db1
.
The name format is quarkus.file.vault.provider.<name>
, where <name>
identifies a specific keystore configuration which in this case is:
quarkus.file.vault.provider.db1.path=dbpasswords.p12
quarkus.file.vault.provider.db1.secret=storepassword
quarkus.file.vault.provider.db1.alias=quarkus_test
You can configure as many keystores as required.
Note setting a keystore alias (quarkus.file.vault.provider.db1.alias=quarkus_test
) in the properties is optional. Instead you can pass it like this:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test
quarkus.datasource.credentials-provider=quarkus.file.vault.provider.db1.quarkus_test
quarkus.file.vault.provider.db1.path=dbpasswords.p12
quarkus.file.vault.provider.db1.secret=storepassword
This is way you can refer to the same keystore but use a different alias each time.
FileVaultCredentialsProvider
will return the extracted secret key as a password
property. It will also use alias value to return a user
property. The extensions such as Agroal
will accept both properties.
However, you can choose for only a password
property be returned with quarkus.file.vault.set-alias-as-user=false
. In this case you will need to configure a username with the extension specific property, for example, when working with Agroal
you can use quarkus.datasource.username
.
Finally, if a keystore alias is not set in application.properties
(for example, quarkus.file.vault.provider.db1.alias=quarkus_test
) and is not encoded in the credentials provider name (for example, quarkus.datasource.credentials-provider=quarkus.file.vault.provider.db1.quarkus_test
) then the provider will return all the keystore entries.
Use FileVaultCredentialsProvider directly
You can access this CredentialsProvider
like this from your code:
package io.quarkiverse.filevault.it;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Map;
import io.quarkus.credentials.CredentialsProvider;
import io.quarkus.credentials.runtime.CredentialsProviderFinder;
...
CredentialsProvider cp = CredentialsProviderFinder.find("quarkus.file.vault");
// Use a `quarkus_test` alias to get a secret value from the keystore `db1`
// where the alias is set in `application.properties`:
cp.getCredentials("quarkus.file.vault.provider.db1");
// Use a `quarkus_test` alias to get a secret value from the keystore `db1` by passing it directly to the provider:
Map<String, String> props = cp.getCredentials("quarkus.file.vault.provider.db1.quarkus_test");
String user = props.get(CredentialsProvider.USER_PROPERTY_NAME);
String secret = props.get(CredentialsProvider.PASSWORD_PROPERTY_NAME);
// Use a `quarkus_cert` alias to get the encoded `X509Certificate` from the keystore `db1` by passing it directly to the provider:
Map<String, String> props = cp.getCredentials("quarkus.file.vault.provider.db1.quarkus_cert");
String user = props.get(CredentialsProvider.USER_PROPERTY_NAME);
String encodedCert = props.get("certificate");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf
.generateCertificate(new ByteArrayInputStream(encodedCert.getBytes(StandardCharsets.ISO_8859_1)));
Use File Vault ConfigSource
Once you have a keystore prepared you can use File Vault
as ConfigSource
.
For example, here is how you can configure it with Agroal
:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test
quarkus.datasource.username=quarkus_test
quarkus.datasource.password=${quarkus_test}
quarkus.file.vault-config-source.keystore-path=dbpasswords.p12
quarkus.file.vault-config-source.keystore-secret=storepassword
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.sql-load-script=import.sql
Protect keystore passwords.
You need to specify a keystore password in application.properties
for Quarkus File Vault be able to extract secrets from the keystore.
However this keystore password is a sensitive value and therefore you should consider how to minimize a risk of leaking it and how to protect it.
One important thing you should be aware of is that leaking this password does not necessarily mean the actual secrets stored in the keystore will also be leaked since an unauthorized person will also need to access the actual keystore file. Restricting an access to the keystore file to a limited number of roles and having Quarkus processes running in one of these roles will make it harder for anyone outside of this group access the keystore. The keystore password can be set as an environment variable and this password should be periodically changed to limit a window during which an attacker can try to get to the keystore.
Use ConfigSource
However if you do need to avoid setting a keystore password in application.properties
then you can use a custom MicroProfile ConfigSource
, for example.
package io.quarkiverse.filevault.it;
import java.util.Set;
import org.eclipse.microprofile.config.spi.ConfigSource;
public class KeyStoreConfigSource implements ConfigSource {
@Override
public Set<String> getPropertyNames() {
return Set.of("db1.storepassword");
}
@Override
public String getValue(String propertyName) {
return "db1.storepassword".equals(propertyName) ? "storepassword" : null;
}
@Override
public String getName() {
return "file-vault-config-source";
}
}
add org.eclipse.microprofile.config.spi.ConfigSource
service provider entry listing io.quarkiverse.filevault.it.KeyStoreConfigSource
to META-INF/services
.
Next, refer to the keystore password like this if you use File Vault
as CredentialsProvider
:
quarkus.file.vault.provider.db1.path=dbpasswords.p12
quarkus.file.vault.provider.db1.secret=${db1.storepassword}
or refer to the keystore password like this if you use File Vault
as ConfigSource
:
quarkus.file.vault-config-source.keystore-path=dbpasswords.p12
quarkus.file.vault-config-source.keystore-secret=${db1.storepassword}
Please note that in this example, hardcoding a keystore password such as db1.storepassword
in KeyStoreConfigSource
is only done to simplify the documentation.
In real world applications, custom ConfigSource
implementations will read it from a DB or other secure storage.
For example, Quarkus Vault extension provides a ConfigSource
which can fetch secrets from a HashiCorp Vault
.
Mask Keystore Password
If you need to mask a keystore password (quarkus.file.vault.provider.<name>.secret
) you will need to build and package the Vault Utils project.
mvn clean install
After that, you will be able to execute the --help
command:
$ java -jar target/quarkus-app/quarkus-run.jar --help
Usage: Encrypt Secret Util [-hV] [-e=<encryptionKey>] -p=<keystorePassword>
-e, --encryption-key=<encryptionKey> (optional) Encryption Key
-h, --help Show this help message and exit.
-p, --keystore-password=<keystorePassword> (mandatory) Keystore password
-V, --version Print version information and exit.
The only mandatory parameter is a keystore password: -p, --keystore-password
.
The encryption key will be auto-generated unless it is provided with: -e, --encryption-key
.
Here is how you can mask a keystore password:
$ java -jar target/quarkus-app/quarkus-run.jar -p storedpass -e justsomestringhere
You should see something like this at the output:
#######################################################################################################################################################
Please add the following parameters to application.properties if you use File Vault as CredentialsProvider and replace the <keystore-name>:
quarkus.file.vault.provider.<name>.encryption-key=justsomestringhere
quarkus.file.vault.provider.<name>.secret=4VLLc9bk+WMnQMR3ezJcpw
Please add the following parameters to application.properties if you use File Vault as ConfigSource:
quarkus.file.vault-config-source.encryption-key=justsomestringhere
quarkus.file.vault-config-source.keystore-secret=4VLLc9bk+WMnQMR3ezJcpw
########################################################################################################################################################
Note even though you now have the keystore password storedpass
masked as 4VLLc9bk+WMnQMR3ezJcpw
, the encryption key which was used to mask it remains in a clear text so it should be protected similarly to how an unmasked keystore password should be, for example, using a ConfigSource
or environment variables. As such the main advantage of masking the password is to introduce an extra level of indirection to make it more complex to get to the keystore password itself.
HashiCorp Vault
Quarkus Vault extension can be used as a ConfigSource
with Quarkus File Vault
to protect the keystore passwords, but it can also act as an alternative to Quarkus File Vault as a CredentialsProvider
implementation.
Configuration Reference
File Vault CredentialsProvider
Configuration property fixed at build time - All other configuration properties are overridable at runtime
Type |
Default |
|
---|---|---|
Determine if the File Vault extension is enabled |
boolean |
|
Key Store configuration which can include Map |
Map |
|
Set the alias which is used to extract a secret from the key store as a 'user' property. |
boolean |
true |