CredentialStore Backed Expression Resolution

In  elytron

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.

  • EAP7-677

  • 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.

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 the default-alias should one be added?

  • key-size - The key size to use for any automatically generated SecretKey.

  • 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 the SecretKeyCredential in the credential store.

  • key-size - The size of the AES 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 the expression=encryption resource has the default-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.