CredentialStore Backed Expression Resolution
Overview
The present CredentialStore
integration is implemented to provide a tight integration between the locations in the model which require access to credentials,
however there is also a desire to encrypt other attribute values stored in the management model. This enhancement is specifically to focus on that latter
requirement.
As the CredentialStore
is a store of credentials and not a generic store of String values the approach here will be a little different to the approach
previously taken by the Vault solution provided by PicketBox. Instead of retrieving values from the CredentialStore
the store will be used to hold one or
more AES
SecretKey
instances. This enhancement will provide utilities to encrypt values using the keys in the credential stores, these can then be placed
into the WildFly management model on any resource that supports expressions. At runtime a new expression resolver resource will be available within WildFly to
dynamically decrypt the expressions by also making use of the SecretKey
instances loaded from one or more credential stores.
Issue Metadata
Issue
-
WFCORE-4360 - Support encrypted expression resolution using a CredentialStore.
-
WFLY-12215 - Support encrypted expression resolution using a CredentialStore.
-
ELY-1842 - Command line support for
SecretKey
manipulation.
Related Issues
-
WFCORE-4584 - Include the new wildfly-elytron-encryption module.
-
ELY-2055 - Refactor CredentialStoreCommand to make development easier.
-
ELY-1850 - CredentialStore API missing ability to check alias types.
-
ELY-2081 - Develop a simple properties based credential store for secret keys.
-
ELY-2086 - Update the WildFly Elytron tool to support encrypting clear text values.
Other Links
The following is a link to an early discussion on the wildfly-dev mailing list.
Dev Contacts
QE Contacts
Testing By
[ ] Engineering
[x] QE
Affected Projects or Components
Within the WildFly Elytron project new utilities will be added to handle the generation of SecretKey
instances and the encryption and decryption of clear
text values. These utilities will support transfer of the values using versioned Base64 encoded tokens which will be described in more detail below.
Within WildFly Core the general ExpressionResolver
support will be updated to detect the availability of an expression resolver capability exposing
a runtime API for expression resolution.
Also within WildFly Core the Elytron subsystem will be updated with new resources as required, resources will also contain operations both for key management and operations for the encryption of clear text strings for use as expressions in the management model.
Additionally all documentation lives within the WildFly project so updates will be applied there accordingly.
Other Interested Projects
Relevant Installation Types
-
Traditional standalone server (unzipped or provisioned by Galleon)
-
Managed domain
-
OpenShift s2i
-
Bootable jar
As a core management feature this enhancement will be applicable to all installation types.
Requirements
Hard Requirements
The overall intent of this enhancement is for the Elytron subsystem to provide a configurable expression resolver resource to make use of an AES
SecretKey
loaded from a CredentialStore
to decrypt expressions in the management model which have been previously encrypted using the same key. To
meet this requirement some additional functionality will also be required to hande the management of the keys.
It is a requirement within this enhancement that multiple secret keys can be used for the decryption of expressions, these keys could be loaded from the same credential store or from separate credential stores - this relates to the "chicken and egg" problem which will be described later.
The initial integration of this enhancement will not support specifying custom java.security.Provider
instances as the key type and transformation will be
using pre-defined defaults - however the expression resolver will be initialised after the Elytron subsystem has performed initialisation and registration of
the providers.
This enhancement will exclusively make use of AES
javax.crypt.SecretKey
instances, although it is possible to avoid the use of a
javax.crypto.SecretKeyFactory
as the keys can be generated using random key material directly using the javax.crypto.spec.SecretKeySpec
class we should
still use the SecretKeyFactory
in case certified providers are added for key generation. The generated SecretKey
will be stored in a CredentialStore
.
Credential Store
PropertiesCredentialStore / secret-key-credential-store
A new credential store type will be implemented, this credential store will be dedicated to the storage of SecretKeyCredential
instances which will be stored
in a properties file. Unlike other credential store types this implementation will not be adding any additional protection to the keys it contains, this
credential store is primarily intended to solve the problem of how we provide the first secret key to the application server process without relying on a hard
coded secret stored within open source code.
The new credential store type will be PropertiesCredentialStore
and a new resource will be added to the Elytron subsystem to configure instances of this
store.
The name of the resource added to the Elytron subsystem will be secret-key-credential-store
and will contain the following configuration attributes:
-
relative-to
- A predefined path that the path of this store is relative to. -
path
- The path to the file representing the credential store. -
create
- If the credential store file does not exist should it be created? -
populate
- If the credential store does not already contain an entry for thedefault-alias
should one be added? -
key-size
- The key size to use for any automatically generatedSecretKey
. -
default-alias
- The name to use for any alias dynamically added to the credential store.
The new resource will expose the org.wildfly.security.credential-store
so can be referenced from other parts of the management model. It should be noted use
across the model would be limited as this store does not support password credentials.
The configuration attributes for this credential store mean that the resource can be added to the configuration and as the server boots if the credential store
does not exist it will be dynamically created and it can also be dynamically populated with a SecretKey
- this means the resource is suitable for a very
simple out of the box getting started experience.
Key manipulation - WildFly Elytron Tool
It will be possible to generate, export, and import the SecretKey instances using the command line tool. The same actions for secret key manipulation will be
applicable to both the existing KeyStoreCredentialStore
and the new PreopertiesCredentialStore
, the only difference being the PasswordCredentialStore
does not require a password to access it.
The Elytron tool credential-store
command already has an --add
action, rather than overloading this action for different types of key and different
operations which will become complex very quickly a new set of actions will be added:
-
--generate-secret-key
-
--export-secret-key
-
--import-secret-key
The --generate-secret-key
action will take the alias to store the key as an argument in the same pattern as add
it will also take the following additional
option:
-
--size
- The key size (number of bits) - acceptable values 128, 192, or 256 - default 256.
The --export-secret-key
action will take the alias of the previously stored key as an argument, no further option will be required.
The --export-secret-key
action will return a single Base64 value which is the key in it’s encoded form.
The --import-secret-key
action will take the alias to store the key as an argument, it will also take the following additional option: -
-
--key
- The key to import encoded using Base64.
If the --key
option is not specified the user will be prompted to enter the key interactively, this will avoid the key being seen in the current running
processes and avoid the key being cached in the users command line history.
For generate-secret-key and import-secret-key if an entry already exists for that alias / algorithm combination the entry in the credential store
will be replaced.
|
For this specific enhancement where an algorithm is required for the SecretKey
we will use the hard coded value of AES
, if later enhancement add support
for further key types then it may be necessary for an optional algorithm
argument / parameter to be added to each of the above commands / operations. Should
an algorithm
argument be added at a later point it would be optional and default to AES
for backwards compatibility.
It is not feasible for all actions to have a short form so these new actions will have a long form only, within the tool we should try and restrict the use of the short form for parameters only.
Key manipulation - Management Operations
A set of operations for secret key manipulation will be added to both the existing credential-store
resource and the new secret-key-credential-store
resource. Presently the credential-store
resource contains an add-alias
operation which can be used to add a credential based on a clear text String,
the new operations will perform a similar purpose.
The following operations will be added to the credential-store
resource:
-
generate-secret-key
-
export-secret-key
-
import-secret-key
Within the management model operations are self describing so making use of dedicated operations makes it easier for tooling to present a meaningful UI to end users automatically.
The credential-store
resource already contains a remove-alias
operation however this assumes the type of the credential is PasswordCredential
a new
attribute will be added to this operation entry-type
which will default to PasswordCredential
but will also accept SecretKeyCredential
as a value
allowing credentials of type SecretKeyCredential
to be removed from the credential store. The secret-key-credential-store
resource will also have a
remove-alias
operation added but as this credential store only supports a single credential type the type parameter will be omitted.
The new secret-key-credential-store
will also have the read-aliases
and reload
operations which will behave the same as on the existing
credential-store
resource.
The credential store also offers a programmatic API, should the tooling and operations be insufficient for an end user manual population of the credential store may remain an option. |
generate-secret-key
The generate-secret-key
operation will take the following parameters:
-
alias
- The alias to use when storing theSecretKeyCredential
in the credential store. -
key-size
- The size of theAES
key to generate.
In the case of the credential-store-resource
if the key-size
attribute is omitted it will default to 256, in the case of the new
secret-key-credential-store
resource if omitted it will default to the value defined on the resource which in turn defaults to 256.
export-secret-key
The export-secret-key
operation takes a single parameter alias
which is the alias of the entry to export, the result of the operation is an encoded
representation of the SecretKey
suitable for importing elsewhere.
import-secret-key
The import-secret-key
operation takes the following parameters:
-
alias
- The alias to use to store the imported key. -
key
- The encoded representation of the secret key.
Expression Resolver Resource
Presently WildFly Core supports an expression resolver that can delegate to a Vault configuration and if that is not available fall back to use either system
properties or environment variables. This will be updated to make use of the CapabilityRegistry
and attempt to lookup a capability using a predefined
constant org.wildfly.controller.expression-resolver
which exposes an expression resolver runtime API. The expression resolver looked up using a capability
will be used after first attempting to resolve the expression as a Vault expression to resolve any expression.
A new singleton resource will be added to the elytron subsystem called expression=encryption
the purpose of this resource is to contain the configuration
both for the encryption and decryption of values. As a singleton we know only one instance of this resource can be defined within the subsystem reducing some
of the complexity multiple instances would cause. The resource will contain the definition of one or more resolvers to decrypt the inline expressions.
The expression=encryption
resource will register it’s expression resolver capability with the CapabilityRegistry
making it available for runtime
resolution of expressions.
The expression=encryption
resource will contain three attributes, the first attribute being resolvers
which will be used to define one or more resolvers to
handle the encryption and decryption of values. Each resolver will support the following configuration:
-
name
- Unique name of the resolver, this can be reference back by the expression. -
credential-store
The name of the credential store to use to load the secret key. -
secret-key
- The alias of the secret key within the credential store.
The format of the expressions resolved using the WildFly Elytron expression resolver will be ${ENC:Resolver:ENCRYPTED_DATA}
where Resolver
is a reference
to the specific resolver defined in the expression=encryption
resource and ENCRYPTED_DATA
is the data to be decrypted encoded using Base64.
The second attribute on the expression=encryption
resource will be default-resolver
which will be used to optionally specify which resolver is the default, the expression can then be simplified to ${ENC:ENCRYPTED_DATA}
.
As expressions are already widely in use with the application server there is a small possibility that users may have already defined expressions with a prefix of ENC:
expecting it to be resolved as a system property. The expression=encryption
resource will also have a third attribute prefix
attribute which will allow an alternative prefix to ENC
to be specified.
TODO - I need to double check if the format should be ENC:
or ENC::
the former may be easier to accidentially interpret as a system property with a
default value.
Encryption
To simplify the first iteration of this enhancement the only supported transformation will be AES/CBC/PKCS5Padding
, this has been selected as one of the
transformations all JVMs are required to support. Additionally as the values to be encryopted will be of varying lengths padding is required as the values
may not fit neatly into the block size for AES encryption.
Additional requirements such as the generation of an initialisation vector will be handled by the underlying Cipher
implementation, different providers
may choose to handle this differently so we will not second guess their requirements.
Command Line Encryption
It was initially considered if a new top level command should be added to the WildFly Elytron Tool to support the encryption of clear text values, however as this enhancement is only making use a single transformation we do not have a need for many additional configuration parameters beyond those required to initialise and access the underlying credential store.
The credential-store
command will be updated with an additional action encrypt
, the argument to the action is the alias of the secret key to use from the
credential store. This action in turn will optionally make use of one additional parameter:
-
--clear-text
- The clear text string to be encrypted.
If the --clear-text
parameter is omitted from the command the user will be prompted to enter the clear text to be encrypted twice to prevent the sensitive
data from being retained within the shell’s command history.
By default the output of the encrypt
action will just output the token representing the encrypted value, if the parameter --summary
is also supplied to
the command the output will also illustrate how this could be represented as an expression. However the command line tool will not be aware of the management
model configuration so any expression representation will be for guidance only.
Management Operation
The expression=encryption
resource will also contain an operation create-expression
the purpose of this operation being to take a clear text value and
using one of the defined resolvers
output an expression that can be used elsewhere in the management model. The operation will just require two parameters:
-
resolver
- The name of the defined resolver to use to encrypt the data, if theexpression=encryption
resource has thedefault-resolver
attribute defined this parameter can be omitted. -
clear-text
- The clear text value to be encrypted.
The return value of the operation will be the complete expression ready to be used elsewhere in the mode. As this resource is aware of it’s complete configuration the result will be usable as returned, caution may however be required if an expression is being prepared on one process or profile to be used on another in case there are differences in the resolver configuration.
We will not support the decryption of expressions other than the support within the management model to resolve existing expressions.
Base64 Representations
Base64 representations will be used for both the export and import of the generated keys and for the expression value to be passed to the expression resolver, generally these details should be opaque to the end user however we may include the description within the documentation allowing for others to make use of the format. We should consider that in the future alternative representations may be required either to support alternative key types or to support alternative representations so the representations will also be versioned.
The general structure of the first representation will be (illustrated as an array): -
{ 'E', 'L', 'Y', (VERSION), (TYPE), VALUE... }
For the changes being made for this enhancement the version will be 1
. For each of the supported commands and operations should a representation with a
version other than 1
an error will be reported.
A generated SecretKey
will be encoded with a type of 1: -
{ 'E', 'L', 'Y', (VERSION), `K`, KEY... }
As version 1
of this implementation only supports AES
SecretKey
instances it is not necessary for the key algorithm to be encoded within this
representation, additionally the key size does not need to be specified as it will be detectable from the length of the actual key.
An encrypted expression will be encoded with a type of 2: -
{ 'E', 'L', 'Y', (VERSION), `C`, IV Length, Initialisation Vector, Cipher Text }
At the moment only a single version of the representation will be supported, in the future the commands and operations that generated the encoded values may take a parameter to specify which version to generate however that is not required within the first implementation. Additionally later versions of the tooling and operations should always attempt to use the oldest version possible to encode a value allowing newer tools to encode values for use by older versions.
These representations are independent of the overall representation of the expressions as it is intended a portion of those expressions is both human readable and modifiable to allow alternative resolver definitions to be used without requiring the Base64 representation to be recreated.
Domain Mode
This enhancement will operate in all modes with some differences to the prior configuration using the PicketBox vault.
The expression resolver to be used to decrypt expression in the management model must be defined in the same profile as the expression. For standalone mode eveything is defined in the same configuration file. For a host controller any expressions encrypted within the host management model would be decrypted using an expression resolver defined in the Elytron subsystem also defined within the hosts mode. For servers running in domain mode, any expressions in the profile would be decrypted using an expression resolver defined in the Elytron subsystem of the same profile. There will be no sharing of the capabilities across different profiles or processes.
It should also be noted that the new key management operations on the credential store resources will not be available on the individual servers running in domain mode. In domain mode however independently generated secret keys would not make sense as the same key would be required on each host to handle the decryption of the centrally defined expressions.
Bouncy Castle
Although the individual resources are not supporting references to custom security providers it should be possibly for alternatives such as BouncyCastle to be registered in the JVM and used.
Chicken or Egg
A common problem when working with application servers is the desire to protect any credentials contained within the configuration, however an application server is often installed in such a way that enables it to be run without requiring direct user interaction. This means that when values are protected the application server installation needs access to everything at once including any secrets to decrypt or access encrypted values.
In the past password based encryption has been used to add some protection, however this still needs an initial secret which would be hard coded into the
source code of an open source project as somearbitrarycrazystringthatdoesnotmatter
which is easily accessible to anyone.
The use of password based encryption with a public secret offers some protection in that someone glancing at a configuration file would find it harder to remember the Base64 representation but as the secret is public if anyone is able to capture the masked password they could take it away and decrypt at their leisure. Overall the encryption is not offering anything more than Base64 encoding with some additional padding could offer. Password based encryption could be updated so that the secret can also be specified in the configuration but this still leaves the situation that on gaining access to the configuration all of the required information is still present to decrypt the values.
The additional resources in this RFE still suffer from similar issues but also offer alternative options for some mitigation.
The new secret-key-credential-store
offers a mechanism that the initial secret used in the configuration can be handled completely independently of
both the configuration and source. Should a malicious actor gain access to all or part of the configuration file they will still not have access to enough
information to decrypt the inlined expressions even with access to the source.
By moving the initial secret into it’s own credential store this does provide an opportunity for the filesystem level access permissions to be defined independently, ideally the only account which should be able to access the file is the account the application server uses to run. No other users which can access the system should have access to the file containing the credential store.
Of course if a user is able to access both the configuration and the credential store they will have sufficient information to decrypt the values within the encrypted expressions. A further mitigation could be to only use this secret key to protect the password to a second credential store, this second credential store could in turn contain the secret key for the remaining expression in the management model. If this second credential store was backed by a hardware security module it would make it a lot harder for a malicious actor to decrypt the values in the configuration themselves.
Nice-to-Have Requirements
These nice to have requirements are outside the scope of the current enhancement, they are added here based on discussions and thoughts during development to identify further enhancements we could add.
It would also be beneficial to support Public / Private Key Pairs, in this case a public key from the server can be used to encrypt the value leaving it decryptable using the private key, this will have a benefit that giving a user the ability to encrypt a value does not give them the ability to decrypt that value. If we are to support private key encryption the credential store does not presently support the storage of private keys unless they are either paired with their public key or are associated with an X509 certificate - individual private key storage may become desirable.
We should consider deprecating the --add
operation for the credential-store
command on the Elytron tool and instead adding an --add-password
operation to being this in alignment with the operations being used for keys. As passwords are not generated equivalent import / export operations would not
be required.
It would be nice to cross reference subsystem managed security providers for the expression resolver, however this component needs to be usable at the start of Stage.RUNTIME
so there will be a limit as to how many subsystem managed resources can be depended upon.
A lot of the arguments passed into the command line tool are repeated on each invocation, a configuration file containing these to avoid repetition may be beneficial - I suspect however that may be an independent enhancement.
The import operations could be enhanced to also support the import of a raw SecretKey encoded using Base64, after decoding the Base64 representation we could
check if the resulting size is appropriate for a SecretKey
as only fixed sizes are supported and we can also detect the missing header.
In domain mode we will not support propagating secret keys ourselves, an administrator will be required to ensure the correct keys are available on each host. As a separate RFE we could consider if it would make sense for an application server to be able to access credentials in the credential store of it’s host controller or even from the central host controller.
Non-Requirements
This enhancement will not support the retrieval of plain text strings from the credential store, this enhancement is specifically adding support for decrypting reversibly encrypted attribute expression values. Other than the support within the expression resolver to decrypt values at runtime we will not be providing any tooling to decrypt the previously encrypted tokens.
Automatic encryption of attribute values will not be supported via this enhancement, as multiple steps are required that would be better performed within enhancements to the management tooling - each of which would require special consideration based on their own user interfaces. A big risk when using multiple steps is if intermediate representations of the management model are persisted as these intermediate representations could contain the clear text values.
As with other CredentialStore use cases no automatic replication of the store or it’s entries are supported with this enhancement.
This enhancement will not add support for migrating expressions to a different credential, however if support for multiple expression resolvers is added at a later point there may be opportunities to support migration.
This enhancement is only in relation to expression resolution within the application server’s management model - this does not extend to any other descriptors or configuration files.
Expression resolution will only be supported against attributes that already support expression resolution, this enhancement will not perform a review of which attributes support expression resolution and will not be changing any attributes to support expression resolution.
Custom expression resolver implementations are outside of the scope of this RFE, adding custom implementations which potentially reference a credential store could be a future enhancement.
Resolved expressions are a deliberate decision to move values from the model to an alternative expression resolver, it is not possible to determine the
expression resolution capabilities of a slave. Where credential store backed expression resolution is in place transformers will not reject sending those
expressions to the slave. However as the expression=encryption
resource is not supported on the slave that will by itself fail transformation and be
rejected.
Test Plan
Any utilities added to the Wildfly Elytron project will also be accompanied with their own unit tests.
The major testing of this enhancement will happen within the WildFly Core project allowing us to test in the same location it is implemented.
Community Documentation
The community documentation will be enhanced to include details of the new feature.
The documentation must sufficiently describe the encryption process to a level which would allow users and third parties to create their own tooling to generate the expressions. They may wish to accomplish this using Java however they may also choose to do so in alternative languages provided the required cipher algorithms are available.
Release Note Content
[WFCORE-4360] Adds support for expressions in the management model to be encrypted using AES encryption and dynamically
decrypted at runtime using a SecretKey
from a credential store.
This enhancement makes use of a new resource expression-encryption
in the elytron
subsystem to configure the expression resolution, this new resource also
contains a management operation create-expression
which allows users to create encrypted expressions using the usual management clients.
In addition to the new resource for expression resolution a new secret-key-credential-store
is added for the purpose of providing an initial secret key to
the application server process, in the past users needed to rely on masking a password but this was achieved using a well known public password and password
based encryption. Starting from a secret key allows administrators to manage their own initial secret. Both this new credential store resource and the
existing credential-store
resource are updated to support the generation of secret keys as well as the ability to export and import previously generated
secret keys.
Finally the wildfly-elytron-tool
has also been updated to support both types of credential store and the credential-store
command updated to support
management of secret keys and the generation of encrypted tokens for use in expressions.