Chapter 13. Using a vault to obtain secrets

To obtain a secret from a vault rather than entering it directly, enter the following specially crafted string into the appropriate field:

**${vault.**_key_**}**

where the key is the name of the secret recognized by the vault.

To prevent secrets from leaking across realms, Red Hat Single Sign-On combines the realm name with the key obtained from the vault expression. This method means that the key does not directly map to an entry in the vault but creates the final entry name according to the algorithm used to combine the key with the realm name.

You can obtain the secret from the vault in the following fields:

SMTP password
In the realm SMTP settings
LDAP bind credential
In the LDAP settings of LDAP-based user federation.
OIDC identity provider secret
In the Client Secret inside identity provider OpenID Connect Config

To use a vault, register a vault provider in Red Hat Single Sign-On. You can use the providers described here or implement your provider. See the Server Developer Guide for more information.

Note

Red Hat Single Sign-On permits a maximum of one active vault provider per Red Hat Single Sign-On instance at a time. Configure the vault provider in each instance within the cluster consistently.

13.1. Kubernetes / OpenShift files plain-text vault provider

Red Hat Single Sign-On supports vault implementation for Kubernetes secrets. You can mount Kubernetes secrets as data volumes, and they appear as a directory with a flat-file structure. Red Hat Single Sign-On represents each secret as a file with the file’s name as the secret name and the file’s contents as the secret value.

You must name the files within this directory as the secret name prefixed by the realm name and an underscore. Double all underscores within the secret name or the realm name in the file name. For example, for a field within a realm named sso_realm, a reference to a secret with the name secret-name would be written as ${vault.secret-name}, and the file name looked up would be sso__realm_secret-name. Note the underscore doubled in realm name.

To use this type of secret store, you must declare the files-plaintext vault provider in the standalone.xml file and set its parameter for the directory containing the mounted volume. This example shows the files-plaintext provider with the directory where vault files are searched set to standalone/configuration/vault relative to the Red Hat Single Sign-On base directory:

<spi name="vault">
    <default-provider>files-plaintext</default-provider>
    <provider name="files-plaintext" enabled="true">
        <properties>
            <property name="dir" value="${jboss.home.dir}/standalone/configuration/vault/" />
        </properties>
    </provider>
</spi>

Here is the equivalent configuration using CLI commands:

/subsystem=keycloak-server/spi=vault/:add
/subsystem=keycloak-server/spi=vault/provider=files-plaintext/:add(enabled=true,properties={dir => "${jboss.home.dir}/standalone/configuration/vault"})

13.2. Elytron credential store vault provider

Red Hat Single Sign-On also provides support for reading secrets stored in an Elytron credential store. The elytron-cs-keystore vault provider can retrieve secrets from the credential store’s keystore based implementation, which is also the default implementation Elytron provides.

A keystore backs this credential store. JCEKS is the default format, but you can use other formats such as PKCS12. Users can create and manage the store contents using the elytron subsystem in WildFly/JBoss EAP, or the elytron-tool.sh script.

To use this provider, you must declare the elytron-cs-keystore in the keycloak-server subsystem and set the location and master secret of the keystore created by Elytron. An example of the minimal configuration for the provider follows:

<spi name="vault">
    <default-provider>elytron-cs-keystore</default-provider>
    <provider name="elytron-cs-keystore" enabled="true">
        <properties>
            <property name="location" value="${jboss.home.dir}/standalone/configuration/vault/credential-store.jceks" />
            <property name="secret" value="secretpw1!"/>
        </properties>
    </provider>
</spi>

If the underlying keystore has a format different from JCEKS, you must specify this format by using the keyStoreType:

<spi name="vault">
    <default-provider>elytron-cs-keystore</default-provider>
    <provider name="elytron-cs-keystore" enabled="true">
        <properties>
            <property name="location" value="${jboss.home.dir}/standalone/configuration/vault/credential-store.p12" />
            <property name="secret" value="secretpw1!"/>
            <property name="keyStoreType" value="PKCS12"/>
        </properties>
    </provider>
</spi>

For the secret, the elytron-cs-keystore provider supports clear-text values and masked values by using the elytron-tool.sh script:

<spi name="vault">
   ...
            <property name="secret" value="MASK-3u2HNQaMogJJ8VP7J6gRIl;12345678;321"/>
   ...
