Secret Keys#
Secret Keys Expressions#
In SmallRye Config, a secret configuration may be expressed as ${handler::value}, where the handler is the name of
a io.smallrye.config.SecretKeysHandler to decode or decrypt the value separated by a double colon ::.
It is possible to create a custom SecretKeysHandler and provide different ways to decode or decrypt configuration
values.
A custom SecretKeysHandler requires an implementation of io.smallrye.config.SecretKeysHandler or
io.smallrye.config.SecretKeysHandlerFactory. Each implementation requires registration via the ServiceLoader
mechanism, either in META-INF/services/io.smallrye.config.SecretKeysHandler or
META-INF/services/io.smallrye.config.SecretKeysHandlerFactory files.
Custom SecretKeysHandler#
A direct SecretKeysHandler implementation is suitable when the handler needs no configuration of its own:
public class Base64SecretKeysHandler implements SecretKeysHandler {
@Override
public String decode(final String secret) {
return new String(Base64.getDecoder().decode(secret));
}
@Override
public String getName() {
return "base64";
}
}
A secret value encoded with the base64 handler can then be expressed as:
SecretKeysHandlerFactory#
When a handler requires configuration from other config sources (for example, a key or a credential read from
the config), use SecretKeysHandlerFactory instead. The factory receives a ConfigSourceContext that provides
access to all config sources initialized before the factory runs:
public class VaultSecretKeysHandlerFactory implements SecretKeysHandlerFactory {
@Override
public SecretKeysHandler getSecretKeysHandler(final ConfigSourceContext context) {
ConfigValue token = context.getValue("vault.token");
return new VaultSecretKeysHandler(token.getValue());
}
@Override
public String getName() {
return "vault";
}
}
org.acme.config.VaultSecretKeysHandlerFactory
LazySecretKeysHandler#
SecretKeysHandlerFactory initializes during the first phase of SmallRyeConfig bootstrap, alongside regular
ConfigSource and ConfigSourceProvider registrations. This means that config values produced by a
ConfigSourceFactory are not yet available when the factory’s getSecretKeysHandler is called.
For handlers that depend on sources provided by a ConfigSourceFactory, wrap an inner SecretKeysHandlerFactory
in a SecretKeysHandlerFactory.LazySecretKeysHandler. The inner factory’s getSecretKeysHandler is only invoked
the first time a value actually needs to be decoded, by which point all sources — including those from
ConfigSourceFactory — are fully initialized:
public class VaultSecretKeysHandlerFactory implements SecretKeysHandlerFactory {
@Override
public SecretKeysHandler getSecretKeysHandler(final ConfigSourceContext context) {
return new LazySecretKeysHandler(new SecretKeysHandlerFactory() {
@Override
public SecretKeysHandler getSecretKeysHandler(final ConfigSourceContext context) {
// This runs lazily, after all sources are ready.
ConfigValue token = context.getValue("vault.token");
return new VaultSecretKeysHandler(token.getValue());
}
@Override
public String getName() {
return "vault";
}
});
}
@Override
public String getName() {
return "vault";
}
}
Warning
The inner factory wrapped by LazySecretKeysHandler is what defers initialization. Do not call
context.getValue in the outer getSecretKeysHandler; only the inner factory’s getSecretKeysHandler (invoked lazily) may resolve configuration values.
Danger
It is not possible to mix Secret Keys Expressions with Property Expressions.
Crypto#
The smallrye-config-crypto artifact contains a few out-of-the-box SecretKeysHandlers ready for use. It requires
the following dependency:
<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config-crypto</artifactId>
<version>4.0.0-SNAPSHOT</version>
</dependency>
AES/GCM/NoPadding ${aes-gcm-nopadding::...}#
- The encoding length is 128.
- The secret and the encryption key (without padding) must be base 64 encoded.
Example
smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key=DDne5obnfH1RSeTg71xSZg
my.secret=${aes-gcm-nopadding::DLTb_9zxThxeT5iAQqswEl5Dn1ju4FdM9hIyVip35t5V}
The ${aes-gcm-nopadding::...} SecretKeyHandler requires
smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key configuration to state the encryption key to be
used by the aes-gcm-nopaddin handler.
A lookup to my.secret will use the SecretKeysHandler name aes-gcm-nopadding to decode the value
DJNrZ6LfpupFv6QbXyXhvzD8eVDnDa_kTliQBpuzTobDZxlg.
Info
It is possible to generate the encrypted secret with the following JBang script:
Configuration#
| Configuration Property | Type | Default |
|---|---|---|
smallrye.config.secret-handler.aes-gcm-nopadding.encryption-keyThe encryption key to use to decode secrets encoded by the AES/GCM/NoPadding algorithm. |
String | |
"smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key-decode"Decode the encryption key in Base64, if the plain text key was used to encrypt the secret. |
boolean | false |
Secret Keys Names#
When configuration properties contain passwords or other kinds of secrets, Smallrye Config can hide them to prevent accidental exposure of such values.
This is no way a replacement for securing secrets. Proper security mechanisms must still be used to secure secrets. However, there is still the fundamental problem that passwords and secrets are generally encoded simply as strings. Secret Keys provides a way to “lock” the configuration so that secrets do not appear unless explicitly enabled.
To mark specific keys as secrets, register an instance of io.smallrye.config.SecretKeysConfigSourceInterceptor by
using the interceptor factory as follows:
public class SecretKeysConfigInterceptorFactory implements ConfigSourceInterceptorFactory {
@Override
public ConfigSourceInterceptor getInterceptor(ConfigSourceInterceptorContext context) {
return new SecretKeysConfigSourceInterceptor(Set.of("secret"));
}
}
Register the factory so that it can be found at runtime by creating a
META-INF/services/io.smallrye.config.ConfigSourceInterceptorFactory file that contains the fully qualified name of
this factory class.
From this point forward, every lookup to the configuration name secret will throw a SecurityException.
Access the Secret Keys using the APIs io.smallrye.config.SecretKeys#doUnlocked(java.lang.Runnable)
and io.smallrye.config.SecretKeys#doUnlocked(java.util.function.Supplier<T>).
Secret Keys are only unlocked in the context of doUnlocked. Once the execution completes, the secrets become locked
again.