</spi>

For more information about creating and managing elytron credential stores and masking keystore secrets, see the Elytron documentation.

Note

Red Hat Single Sign-On implements the elytron-cs-keystore vault provider as a WildFly extension and is available if the Red Hat Single Sign-On server runs on WildFly/JBoss EAP only.

13.3. Key resolvers

All built-in providers support the configuration of key resolvers. A key resolver implements the algorithm or strategy for combining the realm name with the key, obtained from the ${vault.key} expression, into the final entry name used to retrieve the secret from the vault. Red Hat Single Sign-On uses the keyResolvers property to configure the resolvers that the provider uses. The value is a comma-separated list of resolver names. An example of the configuration for the files-plaintext provider follows:

<spi name="vault">
    <default-provider>files-plaintext</default-provider>
    <provider name="files-plaintext" enabled="true">
        <properties>
            <property name="dir" value="${jboss.home.dir}/standalone/configuration/vault/" />
            <property name="keyResolvers" value="REALM_UNDERSCORE_KEY, KEY_ONLY"/>
        </properties>
    </provider>
</spi>

The resolvers run in the same order you declare them in the configuration. For each resolver, Red Hat Single Sign-On uses the last entry name the resolver produces, which combines the realm with the vault key to search for the vault’s secret. If Red Hat Single Sign-On finds a secret, it returns the secret. If not, Red Hat Single Sign-On uses the next resolver. This search continues until Red Hat Single Sign-On finds a non-empty secret or runs out of resolvers. If Red Hat Single Sign-On finds no secret, Red Hat Single Sign-On returns an empty secret.

In the previous example, Red Hat Single Sign-On uses the REALM_UNDERSCORE_KEY resolver first. If Red Hat Single Sign-On finds an entry in the vault that using that resolver, Red Hat Single Sign-On returns that entry. If not, Red Hat Single Sign-On searches again using the KEY_ONLY resolver. If Red Hat Single Sign-On finds an entry by using the KEY_ONLY resolver, Red Hat Single Sign-On returns that entry. If Red Hat Single Sign-On uses all resolvers, Red Hat Single Sign-On returns an empty secret.

A list of the currently available resolvers follows:

NameDescription

KEY_ONLY

Red Hat Single Sign-On ignores the realm name and uses the key from the vault expression.

REALM_UNDERSCORE_KEY

Red Hat Single Sign-On combines the realm and key by using an underscore character. Red Hat Single Sign-On escapes occurrences of underscores in the realm or key with another underscore character. For example, if the realm is called master_realm and the key is smtp_key, the combined key is master__realm_smtp__key.

REALM_FILESEPARATOR_KEY

Red Hat Single Sign-On combines the realm and key by using the platform file separator character.

If you have not configured a resolver for the built-in providers, Red Hat Single Sign-On selects the REALM_UNDERSCORE_KEY.

13.4. Sample Configuration

The following is an example of configuring a vault and credential store. The procedure involves two parts:

  • Creating the credential store and a vault, where the credential store and vault passwords are in plain text.
  • Updating the credential store and vault to have the password use a mask provided by elytron-tool.sh.

In this example, the test target used is an LDAP instance with BIND DN credential: secret12. The target is mapped using user federation in the realm ldaptest.

13.4.1. Configuring the credential store and vault without a mask

You create the credential store and a vault where the credential store and vault passwords are in plain text.

Prerequisites

  • A running LDAP instance has BIND DN credential: secret12.
  • The alias uses the format <realm-name>_< key-value> when using the default key resolver. In this case, the instance is running in the realm ldaptest and ldaptest_ldap_secret is the alias that corresponds to the value ldap_secret in that realm.
Note

The resolver replaces underscore characters with double underscore characters in the realm and key names. For example, for the key ldaptest_ldap_secret, the final key will be ldaptest_ldap__secret.

Procedure

  1. Create the Elytron credential store.

    [standalone@localhost:9990 /] /subsystem=elytron/credential-store=test-store:add(create=true, location=/home/test/test-store.p12, credential-reference={clear-text=testpwd1!},implementation-properties={keyStoreType=PKCS12})
  2. Add an alias to the credential store.

    /subsystem=elytron/credential-store=test-store:add-alias(alias=ldaptest_ldap__secret,secret-value=secret12)

    Notice how the resolver causes the key ldaptest_ldap__secret to use double underscores.

  3. List the aliases from the credential store to inspect the contents of the keystore that is produced by Elytron.

    keytool -list -keystore /home/test/test-store.p12 -storetype PKCS12 -storepass testpwd1!
    Keystore type: PKCS12
    Keystore provider: SUN
    
    Your keystore contains 1 entries
    
    ldaptest_ldap__secret/passwordcredential/clear/, Oct 12, 2020, SecretKeyEntry,
  4. Configure the vault SPI in Red Hat Single Sign-On.

    /subsystem=keycloak-server/spi=vault:add(default-provider=elytron-cs-keystore)
    
    /subsystem=keycloak-server/spi=vault/provider=elytron-cs-keystore:add(enabled=true, properties={location=>/home/test/test-store.p12, secret=>testpwd1!, keyStoreType=>PKCS12})

    At this point, the vault and credentials store passwords are not masked.

            <spi name="vault">
                    <default-provider>elytron-cs-keystore</default-provider>
                    <provider name="elytron-cs-keystore" enabled="true">
                        <properties>
                            <property name="location" value="/home/test/test-store.p12"/>
                            <property name="secret" value="testpwd1!"/>
                            <property name="keyStoreType" value="PKCS12"/>
                        </properties>
                    </provider>
                </spi>
    
             <credential-stores>
                    <credential-store name="test-store" location="/home/test/test-store.p12" create="true">
                        <implementation-properties>
                            <property name="keyStoreType" value="PKCS12"/>
                        </implementation-properties>
                        <credential-reference clear-text="testpwd1!"/>
                    </credential-store>
             </credential-stores>
  5. In the LDAP provider, replace binDN credential with ${vault.ldap_secret}.
  6. Test your LDAP connection.

    LDAP Vault

    LDAP Vault

13.4.2. Masking the password in the credential store and vault

You can now update the credential store and vault to have passwords that use a mask provided by elytron-tool.sh.

  1. Create a masked password using values for the salt and the iteration parameters:

    $ EAP_HOME/bin/elytron-tool.sh mask --salt SALT --iteration ITERATION_COUNT --secret PASSWORD

    For example:

    elytron-tool.sh mask --salt 12345678 --iteration 123 --secret testpwd1!
    MASK-3BUbFEyWu0lRAu8.fCqyUk;12345678;123
  2. Update the Elytron credential store configuration to use the masked password.

    /subsystem=elytron/credential-store=cs-store:write-attribute(name=credential-reference.clear-text,value="MASK-3BUbFEyWu0lRAu8.fCqyUk;12345678;123")
  3. Update the Red Hat Single Sign-On vault configuration to use the masked password.

    /subsystem=keycloak-server/spi=vault/provider=elytron-cs-keystore:remove()
    /subsystem=keycloak-server/spi=vault/provider=elytron-cs-keystore:add(enabled=true, properties={location=>/home/test/test-store.p12, secret=>”MASK-3BUbFEyWu0lRAu8.fCqyUk;12345678;123”, keyStoreType=>PKCS12})

    The vault and credential store are now masked:

            <spi name="vault">
                    <default-provider>elytron-cs-keystore</default-provider>
                    <provider name="elytron-cs-keystore" enabled="true">
                        <properties>
                            <property name="location" value="/home/test/test-store.p12"/>
                            <property name="secret" value="MASK-3BUbFEyWu0lRAu8.fCqyUk;12345678;123"/>
                            <property name="keyStoreType" value="PKCS12"/>
                        </properties>
                    </provider>
                </spi>
             ....
             .....
             <credential-stores>
                    <credential-store name="test-store" location="/home/test/test-store.p12" create="true">
                        <implementation-properties>
                            <property name="keyStoreType" value="PKCS12"/>
                        </implementation-properties>
                        <credential-reference clear-text="MASK-3BUbFEyWu0lRAu8.fCqyUk;12345678;123"/>
                    </credential-store>
             </credential-stores>
  4. You can now test the connection to the LDAP using ${vault.ldap_secret}.

Additional resources

For more information about the Elytron tool, see Using Credential Stores with Elytron Client.