1. About
WildFly Elytron brings to WildFly a single unified security framework across the whole of the application server. As a single framework it will be usable both for configuring management access to the server and for applications deployed to the server. It will also be usable across all process types so there will be no need to learn a different security framework for host controllers in a domain compared to configuring a standalone server.
The project covers these main areas:
-
Authentication
-
Authorization
-
SSL / TLS
-
Secure Credential Storage
1.1. Authentication
One of the fundamental objectives of the project was to ensure that we can use stronger authentication mechanisms for both HTTP and SASL based authentication, in both cases the new framework also makes it possible to bring in new implementations opening up various integration opportunities with external solutions.
1.2. Authorization
The architecture of the project makes a very clear distinction between the raw representation of the identity as returned by a SecurityRealm from the repository of identities and the final representation as a SecurityIdentity after roles have been decoded and mapped and permissions have been mapped.
Custom implementations of the components to perform role decoding and mapping, and permission mapping can be provided allowing for further flexibility beyond the default set of components provided by the project.
1.3. SSL / TLS
The project becomes the centralised point within the application server for configuring SSL related resources meaning they can be configured in a central location and referenced by resources across the application server. The centralised configuration also covers advanced options such as configuration of enabled cipher suites and protocols without this information needing to be distributed across the management model.
The SSL / TLS implementation also includes an optimisation where it can be closely tied to authentication allowing for permissions checks to be performed on establishment of a connection before the first request is received and the eager construction of a SecurityIdentity eliminating the need for it to be constructed on a per-request basis.
2. General Elytron Architecture
The overall architecture for WildFly Elytron is building up a full security policy from assembling smaller components together, by default we include various implementations of the components - in addition to this, custom implementations of many components can be provided in order to provide more specialised implementations.
Within WildFly the different Elytron components are handled as capabilities meaning that different implementations can be mixed and matched, however the different implementations are modelled using distinct resources. This section contains a number of diagrams to show the general relationships between different components to provide a high level view, however the different resource definitions may use different dependencies depending on their purpose.
2.1. Security Domains
Within WildFly Elytron a SecurityDomain can be considered as a security policy backed by one or more SecurityRealm instances. Resources that make authorization decisions will be associated with a SecurityDomain, from the SecurityDomain a SecurityIdentity can be obtained which is a representation of the current identity, from this the identity’s roles and permissions can be checked to make the authorization decision for the resource.
The SecurityDomain is the general wrapper around the policy describing a resulting SecurityIdentity and makes use of the following components to define this policy.
-
NameRewriter
NameRewriters are used in multiple places within the Elytron configuration, as their name implies, their purpose is to take a name and map it to another representation of the name or perform some normalisation or clean up of the name.
-
RealmMapper
As a SecurityDomain is able to reference multiple SecurityRealms the RealmMapper is responsible for identifying which SecurityRealm to use based on the supplied name for authentication.
-
SecurityRealm
One or more named SecurityRealms are associated with a SecurityDomain. A SecurityRealm provides access to the underlying repository of identities. It is used for obtaining credentials to allow authentication mechanisms to perform verification, for validation of Evidence, and for obtaining the raw AuthorizationIdentity performing the authentication.
Some SecurityRealm implementations are also modifiable and therefore expose an API that allows for updates to be made to the repository containing the identities.
-
RoleDecoder
Along with the SecurityRealm association is also a reference to a RoleDecoder, the RoleDecoder takes the raw AuthorizationIdentity returned from the SecurityRealm and converts its attributes into roles.
-
RoleMapper
After the roles have been decoded for an identity, further mapping can be applied. This could be as simple as normalising the format of role names, or perhaps adding or removing specific role names. If a RoleMapper is referenced by a SecurityRealm association, that RoleMapper is applied first before applying the RoleMapper associated with the SecurityDomain.
-
PrincipalDecoder
A PrincipalDecoder converts from a Principal to a String representation of a name. One example for this is the X500PrincipalDecoder, which is able to extract an attribute from a distinguished name.
-
PermissionMapper
In addition to having roles, a SecurityIdentity can also have a set of permissions. The PermissionMapper assigns those permissions to the identity.
Different secured resources can be associated with different SecurityDomains for their authorization decisions. Within WildFly Elytron we have the ability to configure inflow between different SecurityDomains. The inflow process means that a SecurityIdentity inflowed into a second SecurityDomain has the mappings of the new SecurityDomain applied to it. So although a common identity may be calling different resources, each of those resources could have a very different view of the roles and permissions associated with the identity.
2.2. SASL Authentication
The SaslAuthenticationFactory is an authentication policy for authentication using SASL authentication mechanisms. In addition to being a policy it is also a factory for configured authentication mechanisms backed by a SecurityDomain.
The SaslAuthenticationFactory references the following: -
-
SecurityDomain
This is the security domain that any mechanism authentication will be performed against.
-
SaslServerFactory
This is the general factory for server side SASL authentication mechanisms.
-
MechanismConfigurationSelector
Additional configuration can be supplied for the authentication mechanisms. The configuration will be described in more detail later but the purpose of the MechanismConfigurationSelector is to obtain configuration specific to the mechanism selected. This can include information about realm names a mechanism should present to a remote client plus additional NameRewriters and RealmMappers to use during the authentication process.
The reason some components referenced by the SecurityDomain are duplicated is so that mechanism-specific mappings can be applied.
2.3. HTTP Authentication
The HttpAuthenticationFactory is an authentication policy for authentication using HTTP authentication mechanisms, including the BASIC, DIGEST, EXTERNAL, FORM, SPNEGO, and CLIENT_CERT mechanisms. In addition to being a policy, it is also a factory for configured authentication mechanisms backed by a SecurityDomain.
The HttpAuthenticationFactory references the following:
-
SecurityDomain
This is the security domain that any mechanism authentication will be performed against.
-
HttpServerAuthenticationMechanismFactory
This is the general factory for server side HTTP authentication mechanisms.
-
MechanismConfigurationSelector
Additional configuration can be supplied for the authentication
mechanisms. The configuration will be described in more detail later but
the purpose of the MechanismConfigurationSelector
is to obtain
configuration specific to the mechanism selected. This can include
information about realm names a mechanism should present to a remote
client plus additional NameRewriters and RealmMappers to use during the
authentication process.
The reason some components referenced by the SecurityDomain are duplicated is so that mechanism-specific mappings can be applied.
2.4. SSL / TLS
The SSLContext defined within Elytron is a javax.net.ssl.SSLContext
meaning it can be used by anything that uses an SSLContext directly.
In addition to the usual configuration for an SSLContext it is possible to configure additional items such as cipher suites and protocols and the SSLContext returned will wrap any engines created to set these values.
The SSLContext within Elytron can also reference the following:
-
KeyManagers
An array of KeyManager instances to be used by the SSLContext, this in turn can reference a KeyStore to load the keys.
-
TrustManagers
An array of TrustManager instances to be used by the SSLContext, this in turn can also reference a KeyStore to load the certificates.
-
SecurityDomain
If an SSLContext is (optionally) configured to reference a SecurityDomain then verification of a client’s certificate can be performed as an authentication ensuring the appropriate permissions to Logon are assigned before even allowing the connection to be fully opened. Additionally, the SecurityIdentity can be established at the time the connection is opened and used for any invocations over the connection.
2.5. Passwords
One of the core features of WildFly Elytron is the ability to work with many different formats for representing passwords, WildFly Elytron also contains APIs that can be used to convert from clear text passwords to these representations that can be stored in the underlying identity stores.
This section will document how these APIs can be used to work with the different password types.
2.5.1. PasswordFactory
Working with passwords will require interaction with the org.wildfly.security.password.PasswordFactory
API, this obtains access to implementations from java.security.Provider
instances, there are two different ways these can be identified: -
-
By querying the globally installed Providers
-
By passing in the
Provider
to thegetInstance
method of thePasswordFactory
API.
In both cases the WildFly Elytron implementations are provided by the org.wildfly.security.WildFlyElytronProvider
provider.
When relying on the Provider being globally registered the PasswordFactory
for a specific algorithm can be obtained as: -
PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm);
However an alternative approach could be to manually instantiate the Provider and pass it in when obtaining an instance of the PasswordFactory
: -
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
...
PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm, ELYTRON_PROVIDER);
2.5.2. Clear Password
The simplest type of Password
to obtain from the PasswordFactory
is a clear text password, the following code illustrates how this can be obtained.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, ELYTRON_PROVIDER);
ClearPasswordSpec passwordSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());
Password password = passwordFactory.generatePassword(passwordSpec);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(password, TEST_PASSWORD.toCharArray())));
}
A second approach is to obtain a raw representation of the ClearPassword
, however this will need to be translated into the PasswordFactory
if it will be used for evidence validation but if the Password
is not being used for verification this can be a suitable alternative provided the parameters have been pre-verified.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, ELYTRON_PROVIDER);
Password rawPassword = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, TEST_PASSWORD.toCharArray());
Password password = passwordFactory.translate(rawPassword);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(password, TEST_PASSWORD.toCharArray())));
}
The raw password can be used for other areas of the Elytron APIs however if it is used for validation an error similar to the following will be thrown.
Exception in thread "main" java.security.InvalidKeyException
at org.wildfly.security.password.impl.PasswordFactorySpiImpl.engineVerify(PasswordFactorySpiImpl.java:762)
at org.wildfly.security.password.PasswordFactory.verify(PasswordFactory.java:210)
[source,java]
2.5.3. Simple Digest
The next type of password is the simple digest, for this password type the clear text password id digested using the specified algorithm however no salt is used.
The following algorithms are applicable to this type of encoding.
-
simple-digest-md2
-
simple-digest-md5
-
simple-digest-sha-1
-
simple-digest-sha-256
-
simple-digest-sha-384
-
simple-digest-sha-512
The following example illustrates how the Password
instance can be created starting from the clear text password,from this the digested representation of the password can be obtained which could be used to store the password. The digested representation is then used to create a further Password
instance.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_512, ELYTRON_PROVIDER);
ClearPasswordSpec clearSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());
SimpleDigestPassword original = (SimpleDigestPassword) passwordFactory.generatePassword(clearSpec);
byte[] digest = original.getDigest();
HashPasswordSpec hashSpec = new HashPasswordSpec(digest);
SimpleDigestPassword restored = (SimpleDigestPassword) passwordFactory.generatePassword(hashSpec);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
Starting from the digest the raw APIs can be used as.
SimpleDigestPassword rawPassword = SimpleDigestPassword.createRaw(SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_512, digest);
SimpleDigestPassword restored = (SimpleDigestPassword) passwordFactory.translate(rawPassword);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
2.5.4. Salted Digest
Two variations of salted digests are supported, these can either be digested with the salt first or the password first. The following algorithms are supported: -
-
password-salt-digest-md5
-
password-salt-digest-sha-1
-
password-salt-digest-sha-256
-
password-salt-digest-sha-384
-
password-salt-digest-sha-512
-
salt-password-digest-md5
-
salt-password-digest-sha-1
-
salt-password-digest-sha-256
-
salt-password-digest-sha-384
-
salt-password-digest-sha-512
The following example shows how using a generated salt the password can be created from a clear text password and then subsequently how the password can be recreated from the salt and digest.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512, ELYTRON_PROVIDER);
byte[] salt = new byte[32];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
SaltedPasswordAlgorithmSpec saltedSpec = new SaltedPasswordAlgorithmSpec(salt);
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), saltedSpec);
SaltedSimpleDigestPassword original = (SaltedSimpleDigestPassword) passwordFactory.generatePassword(encryptableSpec);
byte[] digest = original.getDigest();
SaltedHashPasswordSpec saltedHashSpec = new SaltedHashPasswordSpec(digest, salt);
SaltedSimpleDigestPassword restored = (SaltedSimpleDigestPassword) passwordFactory.generatePassword(saltedHashSpec);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
Alternatively the salt could be generated automatically by using a ClearPasswordSpec
.
ClearPasswordSpec clearSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());
SaltedSimpleDigestPassword original = (SaltedSimpleDigestPassword) passwordFactory.generatePassword(clearSpec);
byte[] salt = original.getSalt();
byte[] digest = original.getDigest();
Starting with the digest and salt the raw APIs can also be used.
SaltedSimpleDigestPassword rawPassword = SaltedSimpleDigestPassword.createRaw(SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512, digest, salt);
SaltedSimpleDigestPassword restored = (SaltedSimpleDigestPassword) passwordFactory.translate(rawPassword);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
2.5.5. Digest
The Digest passwords are an alternative form of digest where the username, realm and password are digested together delimited with a ':', these are usable with clear text authentication mechanisms but also usable with the digest authentication mechanisms also eliminating the transmission of clear text passwords during authentication. The following algorithms are supported: -
-
digest-md5
-
digest-sha
-
digest-sha-256
-
digest-sha-384
-
digest-sha-512
-
digest-sha-512-256
The following example illustrates how a password can be created from the username, realm, and password and then how it can be recreated from the digest, realm, and password.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_USERNAME = "test_username";
static final String TEST_REALM = "Test Realm";
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(DigestPassword.ALGORITHM_DIGEST_MD5, ELYTRON_PROVIDER);
DigestPasswordAlgorithmSpec digestAlgorithmSpec = new DigestPasswordAlgorithmSpec(TEST_USERNAME, TEST_REALM);
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), digestAlgorithmSpec);
DigestPassword original = (DigestPassword) passwordFactory.generatePassword(encryptableSpec);
byte[] digest = original.getDigest();
DigestPasswordSpec digestPasswordSpec = new DigestPasswordSpec(TEST_USERNAME, TEST_REALM, digest);
DigestPassword restored = (DigestPassword) passwordFactory.generatePassword(digestPasswordSpec);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
For this password type is not possible to create the password from the ClearPasswordSpec
as additional information always needs to be specified and can not be dynamically or randomly generated, however the raw APIs can still be used.
DigestPassword rawPassword = DigestPassword.createRaw(DigestPassword.ALGORITHM_DIGEST_MD5, TEST_USERNAME, TEST_REALM, digest);
DigestPassword restored = (DigestPassword) passwordFactory.translate(rawPassword);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
2.5.6. SCRAM
Another set of passwords more tightly tied to a specific authentication mechanism are the SCRAM password types, the following algorithms are supported: -
-
scram-sha-1
-
scram-sha-256
-
scram-sha-384
-
scram-sha-512
The following demonstrates how a clear password can be converted to a scram password using a specified salt and iteration count and how this can be recreated from the digested value.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(ScramDigestPassword.ALGORITHM_SCRAM_SHA_512, ELYTRON_PROVIDER);
byte[] salt = new byte[12];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
IteratedSaltedPasswordAlgorithmSpec iteratedAlgorithmSpec = new IteratedSaltedPasswordAlgorithmSpec(2000, salt);
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), iteratedAlgorithmSpec);
ScramDigestPassword original = (ScramDigestPassword) passwordFactory.generatePassword(encryptableSpec);
byte[] digest = original.getDigest();
IteratedSaltedHashPasswordSpec scramPasswordSpec = new IteratedSaltedHashPasswordSpec(digest, salt, 2000);
ScramDigestPassword restored = (ScramDigestPassword) passwordFactory.generatePassword(scramPasswordSpec);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
Alternatively instead of using the IteratedSaltedPasswordAlgorithmSpec
is it also possible to use a SaltedPasswordAlgorithmSpec
when converting from the clear text password and a default iteration count will be used instead, this is similar to how the conversion happens for salted digest passwords.
It is also possible to omit the salt and iteration count and these will be generated.
ClearPasswordSpec clearSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());
ScramDigestPassword original = (ScramDigestPassword) passwordFactory.generatePassword(clearSpec);
byte[] salt = original.getSalt();
byte[] digest = original.getDigest();
int iterationCount = original.getIterationCount();
Starting with a digest, salt, and iteration count the raw APIs can also be used.
ScramDigestPassword rawPassword = ScramDigestPassword.createRaw(ScramDigestPassword.ALGORITHM_SCRAM_SHA_256, digest, salt, 2000);
ScramDigestPassword restored = (ScramDigestPassword) passwordFactory.translate(rawPassword);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
2.5.7. OTP
One more type of mechanism specific password is the one time password type. The following algorithms are supported: -
-
otp-md5
-
otp-sha1
-
otp-sha256
-
otp-sha384
-
otp-sha512
The following demonstrates how a clear password can be converted to a one time password using a specified seed and iteration count and how this can be recreated from the hashed value.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(OneTimePassword.ALGORITHM_OTP_SHA_512, ELYTRON_PROVIDER);
String seed = "ke1234";
int sequenceNumber = 500;
OneTimePasswordAlgorithmSpec oneTimeAlgorithmSpec = new OneTimePasswordAlgorithmSpec(OneTimePassword.ALGORITHM_OTP_SHA_512, seed, sequenceNumber);
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), oneTimeAlgorithmSpec);
OneTimePassword original = (OneTimePassword) passwordFactory.generatePassword(encryptableSpec);
byte[] hash = original.getHash();
OneTimePasswordSpec oneTimeSpec = new OneTimePasswordSpec(hash, seed, sequenceNumber);
OneTimePassword restored = (OneTimePassword) passwordFactory.generatePassword(oneTimeSpec);
}
This example does not include verification as that is handled by the SASL mechanism which also increments the sequence and replaces the hash, this does mean this password type needs to be used with a security realm which also supports updates.
Starting with the hash and sequence number the raw APIs can also be used.
OneTimePassword rawPassword = OneTimePassword.createRaw(OneTimePassword.ALGORITHM_OTP_SHA_512, original.getHash(), original.getSeed(), original.getSequenceNumber());
OneTimePassword restored = (OneTimePassword) passwordFactory.translate(rawPassword);
2.5.8. Other Iterated Salted Types
The following algorithms are also supported for alternative iterated salted password types: -
-
bcrypt
-
sun-crypt-md5
-
sun-crypt-md5-bare-salt
-
crypt-sha-256
-
crypt-sha-512
-
bsd-crypt-des
The general pattern for working with these password types is the same as was used for Scram password types if an interaction count is specified or the same as salted digest password types if a default iteration count is to be used instead.
2.5.9. Other Salted Types
The following algorithms are also supported for salted password types: -
-
crypt-md5
-
crypt-des
The general pattern for working with these password types is the same as was used for salted digest password types as no iteration count is required.
2.5.10. Masked Password Types
Finally a set of masked password types are also supported to add support for legacy password types which were previously supported within PicketBox, the following algorithms are supported.
-
masked-MD5-DES
-
masked-MD5-DES-CBC-PKCS5
-
masked-MD5-3DES
-
masked-MD5-3DES-CBC-PKCS5
-
masked-SHA1-DES-EDE
-
masked-SHA1-DES-EDE-CBC-PKCS5
-
masked-SHA1-RC2-40
-
masked-SHA1-RC2-40-CBC-PKCS5
-
masked-SHA1-RC2-128
-
masked-SHA1-RC2-128-CBC-PKCS5
-
masked-SHA1-RC4-40
-
masked-SHA1-RC4-40-ECB
-
masked-SHA1-RC4-128
-
masked-SHA1-RC4-128-ECB
-
masked-HMAC-SHA1-AES-128
-
masked-HMAC-SHA224-AES-128
-
masked-HMAC-SHA256-AES-128
-
masked-HMAC-SHA384-AES-128
-
masked-HMAC-SHA512-AES-128
-
masked-HMAC-SHA1-AES-256
-
masked-HMAC-SHA224-AES-256
-
masked-HMAC-SHA256-AES-256
-
masked-HMAC-SHA384-AES-256
-
masked-HMAC-SHA512-AES-256
-
masked-PBKDF-HMAC-SHA1
-
masked-PBKDF-HMAC-SHA224
-
masked-PBKDF-HMAC-SHA256
-
masked-PBKDF-HMAC-SHA384
-
masked-PBKDF-HMAC-SHA512
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(MaskedPassword.ALGORITHM_MASKED_MD5_DES, ELYTRON_PROVIDER);
char[] key = "my_secret_key".toCharArray();
byte[] salt = new byte[8];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
int iterationCount = 100;
MaskedPasswordAlgorithmSpec maskedAlgorithmSpec = new MaskedPasswordAlgorithmSpec(key, iterationCount, salt);
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), maskedAlgorithmSpec);
MaskedPassword original = (MaskedPassword) passwordFactory.generatePassword(encryptableSpec);
byte[] masked = original.getMaskedPasswordBytes();
MaskedPasswordSpec maskedPasswordSpec = new MaskedPasswordSpec(key, iterationCount, salt, masked);
MaskedPassword restored = (MaskedPassword) passwordFactory.generatePassword(maskedPasswordSpec);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
As with the other password types the raw password APIs can also be used to recreate the password.
MaskedPassword rawPassword = MaskedPassword.createRaw(MaskedPassword.ALGORITHM_MASKED_MD5_DES, key, iterationCount, salt, masked);
MaskedPassword restored = (MaskedPassword) passwordFactory.translate(rawPassword);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
2.5.11. Modular Crypt Encoding
A number of password types can be encoded using modular crypt allowing information such as the password type, the hash or digest, the salt, and the iteration count to be encoded in a single String, this can make storage and retrieval of passwords easier as multiple pieces of related data can be handled as one.
Within the WildFly Elytron project the utility org.wildfly.security.password.util.ModularCrypt
can be used to handle the encoding and decoding.
The following password types can be encoded and decoded: -
-
BCryptPassword
-
BSDUnixDESCryptPassword
-
UnixDESCryptPassword
-
UnixMD5CryptPassword
-
SunUnixMD5CryptPassword
-
UnixSHACryptPassword
The following code demonstrates this for a BSDUnixDESCryptPassword
: -
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "myPassword";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(BSDUnixDESCryptPassword.ALGORITHM_BSD_CRYPT_DES, ELYTRON_PROVIDER);
int iterationCount = BSDUnixDESCryptPassword.DEFAULT_ITERATION_COUNT;
byte[] salt = new byte[BSDUnixDESCryptPassword.BSD_CRYPT_DES_SALT_SIZE];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
IteratedSaltedPasswordAlgorithmSpec iteratedAlgorithmSpec = new IteratedSaltedPasswordAlgorithmSpec(iterationCount, salt);
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), iteratedAlgorithmSpec);
BSDUnixDESCryptPassword original = (BSDUnixDESCryptPassword) passwordFactory.generatePassword(encryptableSpec);
String modularCryptString = ModularCrypt.encodeAsString(original);
Password rawPassword = ModularCrypt.decode(modularCryptString);
BSDUnixDESCryptPassword restored = (BSDUnixDESCryptPassword) passwordFactory.translate(rawPassword);
System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
The Password returned from the call to ModularCrypt.decode(…) is a raw password so needs translating by the PasswordFactory .
|
3. Elytron Subsystem
WildFly Elytron is a security framework used to unify security across the entire application server. The elytron subsystem enables a single point of configuration for securing both applications and the management interfaces. WildFly Elytron also provides a set of APIs and SPIs for providing custom implementations of functionality and integrating with the elytron subsystem.
In addition, there are several other important features of the WildFly Elytron:
-
Stronger authentication mechanisms for HTTP and SASL authentication.
-
Improved architecture that allows for SecurityIdentities to be propagated across security domains and transparently transformed ready to be used for authorization. This transformation takes place using configurable role decoders, role mappers, and permission mappers.
-
Centralized point for SSL/TLS configuration including cipher suites and protocols.
-
SSL/TLS optimizations such as eager SecureIdentity construction and closely tying authorization to establishing an SSL/TLS connection. Eager SecureIdentity construction eliminates the need for a SecureIdentity to be constructed on a per-request basis. Closely tying authentication to establishing an SSL/TLS connection enables permission checks to happen BEFORE the first request is received.
-
A secure credential store that replaces the previous vault implementation to store clear text credentials.
The new elytron subsystem exists in parallel to the legacy security subsystem and legacy core management authentication. Both the legacy and Elytron methods may be used for securing the management interfaces as well as providing security for applications.
3.1. Get Started using the Elytron Subsystem
To get started using Elytron, refer to these topics:
-
Use the default Elytron components for application and management authentication
-
Secure an application with a new identity store stored in a filesystem or database.
-
Set up one-way SSL/TLS for applications or the management interfaces.
-
Set up two-way SSL/TLS for applications or the management interfaces.
-
Create a credential store and use it with your SSL/TLS configuration.
-
Override an application’s authentication configuration with Elytron authentication.
-
Secure applications and the management interfaces with an LDAP-based identity store.
3.2. Provided components
WildFly Elytron provides a default set of implementations in the elytron subsystem.
3.2.1. Factories
Component | Description |
---|---|
aggregate-http-server-mechanism-factory |
An HTTP server factory definition where the HTTP server factory is an aggregation of other HTTP server factories. |
aggregate-sasl-server-factory |
A SASL server factory definition where the SASL server factory is an aggregation of other SASL server factories. |
configurable-http-server-mechanism-factory |
A SASL server factory definition where the SASL server factory is an aggregation of other SASL server factories. |
configurable-sasl-server-factory |
A SASL server factory definition where the SASL server factory is an aggregation of other SASL server factories. |
custom-credential-security-factory |
A custom credential SecurityFactory definition. |
http-authentication-factory |
Resource containing the association of a security domain with a HttpServerAuthenticationMechanismFactory. |
kerberos-security-factory |
A security factory for obtaining a GSSCredential for use during authentication. |
mechanism-provider-filtering-sasl-server-factory |
A SASL server factory definition that enables filtering by provider where the factory was loaded using a provider. |
provider-http-server-mechanism-factory |
An HTTP server factory definition where the HTTP server factory is an aggregation of factories from the provider list. |
provider-sasl-server-factory |
A SASL server factory definition where the SASL server factory is an aggregation of factories from the provider list. |
sasl-authentication-factory |
Resource containing the association of a security domain with a SaslServerFactory. |
service-loader-http-server-mechanism-factory |
An HTTP server factory definition where the HTTP server factory is an aggregation of factories identified using a ServiceLoader |
service-loader-sasl-server-factory |
A SASL server factory definition where the SASL server factory is an aggregation of factories identified using a ServiceLoader |
3.2.2. Principal Transformers
Component | Description |
---|---|
aggregate-principal-transformer |
A principal transformer definition where the principal transformer is an aggregation of other principal transformers. |
case-principal-transformer |
A principal transformer definition where the principal is adjusted to upper or lower case. |
chained-principal-transformer |
A principal transformer definition where the principal transformer is a chaining of other principal transformers. |
constant-principal-transformer |
A principal transformer definition where the principal transformer always returns the same constant. |
custom-principal-transformer |
A custom principal transformer definition. |
regex-principal-transformer |
A regular expression based principal transformer |
regex-validating-principal-transformer |
A regular expression based principal transformer which uses the regular expression to validate the name. |
3.2.3. Principal Decoders
Component | Description |
---|---|
aggregate-principal-decoder |
A principal decoder definition where the principal decoder is an aggregation of other principal decoders. |
concatenating-principal-decoder |
A principal decoder definition where the principal decoder is a concatenation of other principal decoders. |
constant-principal-decoder |
Definition of a principal decoder that always returns the same constant. |
custom-principal-decoder |
Definition of a custom principal decoder. |
x500-attribute-principal-decoder |
Definition of a X500 attribute based principal decoder. |
3.2.4. Evidence Decoders
Component | Description |
---|---|
x509-subject-alt-name-evidence-decoder |
An evidence decoder that derives the principal associated with a certificate chain from an X.509 subject alternative name from the first certificate in the given chain. |
x500-subject-evidence-decoder |
An evidence decoder that derives the principal associated with a certificate chain from the subject from the first certificate in the given chain. |
custom-evidence-decoder |
Definition of a custom evidence decoder. |
aggregate-evidence-decoder |
An evidence decoder that is an aggregation of other evidence decoders. Given evidence, these evidence decoders will be attempted in order until one returns a non-null principal or until there are no more evidence decoders left to try. |
3.2.5. Realm Mappers
Component | Description |
---|---|
constant-realm-mapper |
Definition of a constant realm mapper that always returns the same value. |
custom-realm-mapper |
Definition of a custom realm mapper |
mapped-regex-realm-mapper |
Definition of a realm mapper implementation that first uses a regular expression to extract the realm name, this is then converted using the configured mapping of realm names. |
simple-regex-realm-mapper |
Definition of a simple realm mapper that attempts to extract the realm name using the capture group from the regular expression, if that does not provide a match then the delegate realm mapper is used instead. |
3.2.6. Realms
Component | Description |
---|---|
A realm definition that is an aggregation of two or more realms, one for the authentication steps and one or more for loading the identity for the authorization steps and aggregating the resulting attributes. |
|
caching-realm |
A realm definition that enables caching to another security realm. Caching strategy is Least Recently Used where least accessed entries are discarded when maximum number of entries is reached. |
custom-modifiable-realm |
Custom realm configured as being modifiable will be expected to implement the ModifiableSecurityRealm interface. By configuring a realm as being modifiable management operations will be made available to manipulate the realm. |
custom-realm |
A custom realm definitions can implement either the s SecurityRealm interface or the ModifiableSecurityRealm interface. Regardless of which interface is implemented management operations will not be exposed to manage the realm. However other services that depend on the realm will still be able to perform a type check and cast to gain access to the modification API. |
A security realm definition for authentication and authorization identities distributed between multiple security realms. |
|
A realm definition that is an aggregation of two realms, one for default behaviour and second for cases when first realm is unavailable. |
|
A simple security realm definition backed by the filesystem. |
|
identity-realm |
A security realm definition where identities are represented in the management model. |
A security realm definition that uses JAAS configuration file to initialize LoginContext that is used to obtain identities. |
|
A security realm definition backed by database using JDBC. |
|
key-store-realm |
A security realm definition backed by a keystore. |
ldap-realm |
A security realm definition backed by LDAP. |
properties-realm |
A security realm definition backed by properties files. |
token-realm |
A security realm definition capable of validating and extracting identities from security tokens. |
3.2.7. Permission Mappers
Component | Description |
---|---|
custom-permission-mapper |
Definition of a custom permission mapper. |
logical-permission-mapper |
Definition of a logical permission mapper. |
simple-permission-mapper |
Definition of a simple configured permission mapper. |
constant-permission-mapper |
Definition of a permission mapper that always returns the same constant. |
3.2.8. Role Decoders
Component | Description |
---|---|
custom-role-decoder |
Definition of a custom RoleDecoder |
simple-role-decoder |
Definition of a simple RoleDecoder that takes a single attribute and maps it directly to roles. |
source-address-role-decoder |
Definition of a RoleDecoder that maps roles based on the IP address of the remote client. |
aggregate-role-decoder |
A role decoder that is an aggregation of other role decoders. An aggregate role decoder combines the roles obtained using each role decoder. |
3.2.9. Role Mappers
Component | Description |
---|---|
add-prefix-role-mapper |
A role mapper definition for a role mapper that adds a prefix to each provided. |
add-suffix-role-mapper |
A role mapper definition for a role mapper that adds a suffix to each provided. |
constant-role-mapper |
A role mapper definition where a constant set of roles is always returned. |
aggregate-role-mapper |
A role mapper definition where the role mapper is an aggregation of other role mappers. |
logical-role-mapper |
A role mapper definition for a role mapper that performs a logical operation using two referenced role mappers. |
custom-role-mapper |
Definition of a custom role mapper |
mapped-role-mapper |
A role mapper definition for a role mapper that uses configured mapping of role names to map role names. |
regex-role-mapper |
A role mapper definition for a role mapper that performs a regex matching and maps matching roles with provided pattern. Regex can capture groups that replacement pattern can make use of. |
3.2.10. SSL Components
Component | Description |
---|---|
client-ssl-context |
An SSLContext for use on the client side of a connection. |
filtering-key-store |
A filtering keystore definition, which provides a keystore by filtering a key-store. |
key-manager |
A key manager definition for creating the key manager list as used to create an SSL context. |
key-store |
A keystore definition. |
ldap-key-store |
An LDAP keystore definition, which loads a keystore from an LDAP server. |
server-ssl-context |
An SSL context for use on the server side of a connection. |
trust-manager |
A trust manager definition for creating the TrustManager list as used to create an SSL context. |
certificate-authority-account |
A certificate authority account which can be used to obtain and revoke signed certificates. |
3.2.11. Other
Component | Description |
---|---|
aggregate-providers |
An aggregation of two or more Provider[] resources. |
authentication-configuration |
An individual authentication configuration definition, which is used by clients deployed to WildFly and other resources for authenticating when making a remote connection. |
authentication-context |
An individual authentication context definition, which is used to supply an ssl-context and authentication-configuration when clients deployed to WildFly and other resources make a remoting connection. |
credential-store |
Credential store to keep alias for sensitive information such as passwords for external services. |
dir-context |
The configuration to connect to a directory (LDAP) server. |
provider-loader |
A definition for a provider loader. |
security-domain |
A security domain definition. |
security-property |
A definition of a security property to be set. |
3.3. Out of the Box Configuration
WildFly provides a set of components configured by default. While these components are ready to use, the legacy security subsystem and legacy core management authentication is still used by default. To configure WildFly to use the these configured components as well as create new ones, see the Using the Elytron Subsystem section.
Default Component | Description |
---|---|
ApplicationDomain |
The ApplicationDomain security domain uses ApplicationRealm and groups-to-roles for authentication. It also uses default-permission-mapper to assign the login permission. |
ManagementDomain |
The ManagementDomain security domain uses two security realms for authentication: ManagementRealm with groups-to-roles and local with super-user-mapper. It also uses default-permission-mapper to assign the login permission. |
local (security realm) |
The local security realm does no authentication and sets the identity of principals to $local |
ApplicationRealm |
The ApplicationRealm security realm is a properties realm that authenticates principals using application-users.properties and assigns roles using application-roles.properties. These files are located under jboss.server.config.dir, which by default, maps to EAP_HOME/standalone/configuration. They are also the same files used by the legacy security default configuration. |
ManagementRealm |
The ManagementRealm security realm is a properties realm that authenticates principals using mgmt-users.properties and assigns roles using mgmt-groups.properties. These files are located under jboss.server.config.dir, which by default, maps to EAP_HOME/standalone/configuration. They are also the same files used by the legacy security default configuration. |
default-permission-mapper |
The default-permission-mapper mapper is a simple permission mapper that uses the default-permissions permission set to assign the full set of permissions that an identity would require to access any services on the server. For example, the default-permission-mapper mapper uses org.wildfly.extension.batch.jberet.deployment.BatchPermission specified by the default-permissions permission set to assign permission for batch jobs. The batch permissions are start, stop, restart, abandon, and read which aligns with jakarta.batch.operations.JobOperator. The default-permission-mapper mapper also uses org.wildfly.security.auth.permission.LoginPermission specified by the the login-permission permission set to assign the login permission. |
local (mapper) |
The local mapper is a constant role mapper that maps to the local security realm. This is used to map authentication to the local security realm. |
groups-to-roles |
The groups-to-roles mapper is a simple-role-decoder that will decode the groups information of a principal and use it for the role information. |
super-user-mapper |
The super-user-mapper mapper is a constant role mapper that maps the SuperUser role to a principal. |
management-http-authentication |
The management-http-authentication http-authentication-factory can be used for doing authentication over http. It uses the global provider-http-server-mechanism-factory to filter authentication mechanism and uses ManagementDomain for authenticating principals. It accepts the DIGEST authentication mechanisms and exposes it as ManagementRealm to applications. |
global (provider-http-server-mechanism-factory) |
This is the HTTP server factory mechanism definition used to list the provided authentication mechanisms when creating an http authentication factory. |
management-sasl-authentication |
The management-sasl-authentication sasl-authentication-factory can be used for authentication using SASL. It uses the configured sasl-server-factory to filter authentication mechanisms, which also uses the global provider-sasl-server-factory to filter by provider names. management-sasl-authentication uses the ManagementDomain security domain for authentication of principals. It also maps authentication using JBOSS-LOCAL-USER mechanisms using the local realm mapper and authentication using DIGEST-MD5 to ManagementRealm. |
application-sasl-authentication |
The application-sasl-authentication sasl-authentication-factory can be used for authentication using SASL. It uses the configured sasl-server-factory to filter authentication mechanisms, which also uses the global provider-sasl-server-factory to filter by provider names. application-sasl-authentication uses the ApplicationDomain security domain for authentication of principals. |
global (provider-sasl-server-factory) |
This is the SASL server factory definition used to create SASL authentication factories. |
elytron (mechanism-provider-filtering-sasl-server-factor) |
This is used to filter which sasl-authentication-factory is used based on the provider. In this case, elytron will match on the WildFlyElytron provider name. |
configured (configurable-sasl-server-factory) |
This is used to filter sasl-authentication-factory is used based on the mechanism name. In this case, configured will match on JBOSS-LOCAL-USER and DIGEST-MD5. It also sets the wildfly.sasl.local-user.default-user to $local. |
applicationSSC |
The |
combined-providers |
Is an aggregate provider that aggregates the elytron and openssl provider loaders. |
elytron |
A provider loader |
openssl |
A provider loader |
Default WildFly Configuration
/subsystem=elytron:read-resource(recursive=true)
{
"outcome" => "success",
"result" => {
"default-authentication-context" => undefined,
"disallowed-providers" => ["OracleUcrypto"],
"final-providers" => "combined-providers",
"initial-providers" => undefined,
"security-properties" => undefined,
"add-prefix-role-mapper" => undefined,
"add-suffix-role-mapper" => undefined,
"aggregate-evidence-decoder" => undefined,
"aggregate-http-server-mechanism-factory" => undefined,
"aggregate-principal-decoder" => undefined,
"aggregate-principal-transformer" => undefined,
"aggregate-providers" => {"combined-providers" => {"providers" => [
"elytron",
"openssl"
]}},
"aggregate-realm" => undefined,
"aggregate-role-decoder" => undefined,
"aggregate-role-mapper" => undefined,
"aggregate-sasl-server-factory" => undefined,
"aggregate-security-event-listener" => undefined,
"authentication-configuration" => undefined,
"authentication-context" => undefined,
"caching-realm" => undefined,
"case-principal-transformer" => undefined,
"certificate-authority-account" => undefined,
"chained-principal-transformer" => undefined,
"client-ssl-context" => undefined,
"concatenating-principal-decoder" => undefined,
"configurable-http-server-mechanism-factory" => undefined,
"configurable-sasl-server-factory" => {"configured" => {
"filters" => undefined,
"properties" => {"wildfly.sasl.local-user.default-user" => "$local"},
"protocol" => undefined,
"sasl-server-factory" => "elytron",
"server-name" => undefined
}},
"constant-permission-mapper" => undefined,
"constant-principal-decoder" => undefined,
"constant-principal-transformer" => undefined,
"constant-realm-mapper" => {"local" => {"realm-name" => "local"}},
"constant-role-mapper" => {"super-user-mapper" => {"roles" => ["SuperUser"]}},
"credential-store" => undefined,
"custom-credential-security-factory" => undefined,
"custom-evidence-decoder" => undefined,
"custom-modifiable-realm" => undefined,
"custom-permission-mapper" => undefined,
"custom-principal-decoder" => undefined,
"custom-principal-transformer" => undefined,
"custom-realm" => undefined,
"custom-realm-mapper" => undefined,
"custom-role-decoder" => undefined,
"custom-role-mapper" => undefined,
"custom-security-event-listener" => undefined,
"dir-context" => undefined,
"file-audit-log" => {"local-audit" => {
"format" => "JSON",
"path" => "audit.log",
"relative-to" => "jboss.server.log.dir",
"synchronized" => true
}},
"filesystem-realm" => undefined,
"filtering-key-store" => undefined,
"http-authentication-factory" => {
"management-http-authentication" => {
"http-server-mechanism-factory" => "global",
"mechanism-configurations" => [{
"mechanism-name" => "DIGEST",
"mechanism-realm-configurations" => [{"realm-name" => "ManagementRealm"}]
}],
"security-domain" => "ManagementDomain"
}
},
"identity-realm" => {"local" => {
"attribute-name" => undefined,
"attribute-values" => undefined,
"identity" => "$local"
}},
"jdbc-realm" => undefined,
"kerberos-security-factory" => undefined,
"key-manager" => {
"applicationKM" => {
"algorithm" => undefined,
"alias-filter" => undefined,
"credential-reference" => {"clear-text" => "password"},
"generate-self-signed-certificate-host" => "localhost",
"key-store" => "applicationKS",
"provider-name" => undefined,
"providers" => undefined
}
}
"key-store" => {
"applicationKS" => {
"alias-filter" => undefined,
"credential-reference" => {"clear-text" => "password"},
"path" => "application.keystore",
"relative-to" => "jboss.server.config.dir",
"required" => false,
"provider-name" => undefined,
"providers" => undefined,
"type" => "JKS"
}
},
"key-store-realm" => undefined,
"ldap-key-store" => undefined,
"ldap-realm" => undefined,
"logical-permission-mapper" => undefined,
"logical-role-mapper" => undefined,
"mapped-regex-realm-mapper" => undefined,
"mapped-role-mapper" => undefined,
"mechanism-provider-filtering-sasl-server-factory" => {"elytron" => {
"enabling" => true,
"filters" => [{"provider-name" => "WildFlyElytron"}],
"sasl-server-factory" => "global"
}},
"periodic-rotating-file-audit-log" => undefined,
"permission-set" => {
"login-permission" => {"permissions" => [{"class-name" => "org.wildfly.security.auth.permission.LoginPermission"}]},
"default-permissions" => {"permissions" => [
{
"class-name" => "org.wildfly.extension.batch.jberet.deployment.BatchPermission",
"module" => "org.wildfly.extension.batch.jberet",
"target-name" => "*"
},
{
"class-name" => "org.wildfly.transaction.client.RemoteTransactionPermission",
"module" => "org.wildfly.transaction.client"
},
{
"class-name" => "org.jboss.ejb.client.RemoteEJBPermission",
"module" => "org.jboss.ejb-client"
}
]}
},
"policy" => undefined,
"properties-realm" => {
"ApplicationRealm" => {
"groups-attribute" => "groups",
"groups-properties" => {
"path" => "application-roles.properties",
"relative-to" => "jboss.server.config.dir"
},
"users-properties" => {
"path" => "application-users.properties",
"relative-to" => "jboss.server.config.dir",
"digest-realm-name" => "ApplicationRealm"
}
},
"ManagementRealm" => {
"groups-attribute" => "groups",
"groups-properties" => {
"path" => "mgmt-groups.properties",
"relative-to" => "jboss.server.config.dir"
},
"users-properties" => {
"path" => "mgmt-users.properties",
"relative-to" => "jboss.server.config.dir",
"digest-realm-name" => "ManagementRealm"
}
}
},
"provider-http-server-mechanism-factory" => {"global" => {"providers" => undefined}},
"provider-loader" => {
"elytron" => {
"argument" => undefined,
"class-names" => undefined,
"configuration" => undefined,
"module" => "org.wildfly.security.elytron",
"path" => undefined,
"relative-to" => undefined
},
"openssl" => {
"argument" => undefined,
"class-names" => undefined,
"configuration" => undefined,
"module" => "org.wildfly.openssl",
"path" => undefined,
"relative-to" => undefined
}
},
"provider-sasl-server-factory" => {"global" => {"providers" => undefined}},
"regex-role-mapper" => undefined,
"regex-principal-transformer" => undefined,
"regex-validating-principal-transformer" => undefined,
"sasl-authentication-factory" => {
"application-sasl-authentication" => {
"mechanism-configurations" => [
{
"mechanism-name" => "JBOSS-LOCAL-USER",
"realm-mapper" => "local"
},
{
"mechanism-name" => "DIGEST-MD5",
"mechanism-realm-configurations" => [{"realm-name" => "ApplicationRealm"}]
}
],
"sasl-server-factory" => "configured",
"security-domain" => "ApplicationDomain"
},
"management-sasl-authentication" => {
"mechanism-configurations" => [
{
"mechanism-name" => "JBOSS-LOCAL-USER",
"realm-mapper" => "local"
},
{
"mechanism-name" => "DIGEST-MD5",
"mechanism-realm-configurations" => [{"realm-name" => "ManagementRealm"}]
}
],
"sasl-server-factory" => "configured",
"security-domain" => "ManagementDomain"
}
},
"security-domain" => {
"ApplicationDomain" => {
"default-realm" => "ApplicationRealm",
"outflow-anonymous" => false,
"outflow-security-domains" => undefined,
"permission-mapper" => "default-permission-mapper",
"post-realm-principal-transformer" => undefined,
"pre-realm-principal-transformer" => undefined,
"principal-decoder" => undefined,
"realm-mapper" => undefined,
"realms" => [
{
"realm" => "ApplicationRealm",
"role-decoder" => "groups-to-roles"
},
{"realm" => "local"}
],
"role-mapper" => undefined,
"security-event-listener" => undefined,
"trusted-security-domains" => undefined
},
"ManagementDomain" => {
"default-realm" => "ManagementRealm",
"outflow-anonymous" => false,
"outflow-security-domains" => undefined,
"permission-mapper" => "default-permission-mapper",
"post-realm-principal-transformer" => undefined,
"pre-realm-principal-transformer" => undefined,
"principal-decoder" => undefined,
"realm-mapper" => undefined,
"realms" => [
{
"realm" => "ManagementRealm",
"role-decoder" => "groups-to-roles"
},
{
"realm" => "local",
"role-mapper" => "super-user-mapper"
}
],
"role-mapper" => undefined,
"security-event-listener" => undefined,
"trusted-security-domains" => undefined
}
},
"server-ssl-context" => {
"applicationSSC" => {
"authentication-optional" => false,
"cipher-suite-filter" => "DEFAULT",
"cipher-suite-names" => undefined,
"final-principal-transformer" => undefined,
"key-manager" => "applicationKM",
"maximum-session-cache-size" => -1,
"need-client-auth" => false,
"post-realm-principal-transformer" => undefined,
"pre-realm-principal-transformer" => undefined,
"protocols" => undefined,
"provider-name" => undefined,
"providers" => undefined,
"realm-mapper" => undefined,
"security-domain" => undefined,
"session-timeout" => -1,
"trust-manager" => undefined,
"use-cipher-suites-order" => true,
"want-client-auth" => false,
"wrap" => false,
"ssl-session" => undefined
}
},
"service-loader-http-server-mechanism-factory" => undefined,
"service-loader-sasl-server-factory" => undefined,
"simple-permission-mapper" => {"default-permission-mapper" => {
"mapping-mode" => "first",
"permission-mappings" => [
{
"principals" => ["anonymous"],
"permission-sets" => [{"permission-set" => "default-permissions"}]
},
{
"match-all" => true,
"permission-sets" => [
{"permission-set" => "login-permission"},
{"permission-set" => "default-permissions"}
]
}
]
}},
"simple-regex-realm-mapper" => undefined,
"simple-role-decoder" => {"groups-to-roles" => {"attribute" => "groups"}},
"size-rotating-file-audit-log" => undefined,
"source-address-role-decoder" => undefined,
"syslog-audit-log" => undefined,
"token-realm" => undefined,
"trust-manager" => undefined,
"x500-attribute-principal-decoder" => undefined,
"x500-subject-evidence-decoder" => undefined,
"x509-subject-alt-name-evidence-decoder" => undefined
}
}
3.4. Default Application Authentication Configuration
By default, applications are secured using legacy security domains. Applications must specify a security domain in their web.xml as well as the authentication method. If no security domain is specified by the application, WildFly will use the provided other legacy security domain.
3.4.1. Update WildFly to Use the Default Elytron Components for Application
Authentication
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=example-http-auth)
For more information on configuring an http-authentication-factory, see configure an http-authentication-factory
SSL/TLS
Undertow can be configured to make use of the applicationSSC
server SSL context for testing purposes,
as shown below:
batch
/subsystem=undertow/server=default-server/https-listener=https:undefine-attribute(name=security-realm)
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=applicationSSC)
run-batch
reload
The applicationSSC
server SSL context references the applicationKM
key manager:
/subsystem=elytron/key-manager=applicationKM:read-resource()
{
"outcome" => "success",
"result" => {
"algorithm" => undefined,
"alias-filter" => undefined,
"credential-reference" => {"clear-text" => "password"},
"generate-self-signed-certificate-host" => "localhost",
"key-store" => "applicationKS",
"provider-name" => undefined,
"providers" => undefined
}
}
When the applicationSSC
server SSL context is used by Undertow, a self-signed certificate will automatically
be generated the first time the HTTPS interface is accessed if the file that backs the applicationKS
key store
doesn’t exist. This self-signed certificate will then be persisted to the file that backs the applicationKS
key
store. The generate-self-signed-certificate-host
value, localhost
, will be used as the Common Name (CN) value
in the generated self-signed certificate. The following messages will appear in the server log file:
13:21:39,197 WARN [org.wildfly.extension.elytron] (MSC service thread 1-6) WFLYELY01083: KeyStore /wildfly/standalone/configuration/application.keystore not found, it will be auto generated on first use with a self-signed certificate for host localhost
...
13:39:57,152 WARN [org.wildfly.extension.elytron] (default task-1) WFLYELY01084: Generated self-signed certificate at /wildfly/dist/target/wildfly-21.0.0.Beta1-SNAPSHOT/standalone/configuration/application.keystore. Please note that self-signed certificates are not secure and should only be used for testing purposes. Do not use this self-signed certificate in production.
SHA-1 fingerprint of the generated key is fc:16:cf:bf:de:3a:6d:d6:fe:ec:f9:cd:9d:22:c9:3d:43:d7:e3:57
SHA-256 fingerprint of the generated key is 38:69:00:4e:39:e2:40:e2:ef:b6:95:58:c6:ba:d0:0f:56:c5:7c:5d:fc:d5:c3:b9:b0:94:80:9c:f5:45:9d:40
NOTE To disable the automatic self-signed certificate generation, undefine the generate-self-signed-certificate-host
attribute on the applicationKM
key manager.
WARNING This self-signed certificate is only intended to be used for testing purposes. This self-signed certificate
should never be used in a production environment. For more information on configuring an ssl-context
,
see Configuring a server SSLContext. For more information on how to
easily obtain a signed certificate using the WildFly CLI, see Obtain a signed certificate from Let’s Encrypt.
3.4.2. Default Elytron ApplicationDomain Configuration
The http-authentication-factory can be configured to use the ApplicationDomain security domain.
/subsystem=elytron/security-domain=ApplicationDomain:read-resource()
{
"outcome" => "success",
"result" => {
"default-realm" => "ApplicationRealm",
"permission-mapper" => "default-permission-mapper",
"post-realm-principal-transformer" => undefined,
"pre-realm-principal-transformer" => undefined,
"principal-decoder" => undefined,
"realm-mapper" => undefined,
"realms" => [{
"realm" => "ApplicationRealm",
"role-decoder" => "groups-to-roles"
}],
"role-mapper" => undefined,
"trusted-security-domains" => undefined
}
}
The ApplicationDomain security domain is backed by the ApplicationRealm Elytron security realm, which is a properties-based realm.
/subsystem=elytron/properties-realm=ApplicationRealm:read-resource()
{
"outcome" => "success",
"result" => {
"groups-attribute" => "groups",
"groups-properties" => {
"path" => "application-roles.properties",
"relative-to" => "jboss.server.config.dir"
},
"users-properties" => {
"path" => "application-users.properties",
"relative-to" => "jboss.server.config.dir",
"digest-realm-name" => "ApplicationRealm"
}
}
}
3.5. Default Management Authentication Configuration
By default, the WildFly management interfaces are secured using WildFly Elytron capabilities.
3.5.1. Default Elytron Management HTTP Authentication Configuration
When you access the management interface over HTTP, for example when using the web-based management console, WildFly will use the management-http-authentication http-authentication-factory.
/subsystem=elytron/http-authentication-factory=management-http-authentication:read-resource()
{
"outcome" => "success",
"result" => {
"http-server-mechanism-factory" => "global",
"mechanism-configurations" => [{
"mechanism-name" => "DIGEST",
"mechanism-realm-configurations" => [{"realm-name" => "ManagementRealm"}]
}],
"security-domain" => "ManagementDomain"
}
}
The management-http-authentication http-authentication-factory, is configured to use the ManagementDomain security domain.
/subsystem=elytron/security-domain=ManagementDomain:read-resource()
{
"outcome" => "success",
"result" => {
"default-realm" => "ManagementRealm",
"outflow-anonymous" => false,
"outflow-security-domains" => undefined,
"permission-mapper" => "default-permission-mapper",
"post-realm-principal-transformer" => undefined,
"pre-realm-principal-transformer" => undefined,
"principal-decoder" => undefined,
"realm-mapper" => undefined,
"realms" => [
{
"realm" => "ManagementRealm",
"role-decoder" => "groups-to-roles"
},
{
"realm" => "local",
"role-mapper" => "super-user-mapper"
}
],
"role-mapper" => undefined,
"security-event-listener" => undefined,
"trusted-security-domains" => undefined
}
}
The ManagementDomain security domain is backed by the ManagementRealm Elytron security realm, which is a properties-based realm.
/subsystem=elytron/properties-realm=ManagementRealm:read-resource()
{
"outcome" => "success",
"result" => {
"groups-attribute" => "groups",
"groups-properties" => {
"path" => "mgmt-groups.properties",
"relative-to" => "jboss.server.config.dir"
},
"plain-text" => false,
"users-properties" => {
"path" => "mgmt-users.properties",
"relative-to" => "jboss.server.config.dir",
"digest-realm-name" => "ManagementRealm"
}
}
}
3.5.2. Default Elytron Management CLI Authentication
By default, the management CLI ( jboss-cli.sh) is configured to connect over remote+http.
Default jboss-cli.xml
<jboss-cli xmlns="urn:jboss:cli:3.3">
<default-protocol use-legacy-override="true">remote+http</default-protocol>
<!-- The default controller to connect to when 'connect' command is executed w/o arguments -->
<default-controller>
<protocol>remote+http</protocol>
<host>localhost</host>
<port>9990</port>
</default-controller>
This will establish a connection over HTTP and use HTTP upgrade to change the communication protocol to native. The HTTP upgrade connection is secured in the http-upgrade section of the http-interface using a sasl-authentication-factory.
Example Configuration with Default Components
/core-service=management/management-interface=http-interface:read-resource()
{
"outcome" => "success",
"result" => {
"allowed-origins" => undefined,
"console-enabled" => true,
"http-authentication-factory" => "management-http-authentication",
"http-upgrade" => {
"enabled" => true,
"sasl-authentication-factory" => "management-sasl-authentication"
},
"http-upgrade-enabled" => true,
"sasl-protocol" => "remote",
"secure-socket-binding" => undefined,
"security-realm" => undefined,
"server-name" => undefined,
"socket-binding" => "management-http",
"ssl-context" => undefined
}
}
The default sasl-authentication-factory is management-sasl-authentication.
/subsystem=elytron/sasl-authentication-factory=management-sasl-authentication:read-resource()
{
"outcome" => "success",
"result" => {
"mechanism-configurations" => [
{
"mechanism-name" => "JBOSS-LOCAL-USER",
"realm-mapper" => "local"
},
{
"mechanism-name" => "DIGEST-MD5",
"mechanism-realm-configurations" => [{"realm-name" => "ManagementRealm"}]
}
],
"sasl-server-factory" => "configured",
"security-domain" => "ManagementDomain"
}
}
The management-sasl-authentication sasl-authentication-factory specifies JBOSS-LOCAL-USER and DIGEST-MD5 mechanisms.
JBOSS-LOCAL-USER Realm
/subsystem=elytron/identity-realm=local:read-resource()
{
"outcome" => "success",
"result" => {
"attribute-name" => undefined,
"attribute-values" => undefined,
"identity" => "$local"
}
}
The local Elytron security realm is for handling silent authentication for local users.
The ManagementRealm Elytron security realm is the same realm used in the management-http-authentication http-authentication-factory.
3.6. Comparing Legacy Approaches to Elytron Approaches
Legacy Approach | Elytron Approach |
---|---|
UsersRoles Login Module |
Configure Authentication with a Properties File-Based Identity Store |
Database Login Module |
Configure Authentication with a Database Identity Store |
Ldap, LdapExtended, AdvancedLdap, AdvancedADLdap Login Modules |
Configure Authentication with an LDAP-Based Identity Store |
Certificate, Certificate Roles Login Module |
Configure Authentication with Certificates |
Kerberos, SPNEGO Login Modules |
Configure Authentication with a Kerberos-Based Identity Store |
Kerberos, SPNEGO Login Modules with Fallback |
Configure Authentication with a Form as a Fallback for Kerberos |
RoleMapping Login Module |
Configure Authentication with a Mapped Role Mapper |
Vault |
Create and Use a Credential Store |
Legacy Security Realms |
Secure the Management Interfaces with a New Identity Store, Silent Authentication |
RBAC |
Using RBAC with Elytron |
Legacy Security Realms for One-way and Two-way SSL/TLS for Applications |
Enable One-way SSL/TLS for Applications, Enable Two-way SSL/TLS in WildFly for Applications |
Legacy Security Realms for One-way and Two-way SSL/TLS for Management Interfaces |
Enable One-way for the Management Interfaces Using the Elytron Subsystem, Enable Two-way SSL/TLS for the Management Interfaces using the Elytron Subsystem |
4. Using the Elytron Subsystem
4.1. Set Up and Configure Authentication for Applications
4.1.1. Configure Authentication with a Properties File-Based Identity Store
Create properties files:
You need to create two properties files: one that maps user to passwords and another that maps users to roles. Usually these files are located in the jboss.server.config.dir directory and follow the naming convention *-users.properties and *-roles.properties, but other locations and names may be used. The *-users.properties file must also contain a reference to the properties-realm, which you will create in the next step: #$REALM_NAME=YOUR_PROPERTIES_REALM_NAME$
Example user to password file: example-users.properties
#$REALM_NAME=examplePropRealm$ user1=password123 user2=password123
Example user to roles file: example-roles.properties
user1=Admin user2=Guest
The Properties Realm also supports storing hashed passwords in the properties files, namely DIGEST passwords. Learn how to specify the encoding and character set for these passwords in the next section.
Configure a properties-realm in WildFly:
/subsystem=elytron/properties-realm=examplePropRealm:add(groups-attribute=groups,groups-properties={path=example-roles.properties,relative-to=jboss.server.config.dir},users-properties={path=example-users.properties,relative-to=jboss.server.config.dir,plain-text=true})
The name of the properties-realm is examplePropRealm, which is used in the previous step in the example-users.properties file. Also, if your properties files are located outside of jboss.server.config.dir, then you need to change the path and relative-to values appropriately.
Additionally, if storing hashed passwords in the properties file, you can specify the following attributes:
-
hash-encoding
: This attribute specifies the string format for the password if it is not stored in plain text. It is set tohex
encoding by default, butbase64
is also supported. -
hash-charset
: This attribute specifies the character set to use when converting the password string to a byte array. It is set toUTF-8
by default.
For example, the following properties realm is configured to use base64
encoding and the character set
GB2312
.
/subsystem=elytron/properties-realm=examplePropRealm:add(groups-attribute=groups,groups-properties={path=example-roles.properties,relative-to=jboss.server.config.dir},users-properties={path=example-users.properties,relative-to=jboss.server.config.dir},hash-encoding=base64,hash-charset=GB2312)
Configure a security-domain:
/subsystem=elytron/security-domain=exampleSD:add(realms=[{realm=examplePropRealm,role-decoder=groups-to-roles}],default-realm=examplePropRealm,permission-mapper=default-permission-mapper)
Configure an http-authentication-factory:
/subsystem=elytron/http-authentication-factory=example-http-auth:add(http-server-mechanism-factory=global,security-domain=exampleSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=exampleApplicationDomain}]}])
This example shows creating an http-authentication-factory using BASIC authentication, but it could be updated to other mechanisms such as FORM.
Configure an application-security-domain in the Undertow subsystem:
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=example-http-auth)
Configure your application’s web.xml and jboss-web.xml.
Your application’s web.xml and jboss-web.xml must be updated to use the application-security-domain you configured in WildFly. An example of this is available in the Configure Applications to Use Elytron for Authentication section.
4.1.2. Configure Authentication with a Filesystem-Based Identity Store
Chose a directory for users:
You need a directory where your users will be stored. In this example, we are using a directory called fs-realm-users located in jboss.server.config.dir.
Configure a filesystem-realm in WildFly:
/subsystem=elytron/filesystem-realm=exampleFsRealm:add(path=fs-realm-users,relative-to=jboss.server.config.dir)
If your directory is located outside of jboss.server.config.dir, then you need to change the path and relative-to values appropriately.
Encryption
To enable encryption for a filesystem-realm
, there are a couple of attributes that can be configured.
-
credential-store
: This attribute specifies thecredential-store
where the secret key used for encrypting and decrypting the realm is stored. -
secret-key
: This attribute specifies the alias within thecredential-store
that contains the secret key to be used to encrypt and decrypt the realm.
As an example, to create a credential-store
that is automatically populated with a SecretKey
under the alias key
, the following command can be used:
/subsystem=elytron/secret-key-credential-store=mycredstore:add(path=propcredstore.cs, relative-to=jboss.server.config.dir, create=true, populate=true)
An encrypted filesystem-realm
that makes use of the credential-store
could be then created as follows:
/subsystem=elytron/filesystem-realm=exampleFsRealm:add(path=fs-realm-users,relative-to=jboss.server.config.dir, credential-store=mycredstore, secret-key=key)
Integrity
It is also possible to enable support for integrity checking on the filesystem realm by configuring the key-store and key-store-alias attributes for the realm.
When enabling integrity checking on a filesystem realm, you will have to specify the following 2 attributes
-
key-store
: This attribute specifies the key-store that contains the key pair (private key and public key) that’s used for signing each identity file and for integrity checking. -
key-store-alias
: This attribute specifies the alias within the key-store that identifies the PrivateKeyEntry to use to sign identity files and perform filesystem integrity checks.
To create a key-store and a key-pair for the attributes above, you can create a key-store resource using the following commands. This will create a key-store called keystore
with the alias localhost
, and the password secret
.
/subsystem=elytron/key-store=keystore:add(path=keystore, relative-to=jboss.server.config.dir, type=PKCS12, credential-reference={clear-text=secret})
/subsystem=elytron/key-store=keystore:generate-key-pair(alias=localhost,algorithm=RSA,key-size=1024,validity=365,distinguished-name="CN=localhost")
/subsystem=elytron/key-store=keystore:store()
To create an integrity enabled filesystem realm with a key-store
called keystore
and the key-store-alias localhost
, the following command would be used
/subsystem=elytron/filesystem-realm=exampleFsRealm:add(path=fs-realm-users,relative-to=jboss.server.config.dir, key-store=keystore, key-store-alias=localhost)
Integrity and Encryption can be used in conjuction together. To create an encrypted filesystem realm with integrity enabled, the following command would be used
/subsystem=elytron/filesystem-realm=exampleFsRealm:add(path=fs-realm-users,relative-to=jboss.server.config.dir, credential-store=mycredstore, secret-key=key, key-store=keystore, key-store-alias=localhost)
Add a user:
When using the filesystem-realm, you can add users using the management CLI.
/subsystem=elytron/filesystem-realm=exampleFsRealm:add-identity(identity=user1)
/subsystem=elytron/filesystem-realm=exampleFsRealm:set-password(clear={password="password123"}, identity=user1)
/subsystem=elytron/filesystem-realm=exampleFsRealm:add-identity-attribute(identity=user1, name=Roles, value=["Admin","Guest"])
Add a simple-role-decoder:
/subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)
This simple-role-decoder decodes a principal’s roles from the Roles attribute. You can change this value if your roles are in a different attribute.
Configure a security-domain:
/subsystem=elytron/security-domain=exampleFsSD:add(realms=[{realm=exampleFsRealm,role-decoder=from-roles-attribute}],default-realm=exampleFsRealm,permission-mapper=default-permission-mapper)
Configure an http-authentication-factory:
/subsystem=elytron/http-authentication-factory=example-fs-http-auth:add(http-server-mechanism-factory=global,security-domain=exampleFsSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=exampleApplicationDomain}]}])
This example shows creating an http-authentication-factory using BASIC authentication, but it could be updated to other mechanisms such as FORM.
Configure an application-security-domain in the Undertow subsystem:
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=example-fs-http-auth)
Configure your application’s web.xml and jboss-web.xml.
Your application’s web.xml and jboss-web.xml must be updated to use the application-security-domain you configured in WildFly. An example of this is available in the Configure Applications to Use Elytron for Authentication section.
Your application is now using a filesystem-based identity store for authentication.
4.1.3. Configure Authentication with a Database Identity Store
Determine your database format for usernames, passwords, and roles:
To set up authentication using a database for an identity store, you need to determine how your usernames, passwords, and roles are stored in that database. In this example, we are using a single table with the following sample data:
username | password | roles |
---|---|---|
user1 |
password123 |
Admin |
user2 |
password123 |
Guest |
Configure a datasource:
To connect to a database from WildFly, you must have the appropriate database driver deployed as well as a datasource configured. This example shows deploying the driver for postgres and configuring a datasource in WildFly:
deploy /path/to/postgresql-9.4.1210.jar
data-source add --name=examplePostgresDS --jndi-name=java:jboss/examplePostgresDS --driver-name=postgresql-9.4.1210.jar --connection-url=jdbc:postgresql://localhost:5432/postgresdb --user-name=postgresAdmin --password=mysecretpassword
Configure a jdbc-realm in WildFly:
/subsystem=elytron/jdbc-realm=exampleDbRealm:add(principal-query=[{sql="SELECT password,roles FROM wildfly_users WHERE username=?",data-source=examplePostgresDS,clear-password-mapper={password-index=1},attribute-mapping=[{index=2,to=groups}]}])
NOTE: The above example shows how to obtain passwords and roles from a single principal-query. You can also create additional principal-query with attribute-mapping attributes if you require multiple queries to obtain roles or additional authentication or authorization information.
Configure a security-domain:
/subsystem=elytron/security-domain=exampleDbSD:add(realms=[{realm=exampleDbRealm,role-decoder=groups-to-roles}],default-realm=exampleDbRealm,permission-mapper=default-permission-mapper)
Configure an http-authentication-factory:
/subsystem=elytron/http-authentication-factory=example-db-http-auth:add(http-server-mechanism-factory=global,security-domain=exampleDbSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=exampleDbSD}]}])
This example shows creating an http-authentication-factory using BASIC authentication, but it could be updated to other mechanisms such as FORM.
Configure an application-security-domain in the Undertow subsystem:
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=example-db-http-auth)
Configure your application’s web.xml and jboss-web.xml.
Your application’s web.xml and jboss-web.xml must be updated to use the application-security-domain you configured in WildFly. An example of this is available in the Configure Applications to Use Elytron for Authentication section.
4.1.4. Configure Authentication with an LDAP-Based Identity Store
Determine your LDAP format for usernames, passwords, and roles:
To set up authentication using an LDAP server for an identity store, you need to determine how your usernames, passwords, and roles are stored. In this example, we are using the following structure:
dn: dc=wildfly,dc=org
dc: wildfly
objectClass: top
objectClass: domain
dn: ou=Users,dc=wildfly,dc=org
objectClass: organizationalUnit
objectClass: top
ou: Users
dn: uid=jsmith,ou=Users,dc=wildfly,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
cn: John Smith
sn: smith
uid: jsmith
userPassword: password123
dn: ou=Roles,dc=wildfly,dc=org
objectclass: top
objectclass: organizationalUnit
ou: Roles
dn: cn=Admin,ou=Roles,dc=wildfly,dc=org
objectClass: top
objectClass: groupOfNames
cn: Admin
member: uid=jsmith,ou=Users,dc=wildfly,dc=org
Configure a dir-context:
To connect to the LDAP server from WildFly, you need to configure a dir-context that provides the URL as well as the principal used to connect to the server.
/subsystem=elytron/dir-context=exampleDC:add(url="ldap://127.0.0.1:10389",principal="uid=admin,ou=system",credential-reference={clear-text="secret"})
Configure an ldap-realm in WildFly:
/subsystem=elytron/ldap-realm=exampleLR:add(dir-context=exampleDC,identity-mapping={search-base-dn="ou=Users,dc=wildfly,dc=org",rdn-identifier="uid",user-password-mapper={from="userPassword"},attribute-mapping=[{filter-base-dn="ou=Roles,dc=wildfly,dc=org",filter="(&(objectClass=groupOfNames)(member={1}))",from="cn",to="Roles"}]})
Additionally, if storing hashed passwords in the LDIF file, you can specify the following attributes:
-
hash-encoding
: This attribute specifies the string format for the password if it is not stored in plain text. It is set tobase64
encoding by default, buthex
is also supported. -
hash-charset
: This attribute specifies the character set to use when converting the password string to a byte array. It is set toUTF-8
by default.
For example, the following LDAP realm is storing hexadecimal encoded strings hashed using the GB2312
character set:
/subsystem=elytron/ldap-realm=exampleLR:add(dir-context=exampleDC,identity-mapping={...},hash-encoding=hex,hash-charset=GB2312)
Add a simple-role-decoder:
/subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)
Configure a security-domain:
/subsystem=elytron/security-domain=exampleLdapSD:add(realms=[{realm=exampleLR,role-decoder=from-roles-attribute}],default-realm=exampleLR,permission-mapper=default-permission-mapper)
Configure an http-authentication-factory:
/subsystem=elytron/http-authentication-factory=example-ldap-http-auth:add(http-server-mechanism-factory=global,security-domain=exampleLdapSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=exampleApplicationDomain}]}])
This example shows creating an http-authentication-factory using BASIC authentication, but it could be updated to other mechanisms such as FORM.
Configure an application-security-domain in the Undertow subsystem:
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=example-ldap-http-auth)
Configure your application’s web.xml and jboss-web.xml.
Your application’s web.xml and jboss-web.xml must be updated to use the application-security-domain you configured in WildFly. An example of this is available in the Configure Applications to Use Elytron for Authentication section.
IMPORTANT: In cases where you configure an LDAP server in the elytron subsystem for authentication and that LDAP server then becomes unreachable, WildFly will return a 500, or internal server error, error code when attempting authentication using that unreachable LDAP server. This behavior differs from the legacy security subsystem, which will return a 401, or unauthorized, error code under the same conditions.
4.1.5. Configure Authentication with Certificates
IMPORTANT: Before you can set up certificate-based authentication, you must have two-way SSL configured.
Configure a key-store-realm.
/subsystem=elytron/key-store-realm=ksRealm:add(key-store=twoWayTS)
You must configure this realm with a truststore that contains the client’s certificate. The authentication process uses the same certificate presented by the client during the two-way SSL handshake.
Create a Decoder.
You need to create a x500-attribute-principal-decoder to decode the principal you get from your certificate. The below example will decode the principal based on the first CN value.
/subsystem=elytron/x500-attribute-principal-decoder=CNDecoder:add(oid="2.5.4.3",maximum-segments=1)
For example, if the full DN was CN=client,CN=client-certificate,DC=example,DC=jboss,DC=org, CNDecoder would decode the principal as client. This decoded principal is used as the alias value to lookup a certificate in the truststore configured in ksRealm.
IMPORTANT: The decoded principal * MUST* must be the alias value you set in your server’s truststore for the client’s certificate.
Configure an evidence-decoder
By default, the principal associated with a certificate will be the subject name from the certificate. This subject name will then be rewritten using any configured principal decoders and principal transformers to obtain the name that should be used when locating and loading the identity from the underlying identity store.
To specify that a subject alternative name from a certificate should be used as the
principal associated with that certificate, an x509-subject-alt-name-evidence-decoder
needs to be configured. This element has two attributes:
-
alt-name-type
- The subject alternative name type to decode. This attribute is required. Must be one of the subject alternative name types that can be represented as aString
:-
rfc822Name
-
dNSName
-
uniformResourceIdentifier
-
iPAddress
-
registeredID
-
directoryName
-
-
segment
- The 0-based occurrence of the subject alternative name to map. This attribute is optional and only used when there is more than one subject alternative name of the givenalt-name-type
. The default value is0
.
For example, consider the following X.509 v3 Subject Alternative Name extension from the first certificate in an X.509 certificate chain:
X509v3 Subject Alternative Name:
DNS:one.example.org, IP Address:127.0.0.1
To associate the certificate chain evidence with the principal "one.example.org", the
following x509-subject-alt-name-evidence-decoder
could be configured:
/subsystem=elytron/x509-subject-alt-name-evidence-decoder=exampleDnsDecoder:add(alt-name-type=dNSName)
Next, consider the following X.509 v3 Subject Alternative Name extension from the first certificate in an X.509 certificate chain:
X509v3 Subject Alternative Name:
DNS:one.example.org, DNS:two.example.org, IP Address:127.0.0.1
To associate the evidence with the principal "two.example.org", the following
x509-subject-alt-name-evidence-decoder
could be configured:
/subsystem=elytron/x509-subject-alt-name-evidence-decoder=anotherDnsDecoder:add(alt-name-type=dNSName, segment=1)
It is also possible to configure an x500-subject-evidence-decoder
. This evidence decoder
will extract the subject from the first certificate in the certificate chain, as an X500Principal
.
Example configuration:
/subsystem=elytron/x500-subject-evidence-decoder=exampleSubjectDecoder:add()
To make use of a custom org.wildfly.security.auth.server.EvidenceDecoder
implementation, a
custom-evidence-decoder
can be configured. The custom implementation class needs to first be
packaged in a JAR. A module can then be added to WildFly that contains this JAR. See
Custom Components for more information on how to add a custom Elytron
component to WildFly.
Example configuration:
/subsystem=elytron/custom-evidence-decoder=myCustomEvidenceDecoder:add(module=org.wildfly.security.examples, class-name=org.wildfly.security.examples.MyCustomEvidenceDecoder)
Finally, it is also possible to configure an aggregate-evidence-decoder
. This consists of
two or more evidence-decoder
elements where each element is reference to a configured
evidence decoder. Given evidence, these evidence decoders will be attempted in order until
one returns a non-null principal or until there are no more evidence decoders left to try.
Example configuration:
/subsystem=elytron/x509-subject-alt-name-evidence-decoder=emailDecoder:add(alt-name-type=rfc822Name)
/subsystem=elytron/x509-subject-alt-name-evidence-decoder=dnsDecoder:add(alt-name-type=dNSName)
/subsystem=elytron/x500-subject-evidence-decoder=subjectDecoder:add()
/subsystem=elytron/aggregate-evidence-decoder=aggregateDecoder:add(evidence-decoders=[emailDecoder,subjectDecoder,dnsDecoder])
Once an evidence-decoder
has been configured, it can be referenced from a security-domain
:
/subsystem=elytron/security-domain=exampleCertSD:add(..., evidence-decoder=aggregateDecoder)
If no evidence-decoder
is specified for a security-domain
, then the principal associated with the evidence will
just be the default principal for the evidence type, i.e., for an X.509 certificate chain, this would continue to
default to the subject from the first certificate in the certificate chain.
Add a constant-role-mapper for assigning roles.
This is example uses a constant-role-mapper to assign roles to a principal from ksRealm but other approaches may also be used.
/subsystem=elytron/constant-role-mapper=constantClientCertRole:add(roles=[Admin,Guest])
Configure a security-domain.
/subsystem=elytron/security-domain=exampleCertSD:add(realms=[{realm=ksRealm}],default-realm=ksRealm,permission-mapper=default-permission-mapper,principal-decoder=CNDecoder,role-mapper=constantClientCertRole)
Configure an http-authentication-factory.
/subsystem=elytron/http-authentication-factory=exampleCertHttpAuth:add(http-server-mechanism-factory=global,security-domain=exampleCertSD,mechanism-configurations=[{mechanism-name=CLIENT_CERT}])
Configure an application-security-domain in the Undertow subsystem.
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=exampleCertHttpAuth)
Update server-ssl-context.
/subsystem=elytron/server-ssl-context=twoWaySSC:write-attribute(name=security-domain,value=exampleCertSD)
/subsystem=elytron/server-ssl-context=twoWaySSC:write-attribute(name=authentication-optional, value=true)
Configure your application’s web.xml and jboss-web.xml.
Your application’s web.xml and jboss-web.xml must be updated to use the application-security-domain you configured in WildFly. An example of this is available in the Configure Applications to Use Elytron for Authentication section.
In addition, you need to update your web.xml to use CLIENT-CERT as its authentication method.
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>exampleApplicationDomain</realm-name>
</login-config>
4.1.6. Configure Authentication with a Kerberos-Based Identity Store
IMPORTANT: The following steps assume you have a working KDC and Kerberos domain as well as your client browsers configured.
Configure a kerberos-security-factory.
/subsystem=elytron/kerberos-security-factory=krbSF:add(principal="HTTP/host@REALM",path="/path/to/http.keytab",mechanism-oids=[1.2.840.113554.1.2.2,1.3.6.1.5.5.2])
Configure the system properties for Kerberos.
Depending on how your environment is configured, you will need to set some of the system properties below.
System Property | Description |
---|---|
java.security.krb5.kdc |
The host name of the KDC. |
java.security.krb5.realm |
The name of the realm. |
java.security.krb5.conf |
The path to the configuration krb5.conf file. |
sun.security.krb5.debug |
If true, debugging mode will be enabled. |
To configure a system property in WildFly:
/system-property=java.security.krb5.conf:add(value="/path/to/krb5.conf")
Configure an Elytron security realm for assigning roles.
The the client’s Kerberos token will provide the principal, but you need a way to map that principal to a role for your application. There are several ways to accomplish this, but this example creates a filesystem-realm, adds a user to the realm that matches the principal from the Kerberos token, and assigns roles to that user.
/subsystem=elytron/filesystem-realm=exampleFsRealm:add(path=fs-realm-users,relative-to=jboss.server.config.dir)
/subsystem=elytron/filesystem-realm=exampleFsRealm:add-identity(identity=user1@REALM)
/subsystem=elytron/filesystem-realm=exampleFsRealm:add-identity-attribute(identity=user1@REALM,name=Roles,value=["Admin","Guest"])
Add a simple-role-decoder.
/subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)
This simple-role-decoder decodes a principal’s roles from the Roles attribute. You can change this value if your roles are in a different attribute.
Configure a security-domain.
/subsystem=elytron/security-domain=exampleFsSD:add(realms=[{realm=exampleFsRealm,role-decoder=from-roles-attribute}],default-realm=exampleFsRealm,permission-mapper=default-permission-mapper)
Configure an http-authentication-factory that uses the
kerberos-security-factory.
/subsystem=elytron/http-authentication-factory=example-krb-http-auth:add(http-server-mechanism-factory=global,security-domain=exampleFsSD,mechanism-configurations=[{mechanism-name=SPNEGO,mechanism-realm-configurations=[{realm-name=exampleFsSD}],credential-security-factory=krbSF}])
Configure an application-security-domain in the Undertow subsystem:
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=example-krb-http-auth)
Configure your application’s web.xml, jboss-web.xml and
jboss-deployment-structure.xml.
Your application’s web.xml and jboss-web.xml must be updated to use the application-security-domain you configured in WildFly. An example of this is available in the Configure Applications to Use Elytron for Authentication section.
In addition, you need to update your web.xml to use SPNEGO as its authentication method.
<login-config>
<auth-method>SPNEGO</auth-method>
<realm-name>exampleApplicationDomain</realm-name>
</login-config>
4.1.7. Configure Authentication with a Form as a Fallback for Kerberos
Configure kerberos-based authentication.
Configuring kerberos-based authentication is covered in a previous section.
Add a mechanism for FORM authentication in the
http-authentication-factory.
You can use the existing http-authentication-factory you configured for kerberos-based authentication and and an additional mechanism for FORM authentication.
/subsystem=elytron/http-authentication-factory=example-krb-http-auth:list-add(name=mechanism-configurations, value={mechanism-name=FORM})
Add additional fallback principals.
The existing configuration for kerberos-based authentication should already have a security realm configured for mapping principals from kerberos token to roles for the application. You can add additional users for fallback authentication to that realm. For example if you used a filesystem-realm, you can simply create a new user with the appropriate roles:
/subsystem=elytron/filesystem-realm=exampleFsRealm:add-identity(identity=fallbackUser1)
/subsystem=elytron/filesystem-realm=exampleFsRealm:set-password(identity=fallbackUser1,clear={password="password123"})
/subsystem=elytron/filesystem-realm=exampleFsRealm:add-identity-attribute(identity=fallbackUser1,name=Roles,value=["Admin","Guest"])
Update the web.xml for FORM fallback.
You need to update the web.xml to use the value SPNEGO,FORM for the auth-method, which will use FORM as a fallback authentication method if SPNEGO fails. You also need to specify the location of your login and error pages.
<login-config>
<auth-method>SPNEGO,FORM</auth-method>
<realm-name>exampleApplicationDomain</realm-name>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>
4.1.8. Configure Applications to Use Elytron for Authentication
After you have configured the elytron subsystem for authentication, you need to configure your application to use it.
Configure your application’s web.xml.
Your application’s web.xml needs to be configured to use the appropriate authentication method. When using elytron, this is defined in the http-authentication-factory you created.
Example web.xml with BASIC Authentication
<web-app>
<security-constraint>
<web-resource-collection>
<web-resource-name>secure</web-resource-name>
<url-pattern>/secure/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>Admin</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<description>The role that is required to log in to /secure/*</description>
<role-name>Admin</role-name>
</security-role>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>exampleApplicationDomain</realm-name>
</login-config>
</web-app>
BASIC Authentication can be configured to be silent
<auth-method>BASIC?silent=true</auth-method>
Basic authentication in silent mode will send challenge to authenticate only if the request contained authorization header, otherwise it is assumed another method will send the challenge.
Configure your application to use a security domain.
You can configure your application’s jboss-web.xml to specify the security domain you want to use for authentication. When using the elytron subsystem, this is defined when you created the application-security-domain.
Example jboss-web.xml
<jboss-web>
<security-domain>exampleApplicationDomain</security-domain>
</jboss-web>
Using jboss-web.xml allows you to configure the security domain for a single application only. Alternatively, you can specify a default security domain for all applications using the undertow subsystem. This allows you to omit using jboss-web.xml to configure a security domain for an individual application.
/subsystem=undertow:write-attribute(name=default-security-domain, value="exampleApplicationDomain")
IMPORTANT: Setting default-security-domain in the undertow subsystem will apply to ALL applications. If default-security-domain is set and an application specifies a security domain in a jboss-web.xml file, the configuration in jboss-web.xml will override the default-security-domain in the undertow subsystem.
4.1.9. Override an Application’s Authentication Configuration
You can override the authentication configuration of an application with one configured in WildFly. To do this, use the override-deployment-configuration property in the application-security-domain section of the undertow subsystem:
/subsystem=undertow/application-security-domain=exampleApplicationDomain:write-attribute(name=override-deployment-config,value=true)
For example, an application is configured to use FORM authentication with the exampleApplicationDomain in its jboss-web.xml.
Example jboss-web.xml
<login-config>
<auth-method>FORM</auth-method>
<realm-name>exampleApplicationDomain</realm-name>
</login-config>
By enabling override-deployment-configuration, you can create a new http-authentication-factory that specifies a different authentication mechanism such as BASIC.
Example http-authentication-factory
/subsystem=elytron/http-authentication-factory=exampleHttpAuth:read-resource()
{
"outcome" => "success",
"result" => {
"http-server-mechanism-factory" => "global",
"mechanism-configurations" => [{
"mechanism-name" => "BASIC",
"mechanism-realm-configurations" => [{"realm-name" => "exampleApplicationDomain"}]
}],
"security-domain" => "exampleSD"
}
}
This will override the authentication mechanism defined in the application’s jboss-web.xml and attempt to authenticate a user using BASIC instead of FORM.
4.1.10. Create and Use a Credential Store
Create credential store.
/subsystem=elytron/credential-store=exampleCS:add(relative-to=jboss.server.data.dir, location=example.jceks,create=true,credential-reference={clear-text=cs-secret})
Add a credential to a credential store.
/subsystem=elytron/credential-store=exampleCS:add-alias(alias=keystorepw,secret-value=secret)
List all credentials in a credential store.
/subsystem=elytron/credential-store=exampleCS:read-aliases()
{
"outcome" => "success",
"result" => ["keystorepw"]
}
4.2. Set up and Configure Authentication for the Management Interfaces
4.2.1. Secure the Management Interfaces with a New Identity Store
Create a security domain and any supporting security realms,
decoders, or mappers for your identity store.
This process is covered in a previous section. For example, if you wanted to secure the management interfaces using a filesystem-based identity store, you would follow the steps in Configure Authentication with a Filesystem-Based Identity Store.
Create an http-authentication-factory or
sasl-authentication-factory.
Example http-authentication-factory
/subsystem=elytron/http-authentication-factory=example-http-auth:add(http-server-mechanism-factory=global,security-domain=exampleSD,mechanism-configurations=[{mechanism-name=DIGEST,mechanism-realm-configurations=[{realm-name=exampleManagementRealm}]}])
Example sasl-authentication-factory
/subsystem=elytron/sasl-authentication-factory=example-sasl-auth:add(sasl-server-factory=configured,security-domain=exampleSD,mechanism-configurations=[{mechanism-name=DIGEST-MD5,mechanism-realm-configurations=[{realm-name=exampleManagementRealm}]}])
Update the management interfaces to use your
http-authentication-factory or sasl-authentication-factory.
Example update http-authentication-factory
/core-service=management/management-interface=http-interface:write-attribute(name=http-authentication-factory, value=example-http-auth)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
reload
Example update sasl-authentication-factory
/core-service=management/management-interface=http-interface:write-attribute(name=http-upgrade.sasl-authentication-factory, value=example-sasl-auth)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
reload
4.2.2. Silent Authentication
By default, WildFly provides an authentication mechanism for local users, also know as silent authentication, through the local security realm.
Silent authentication must be used via a sasl-authentication-factory.
IMPORTANT: When enabling silent authentication, you must ensure the security domain referenced by your sasl-authentication-factory references a security realm that contains the $local user. By default, WildFly provides the local identity realm that provides this user.
Add silent authentication to an existing
sasl-authentication-factory.
/subsystem=elytron/sasl-authentication-factory=example-sasl-auth:list-add(name=mechanism-configurations, value={mechanism-name=JBOSS-LOCAL-USER, realm-mapper=local})
reload
Create a new sasl-server-factory with silent authentication.
/subsystem=elytron/sasl-authentication-factory=example-sasl-auth:add(sasl-server-factory=configured,security-domain=exampleSD,mechanism-configurations=[{mechanism-name=DIGEST-MD5,mechanism-realm-configurations=[{realm-name=exampleManagementRealm}]},{mechanism-name=JBOSS-LOCAL-USER, realm-mapper=local}])
reload
Remove silent authentication from an existing sasl-server-factory:
/subsystem=elytron/sasl-authentication-factory=managenet-sasl-authentication:read-resource
{
"outcome" => "success",
"result" => {
"mechanism-configurations" => [
{
"mechanism-name" => "JBOSS-LOCAL-USER",
"realm-mapper" => "local"
},
{
"mechanism-name" => "DIGEST-MD5",
"mechanism-realm-configurations" => [{"realm-name" => "ManagementRealm"}]
}
],
"sasl-server-factory" => "configured",
"security-domain" => "ManagementDomain"
}
}
/subsystem=elytron/sasl-authentication-factory=temp-sasl-authentication:list-remove(name=mechanism-configurations,index=0)
reload
4.2.3. Using RBAC with Elytron
RBAC can be configured to automatically assign or exclude roles for users that are members of groups. This is configured in the access-control section of the core management. When the management interfaces are secured with the elytron subsystem, and users are assigned groups when they authenticate. You can also configure roles to be assigned to authenticated users in a variety of ways using the elytron subsystem, for example using a role mapper or a role decoder.
4.3. Configure SSL/TLS
4.3.1. Enable One-way SSL/TLS for Applications
There are a couple ways to enable one-way SSL/TLS for deployed applications.
Using a security command:
The security enable-ssl-http-server command can be used to enable one-way SSL/TLS for deployed applications. Example of wizard usage:
security enable-ssl-http-server --interactive --override-ssl-context
Please provide required pieces of information to enable SSL:
Key-store file name (default default-server.keystore): keystore.pkcs12
Password (blank generated): secret
What is your first and last name? [Unknown]: localhost
What is the name of your organizational unit? [Unknown]:
What is the name of your organization? [Unknown]:
What is the name of your City or Locality? [Unknown]:
What is the name of your State or Province? [Unknown]:
What is the two-letter country code for this unit? [Unknown]:
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct y/n [y]?
Validity (in days, blank default): 365
Alias (blank generated): localhost
Enable SSL Mutual Authentication y/n (blank n): n
SSL options:
key store file: keystore.pkcs12
distinguished name: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
password: secret
validity: 365
alias: localhost
Server keystore file keystore.pkcs12, certificate file keystore.pem and keystore.csr file
will be generated in server configuration directory.
Do you confirm y/n: y
NB: Once the command is executed, the CLI will reload the server.
HTTPS is now enabled for applications.
Using Elytron subsystem commands:
You can also use the Elytron subsystem, along with the Undertow subsystem, to enable HTTPS for deployed applications.
Configure a key-store in WildFly:
/subsystem=elytron/key-store=httpsKS:add(path=/path/to/keystore.pkcs12,credential-reference={clear-text=secret},type=PKCS12)
The previous command uses an absolute path to the keystore. Alternatively you can use the relative-to attribute to specify the base directory variable and path specify a relative path. Also, in case of file-based keystore the type attribute can be omitted and the keystore type will be automatically detected.
/subsystem=elytron/key-store=httpsKS:add(path=keystore.pkcs12,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=PKCS12)
If the keystore file does not exist yet, the following commands can be used to generate an example key pair:
/subsystem=elytron/key-store=httpsKS:generate-key-pair(alias=localhost,algorithm=RSA,key-size=2048,validity=365,credential-reference={clear-text=secret},distinguished-name="CN=localhost")
/subsystem=elytron/key-store=httpsKS:store()
List all aliases (recursively) in a key store.
/subsystem=elytron/key-store=httpsKS:read-aliases(recursive=true,verbose=true)
{
"outcome" => "success",
"result" => {"localhost" => {
"alias" => "localhost",
"entry-type" => "PrivateKeyEntry",
"creation-date" => "2022-12-28T15:58:04.818+0530",
"certificate-chain" => [{
"type" => "X.509",
"algorithm" => "RSA",
"format" => "X.509",
"public-key" => "30:82:01:22:30:0d:06:09:2a:86:48:86:f7:0d:01:01:01:
05:00:03:82:01:0f:00:30:82:01:0a:02:82:01:01:00:9e:b2:6f:4e:92:e9:f3:69:eb:9c:0e
:63:1e:d6:d4:4b:2b:5d:46:e2:68:fd:96:83:a1:3b:83:a2:70:a8:6e:e1:2e:8d:e9:8d:ed:9
d:d3:f3:56:26:00:29:32:a0:95:61:54:6f:35:95:41:95:76:73:34:46:db:4b:75:3c:f1:64:
91:4a:a6:61:89:80:8c:6b:87:75:8d:d1:d1:56:bb:5e:1b:ed:97:5a:b4:ac:1e:16:06:07:e5
:cb:6c:a8:c5:66:16:e2:92:6a:db:a6:b0:09:d2:7d:dc:fa:68:f3:2c:c5:c2:a6:f2:bd:73:f
5:3b:94:17:16:2f:d0:8a:e6:d0:f3:53:c4:71:c6:31:61:9b:87:9d:5e:63:d8:c3:e3:81:f8:
6f:f6:35:7b:85:9c:81:c9:94:91:d3:5d:bc:89:3b:dc:7d:c2:ae:d2:ca:b4:61:6f:04:5b:4f
:8f:c6:ac:e0:49:de:5f:bf:84:f5:38:b1:07:48:1f:17:fb:72:15:ac:31:7a:13:f8:58:aa:6
7:b0:67:b1:2b:99:c0:7a:a7:f6:42:d3:f9:f5:33:90:2a:9c:7c:78:73:7f:74:54:91:04:c2:
51:ff:59:11:cd:7d:d7:61:e9:5c:c6:c7:d1:a1:f2:f5:7b:41:94:c4:cd:8d:5e:06:f1:6a:f4
:72:24:37:be:27:02:03:01:00:01",
"sha-1-digest" => "70:46:1d:af:1a:5e:30:34:e9:dd:53:f4:38:b6:3e:89:5
a:1c:f9:90",
"sha-256-digest" => "61:43:2e:6f:c0:ed:91:b1:3a:20:51:fc:2b:e9:33:d2
:66:d2:a6:f2:32:5c:ca:91:14:67:7b:f2:4e:55:ed:77",
"encoded" => "30:82:02:cb:30:82:01:b5:a0:03:02:01:02:02:08:0c:e1:64:
83:a0:95:28:72:30:0b:06:09:2a:86:48:86:f7:0d:01:01:0b:30:14:31:12:30:10:06:03:55
:04:03:13:09:6c:6f:63:61:6c:68:6f:73:74:30:22:18:0f:32:30:32:32:31:32:32:38:31:3
0:32:38:30:34:5a:18:0f:32:30:32:33:31:32:32:38:31:30:32:38:30:34:5a:30:14:31:12:
30:10:06:03:55:04:03:13:09:6c:6f:63:61:6c:68:6f:73:74:30:82:01:22:30:0d:06:09:2a
:86:48:86:f7:0d:01:01:01:05:00:03:82:01:0f:00:30:82:01:0a:02:82:01:01:00:9e:b2:6
f:4e:92:e9:f3:69:eb:9c:0e:63:1e:d6:d4:4b:2b:5d:46:e2:68:fd:96:83:a1:3b:83:a2:70:
a8:6e:e1:2e:8d:e9:8d:ed:9d:d3:f3:56:26:00:29:32:a0:95:61:54:6f:35:95:41:95:76:73
:34:46:db:4b:75:3c:f1:64:91:4a:a6:61:89:80:8c:6b:87:75:8d:d1:d1:56:bb:5e:1b:ed:9
7:5a:b4:ac:1e:16:06:07:e5:cb:6c:a8:c5:66:16:e2:92:6a:db:a6:b0:09:d2:7d:dc:fa:68:
f3:2c:c5:c2:a6:f2:bd:73:f5:3b:94:17:16:2f:d0:8a:e6:d0:f3:53:c4:71:c6:31:61:9b:87
:9d:5e:63:d8:c3:e3:81:f8:6f:f6:35:7b:85:9c:81:c9:94:91:d3:5d:bc:89:3b:dc:7d:c2:a
e:d2:ca:b4:61:6f:04:5b:4f:8f:c6:ac:e0:49:de:5f:bf:84:f5:38:b1:07:48:1f:17:fb:72:
15:ac:31:7a:13:f8:58:aa:67:b0:67:b1:2b:99:c0:7a:a7:f6:42:d3:f9:f5:33:90:2a:9c:7c
:78:73:7f:74:54:91:04:c2:51:ff:59:11:cd:7d:d7:61:e9:5c:c6:c7:d1:a1:f2:f5:7b:41:9
4:c4:cd:8d:5e:06:f1:6a:f4:72:24:37:be:27:02:03:01:00:01:a3:21:30:1f:30:1d:06:03:
55:1d:0e:04:16:04:14:ac:99:db:c3:82:18:60:51:92:7d:75:51:ba:b4:a9:65:90:0e:7d:13
:30:0b:06:09:2a:86:48:86:f7:0d:01:01:0b:03:82:01:01:00:97:83:30:22:ed:2e:4e:55:3
3:73:4c:e2:56:d2:37:0a:47:39:7c:01:f9:97:6c:f6:98:f7:33:5b:b2:57:ba:d9:ff:7d:4c:
f1:92:42:64:a5:ee:56:ca:f1:da:ae:64:51:a0:90:5a:d7:a0:eb:e8:7b:e0:ef:6d:a1:8f:d0
:f6:80:c8:05:1b:29:c0:66:14:4d:bb:d9:87:e1:8e:d5:95:3c:c8:0f:fe:49:74:bd:28:a5:4
6:50:9c:e3:d5:6b:0b:48:b4:7f:1b:ad:95:8b:52:ee:5a:e1:03:43:47:6e:6e:ac:c2:5a:da:
63:3c:a4:8a:c5:f8:d1:6e:ae:eb:09:97:8b:0c:cd:37:92:77:0c:05:6b:a1:99:8f:7b:b8:6a
:30:cc:5b:c0:fc:70:f2:2b:50:73:22:d2:aa:80:b6:b0:c2:67:37:25:91:10:80:3a:ed:8c:6
b:04:02:b5:49:0c:7f:a1:a0:ef:3a:66:07:a5:9d:ab:09:be:21:8e:73:f3:91:19:3e:59:75:
cf:0f:85:3a:c8:18:89:22:68:04:95:81:9a:7d:67:19:28:11:bc:6b:e8:cb:c7:7b:9c:b4:64
:59:63:50:88:f4:05:9e:e6:fa:01:17:2b:eb:75:da:8b:34:f2:1f:47:2d:c5:79:8f:76:59:6
9:d9:89:0b:51:99:00:35",
"subject" => "CN=localhost",
"issuer" => "CN=localhost",
"not-before" => "2022-12-28T15:58:04.000+0530",
"not-after" => "2023-12-28T15:58:04.000+0530",
"serial-number" => "0c:e1:64:83:a0:95:28:72",
"signature-algorithm" => "SHA256withRSA",
"signature" => "97:83:30:22:ed:2e:4e:55:33:73:4c:e2:56:d2:37:0a:47:3
9:7c:01:f9:97:6c:f6:98:f7:33:5b:b2:57:ba:d9:ff:7d:4c:f1:92:42:64:a5:ee:56:ca:f1:
da:ae:64:51:a0:90:5a:d7:a0:eb:e8:7b:e0:ef:6d:a1:8f:d0:f6:80:c8:05:1b:29:c0:66:14
:4d:bb:d9:87:e1:8e:d5:95:3c:c8:0f:fe:49:74:bd:28:a5:46:50:9c:e3:d5:6b:0b:48:b4:7
f:1b:ad:95:8b:52:ee:5a:e1:03:43:47:6e:6e:ac:c2:5a:da:63:3c:a4:8a:c5:f8:d1:6e:ae:
eb:09:97:8b:0c:cd:37:92:77:0c:05:6b:a1:99:8f:7b:b8:6a:30:cc:5b:c0:fc:70:f2:2b:50
:73:22:d2:aa:80:b6:b0:c2:67:37:25:91:10:80:3a:ed:8c:6b:04:02:b5:49:0c:7f:a1:a0:e
f:3a:66:07:a5:9d:ab:09:be:21:8e:73:f3:91:19:3e:59:75:cf:0f:85:3a:c8:18:89:22:68:
04:95:81:9a:7d:67:19:28:11:bc:6b:e8:cb:c7:7b:9c:b4:64:59:63:50:88:f4:05:9e:e6:fa
:01:17:2b:eb:75:da:8b:34:f2:1f:47:2d:c5:79:8f:76:59:69:d9:89:0b:51:99:00:35",
"version" => "v3"
}]
}}
}
List all aliases (recursively, non-verbose) in a key store.
/subsystem=elytron/key-store=httpsKS:read-aliases(recursive=true, verbose=false)
{
"outcome" => "success",
"result" => {"localhost" => {
"alias" => "localhost",
"entry-type" => "PrivateKeyEntry",
"creation-date" => "2022-12-28T15:58:04.818+0530",
"certificate-chain" => [{
"type" => "X.509",
"algorithm" => "RSA",
"format" => "X.509",
"sha-1-digest" => "70:46:1d:af:1a:5e:30:34:e9:dd:53:f4:38:b6:3e:89:5
a:1c:f9:90",
"sha-256-digest" => "61:43:2e:6f:c0:ed:91:b1:3a:20:51:fc:2b:e9:33:d2
:66:d2:a6:f2:32:5c:ca:91:14:67:7b:f2:4e:55:ed:77",
"subject" => "CN=localhost",
"issuer" => "CN=localhost",
"not-before" => "2022-12-28T15:58:04.000+0530",
"not-after" => "2023-12-28T15:58:04.000+0530",
"serial-number" => "0c:e1:64:83:a0:95:28:72",
"signature-algorithm" => "SHA256withRSA",
"signature" => "97:83:30:22:ed:2e:4e:55:33:73:4c:e2:56:d2:37:0a:47:3
9:7c:01:f9:97:6c:f6:98:f7:33:5b:b2:57:ba:d9:ff:7d:4c:f1:92:42:64:a5:ee:56:ca:f1:
da:ae:64:51:a0:90:5a:d7:a0:eb:e8:7b:e0:ef:6d:a1:8f:d0:f6:80:c8:05:1b:29:c0:66:14
:4d:bb:d9:87:e1:8e:d5:95:3c:c8:0f:fe:49:74:bd:28:a5:46:50:9c:e3:d5:6b:0b:48:b4:7
f:1b:ad:95:8b:52:ee:5a:e1:03:43:47:6e:6e:ac:c2:5a:da:63:3c:a4:8a:c5:f8:d1:6e:ae:
eb:09:97:8b:0c:cd:37:92:77:0c:05:6b:a1:99:8f:7b:b8:6a:30:cc:5b:c0:fc:70:f2:2b:50
:73:22:d2:aa:80:b6:b0:c2:67:37:25:91:10:80:3a:ed:8c:6b:04:02:b5:49:0c:7f:a1:a0:e
f:3a:66:07:a5:9d:ab:09:be:21:8e:73:f3:91:19:3e:59:75:cf:0f:85:3a:c8:18:89:22:68:
04:95:81:9a:7d:67:19:28:11:bc:6b:e8:cb:c7:7b:9c:b4:64:59:63:50:88:f4:05:9e:e6:fa
:01:17:2b:eb:75:da:8b:34:f2:1f:47:2d:c5:79:8f:76:59:69:d9:89:0b:51:99:00:35",
"version" => "v3"
}]
}}
}
Read an alias in a key store.
/subsystem=elytron/key-store=httpsKS:read-alias(alias="localhost")
{
"outcome" => "success",
"result" => {
"alias" => "localhost",
"entry-type" => "PrivateKeyEntry",
"creation-date" => "2022-12-28T15:58:04.818+0530",
"certificate-chain" => [{
"type" => "X.509",
"algorithm" => "RSA",
"format" => "X.509",
"public-key" => "30:82:01:22:30:0d:06:09:2a:86:48:86:f7:0d:01:01:01:
05:00:03:82:01:0f:00:30:82:01:0a:02:82:01:01:00:9e:b2:6f:4e:92:e9:f3:69:eb:9c:0e
:63:1e:d6:d4:4b:2b:5d:46:e2:68:fd:96:83:a1:3b:83:a2:70:a8:6e:e1:2e:8d:e9:8d:ed:9
d:d3:f3:56:26:00:29:32:a0:95:61:54:6f:35:95:41:95:76:73:34:46:db:4b:75:3c:f1:64:
91:4a:a6:61:89:80:8c:6b:87:75:8d:d1:d1:56:bb:5e:1b:ed:97:5a:b4:ac:1e:16:06:07:e5
:cb:6c:a8:c5:66:16:e2:92:6a:db:a6:b0:09:d2:7d:dc:fa:68:f3:2c:c5:c2:a6:f2:bd:73:f
5:3b:94:17:16:2f:d0:8a:e6:d0:f3:53:c4:71:c6:31:61:9b:87:9d:5e:63:d8:c3:e3:81:f8:
6f:f6:35:7b:85:9c:81:c9:94:91:d3:5d:bc:89:3b:dc:7d:c2:ae:d2:ca:b4:61:6f:04:5b:4f
:8f:c6:ac:e0:49:de:5f:bf:84:f5:38:b1:07:48:1f:17:fb:72:15:ac:31:7a:13:f8:58:aa:6
7:b0:67:b1:2b:99:c0:7a:a7:f6:42:d3:f9:f5:33:90:2a:9c:7c:78:73:7f:74:54:91:04:c2:
51:ff:59:11:cd:7d:d7:61:e9:5c:c6:c7:d1:a1:f2:f5:7b:41:94:c4:cd:8d:5e:06:f1:6a:f4
:72:24:37:be:27:02:03:01:00:01",
"sha-1-digest" => "70:46:1d:af:1a:5e:30:34:e9:dd:53:f4:38:b6:3e:89:5
a:1c:f9:90",
"sha-256-digest" => "61:43:2e:6f:c0:ed:91:b1:3a:20:51:fc:2b:e9:33:d2
:66:d2:a6:f2:32:5c:ca:91:14:67:7b:f2:4e:55:ed:77",
"encoded" => "30:82:02:cb:30:82:01:b5:a0:03:02:01:02:02:08:0c:e1:64:
83:a0:95:28:72:30:0b:06:09:2a:86:48:86:f7:0d:01:01:0b:30:14:31:12:30:10:06:03:55
:04:03:13:09:6c:6f:63:61:6c:68:6f:73:74:30:22:18:0f:32:30:32:32:31:32:32:38:31:3
0:32:38:30:34:5a:18:0f:32:30:32:33:31:32:32:38:31:30:32:38:30:34:5a:30:14:31:12:
30:10:06:03:55:04:03:13:09:6c:6f:63:61:6c:68:6f:73:74:30:82:01:22:30:0d:06:09:2a
:86:48:86:f7:0d:01:01:01:05:00:03:82:01:0f:00:30:82:01:0a:02:82:01:01:00:9e:b2:6
f:4e:92:e9:f3:69:eb:9c:0e:63:1e:d6:d4:4b:2b:5d:46:e2:68:fd:96:83:a1:3b:83:a2:70:
a8:6e:e1:2e:8d:e9:8d:ed:9d:d3:f3:56:26:00:29:32:a0:95:61:54:6f:35:95:41:95:76:73
:34:46:db:4b:75:3c:f1:64:91:4a:a6:61:89:80:8c:6b:87:75:8d:d1:d1:56:bb:5e:1b:ed:9
7:5a:b4:ac:1e:16:06:07:e5:cb:6c:a8:c5:66:16:e2:92:6a:db:a6:b0:09:d2:7d:dc:fa:68:
f3:2c:c5:c2:a6:f2:bd:73:f5:3b:94:17:16:2f:d0:8a:e6:d0:f3:53:c4:71:c6:31:61:9b:87
:9d:5e:63:d8:c3:e3:81:f8:6f:f6:35:7b:85:9c:81:c9:94:91:d3:5d:bc:89:3b:dc:7d:c2:a
e:d2:ca:b4:61:6f:04:5b:4f:8f:c6:ac:e0:49:de:5f:bf:84:f5:38:b1:07:48:1f:17:fb:72:
15:ac:31:7a:13:f8:58:aa:67:b0:67:b1:2b:99:c0:7a:a7:f6:42:d3:f9:f5:33:90:2a:9c:7c
:78:73:7f:74:54:91:04:c2:51:ff:59:11:cd:7d:d7:61:e9:5c:c6:c7:d1:a1:f2:f5:7b:41:9
4:c4:cd:8d:5e:06:f1:6a:f4:72:24:37:be:27:02:03:01:00:01:a3:21:30:1f:30:1d:06:03:
55:1d:0e:04:16:04:14:ac:99:db:c3:82:18:60:51:92:7d:75:51:ba:b4:a9:65:90:0e:7d:13
:30:0b:06:09:2a:86:48:86:f7:0d:01:01:0b:03:82:01:01:00:97:83:30:22:ed:2e:4e:55:3
3:73:4c:e2:56:d2:37:0a:47:39:7c:01:f9:97:6c:f6:98:f7:33:5b:b2:57:ba:d9:ff:7d:4c:
f1:92:42:64:a5:ee:56:ca:f1:da:ae:64:51:a0:90:5a:d7:a0:eb:e8:7b:e0:ef:6d:a1:8f:d0
:f6:80:c8:05:1b:29:c0:66:14:4d:bb:d9:87:e1:8e:d5:95:3c:c8:0f:fe:49:74:bd:28:a5:4
6:50:9c:e3:d5:6b:0b:48:b4:7f:1b:ad:95:8b:52:ee:5a:e1:03:43:47:6e:6e:ac:c2:5a:da:
63:3c:a4:8a:c5:f8:d1:6e:ae:eb:09:97:8b:0c:cd:37:92:77:0c:05:6b:a1:99:8f:7b:b8:6a
:30:cc:5b:c0:fc:70:f2:2b:50:73:22:d2:aa:80:b6:b0:c2:67:37:25:91:10:80:3a:ed:8c:6
b:04:02:b5:49:0c:7f:a1:a0:ef:3a:66:07:a5:9d:ab:09:be:21:8e:73:f3:91:19:3e:59:75:
cf:0f:85:3a:c8:18:89:22:68:04:95:81:9a:7d:67:19:28:11:bc:6b:e8:cb:c7:7b:9c:b4:64
:59:63:50:88:f4:05:9e:e6:fa:01:17:2b:eb:75:da:8b:34:f2:1f:47:2d:c5:79:8f:76:59:6
9:d9:89:0b:51:99:00:35",
"subject" => "CN=localhost",
"issuer" => "CN=localhost",
"not-before" => "2022-12-28T15:58:04.000+0530",
"not-after" => "2023-12-28T15:58:04.000+0530",
"serial-number" => "0c:e1:64:83:a0:95:28:72",
"signature-algorithm" => "SHA256withRSA",
"signature" => "97:83:30:22:ed:2e:4e:55:33:73:4c:e2:56:d2:37:0a:47:3
9:7c:01:f9:97:6c:f6:98:f7:33:5b:b2:57:ba:d9:ff:7d:4c:f1:92:42:64:a5:ee:56:ca:f1:
da:ae:64:51:a0:90:5a:d7:a0:eb:e8:7b:e0:ef:6d:a1:8f:d0:f6:80:c8:05:1b:29:c0:66:14
:4d:bb:d9:87:e1:8e:d5:95:3c:c8:0f:fe:49:74:bd:28:a5:46:50:9c:e3:d5:6b:0b:48:b4:7
f:1b:ad:95:8b:52:ee:5a:e1:03:43:47:6e:6e:ac:c2:5a:da:63:3c:a4:8a:c5:f8:d1:6e:ae:
eb:09:97:8b:0c:cd:37:92:77:0c:05:6b:a1:99:8f:7b:b8:6a:30:cc:5b:c0:fc:70:f2:2b:50
:73:22:d2:aa:80:b6:b0:c2:67:37:25:91:10:80:3a:ed:8c:6b:04:02:b5:49:0c:7f:a1:a0:e
f:3a:66:07:a5:9d:ab:09:be:21:8e:73:f3:91:19:3e:59:75:cf:0f:85:3a:c8:18:89:22:68:
04:95:81:9a:7d:67:19:28:11:bc:6b:e8:cb:c7:7b:9c:b4:64:59:63:50:88:f4:05:9e:e6:fa
:01:17:2b:eb:75:da:8b:34:f2:1f:47:2d:c5:79:8f:76:59:69:d9:89:0b:51:99:00:35",
"version" => "v3"
}]
}
}
Read an alias in a key store (non-verbose).
/subsystem=elytron/key-store=httpsKS:read-alias(alias="localhost",verbose=false)
{
"outcome" => "success",
"result" => {
"alias" => "localhost",
"entry-type" => "PrivateKeyEntry",
"creation-date" => "2022-12-28T15:58:04.818+0530",
"certificate-chain" => [{
"type" => "X.509",
"algorithm" => "RSA",
"format" => "X.509",
"sha-1-digest" => "70:46:1d:af:1a:5e:30:34:e9:dd:53:f4:38:b6:3e:89:5
a:1c:f9:90",
"sha-256-digest" => "61:43:2e:6f:c0:ed:91:b1:3a:20:51:fc:2b:e9:33:d2
:66:d2:a6:f2:32:5c:ca:91:14:67:7b:f2:4e:55:ed:77",
"subject" => "CN=localhost",
"issuer" => "CN=localhost",
"not-before" => "2022-12-28T15:58:04.000+0530",
"not-after" => "2023-12-28T15:58:04.000+0530",
"serial-number" => "0c:e1:64:83:a0:95:28:72",
"signature-algorithm" => "SHA256withRSA",
"signature" => "97:83:30:22:ed:2e:4e:55:33:73:4c:e2:56:d2:37:0a:47:3
9:7c:01:f9:97:6c:f6:98:f7:33:5b:b2:57:ba:d9:ff:7d:4c:f1:92:42:64:a5:ee:56:ca:f1:
da:ae:64:51:a0:90:5a:d7:a0:eb:e8:7b:e0:ef:6d:a1:8f:d0:f6:80:c8:05:1b:29:c0:66:14
:4d:bb:d9:87:e1:8e:d5:95:3c:c8:0f:fe:49:74:bd:28:a5:46:50:9c:e3:d5:6b:0b:48:b4:7
f:1b:ad:95:8b:52:ee:5a:e1:03:43:47:6e:6e:ac:c2:5a:da:63:3c:a4:8a:c5:f8:d1:6e:ae:
eb:09:97:8b:0c:cd:37:92:77:0c:05:6b:a1:99:8f:7b:b8:6a:30:cc:5b:c0:fc:70:f2:2b:50
:73:22:d2:aa:80:b6:b0:c2:67:37:25:91:10:80:3a:ed:8c:6b:04:02:b5:49:0c:7f:a1:a0:e
f:3a:66:07:a5:9d:ab:09:be:21:8e:73:f3:91:19:3e:59:75:cf:0f:85:3a:c8:18:89:22:68:
04:95:81:9a:7d:67:19:28:11:bc:6b:e8:cb:c7:7b:9c:b4:64:59:63:50:88:f4:05:9e:e6:fa
:01:17:2b:eb:75:da:8b:34:f2:1f:47:2d:c5:79:8f:76:59:69:d9:89:0b:51:99:00:35",
"version" => "v3"
}]
}
}
Configure a key-manager that references your key-store:
/subsystem=elytron/key-manager=httpsKM:add(key-store=httpsKS,credential-reference={clear-text=secret})
Configure a server-ssl-context that references your key-manager:
/subsystem=elytron/server-ssl-context=httpsSSC:add(key-manager=httpsKM,protocols=["TLSv1.2"])
IMPORTANT: You need to determine what SSL/TLS protocols you want to support. The example commands above uses TLSv1.2.
Check and see if the https-listener is configured to use a legacy security realm for its SSL configuration:
/subsystem=undertow/server=default-server/https-listener=https:read-attribute(name=security-realm)
{
"outcome" => "success",
"result" => "ApplicationRealm"
}
The above command shows that the https-listener is configured to use the ApplicationRealm legacy security realm for its SSL configuration. Undertow cannot reference both a legacy security realm and an ssl-context in Elytron at the same time so you must remove the reference to the legacy security realm. Also there has to be always configured either ssl-context or security-realm. Thus when changing between those, you have to use batch operation:
Remove the reference to the legacy security realm and update the https-listener to use the ssl-context from Elytron :
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=httpsSSC)
4.3.2. Enable Two-way SSL/TLS in WildFly for Applications
First, obtain or generate your client keystore.
$ keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -validity 365 -keystore client.keystore.pkcs12 -dname "CN=client" -keypass secret -storepass secret
Export the client certificate:
$ keytool -exportcert -keystore client.keystore.pkcs12 -alias client -keypass secret -storepass secret -file /path/to/client.cer
There are a couple ways to enable two-way SSL/TLS for deployed applications.
Using a security command:
The security enable-ssl-http-server command can be used to enable two-way SSL/TLS for the deployed applications. Example of wizard usage:
In the following example, we enter n when propmted with Validate certificate because the example uses a self-signed certificate. If you use Cretificate Authority (CA) signed certificates, enter y when prompted. |
security enable-ssl-http-server --interactive --override-ssl-context
Please provide required pieces of information to enable SSL:
Key-store file name (default default-server.keystore): server.keystore.pkcs12
Password (blank generated): secret
What is your first and last name? [Unknown]: localhost
What is the name of your organizational unit? [Unknown]:
What is the name of your organization? [Unknown]:
What is the name of your City or Locality? [Unknown]:
What is the name of your State or Province? [Unknown]:
What is the two-letter country code for this unit? [Unknown]:
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct y/n [y]?
Validity (in days, blank default): 365
Alias (blank generated): localhost
Enable SSL Mutual Authentication y/n (blank n): y
Client certificate (path to pem file): /path/to/client.cer
Validate certificate y/n (blank y): n
Trust-store file name (management.truststore): server.truststore.pkcs12
Password (blank generated): secret
SSL options:
key store file: server.keystore.pkcs12
distinguished name: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
password: secret
validity: 365
alias: localhost
client certificate: /path/to/client.cer
trust store file: server.trustore.pkcs12
trust store password: secret
Server keystore file server.keystore.pkcs12, certificate file server.pem and server.csr file will be generated in server configuration directory.
Server truststore file server.trustore.pkcs12 will be generated in server configuration directory.
Do you confirm y/n: y
NB: Once the command is executed, the CLI will reload the server. To complete the two-way SSL/TLS authentication, you need to import the server certificate into the client truststore and configure your client to present the client certificate.
Using Elytron subsystem commands:
You can also use the Elytron subsystem, along with the Undertow subsystem, to enable two-way SSL/TLS for deployed applications.
Obtain or generate your key stores:
Before enabling HTTPS in WildFly, you must obtain or generate the server key store and trust store you plan on using. To generate an example key store and trust store, use the following commands.
Create a server key-store:
/subsystem=elytron/key-store=twoWayKS:add(path=/path/to/server.keystore.pkcs12,credential-reference={clear-text=secret},type=PKCS12)
/subsystem=elytron/key-store=twoWayKS:generate-key-pair(alias=localhost,algorithm=RSA,key-size=2048,validity=365,credential-reference={clear-text=secret},distinguished-name="CN=localhost")
/subsystem=elytron/key-store=twoWayKS:store()
NOTE
The first command above uses an absolute path to the keystore.
Alternatively you can use the relative-to attribute to specify the
base directory variable and path specify a relative path.
/subsystem=elytron/key-store=twoWayKS:add(path=server.keystore.pkcs12,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=PKCS12)
Export the server certificate:
/subsystem=elytron/key-store=twoWayKS:export-certificate(alias=localhost,path=/path/to/server.cer,pem=true)
Create a key-store for the server truststore and import the client certificate into the server truststore:
In the following example, we enter validate=false in the import-certificate command because the example uses a self-signed certificate. If you use Certificate Authority (CA) signed certificates, omit validate=false. |
/subsystem=elytron/key-store=twoWayTS:add(path=/path/to/server.truststore.pkcs12,credential-reference={clear-text=secret},type=PKCS12)
/subsystem=elytron/key-store=twoWayTS:import-certificate(alias=client,path=/path/to/client.cer,credential-reference={clear-text=secret},trust-cacerts=true,validate=false)
/subsystem=elytron/key-store=twoWayTS:store()
Configure a key-manager that references your key store key-store:
/subsystem=elytron/key-manager=twoWayKM:add(key-store=twoWayKS,credential-reference={clear-text=secret})
Configure a trust-manager that references your truststore key-store:
/subsystem=elytron/trust-manager=twoWayTM:add(key-store=twoWayTS)
Configure a server-ssl-context that references your key-manager, trust-manager, and enables client authentication:
/subsystem=elytron/server-ssl-context=twoWaySSC:add(key-manager=twoWayKM,protocols=["TLSv1.2"],trust-manager=twoWayTM,need-client-auth=true)
IMPORTANT
You need to determine what SSL/TLS protocols you want to support. The
example commands above uses TLSv1.2.
Check and see if the https-listener is configured to use a legacy security realm for its SSL configuration:
/subsystem=undertow/server=default-server/https-listener=https:read-attribute(name=security-realm)
{
"outcome" => "success",
"result" => "ApplicationRealm"
}
The above command shows that the https-listener is configured to use the ApplicationRealm legacy security realm for its SSL configuration. Undertow cannot reference both a legacy security realm and an ssl-context in Elytron at the same time so you must remove the reference to the legacy security realm. Also there has to be always configured either ssl-context or security-realm. Thus when changing between those, you have to use batch operation:
Remove the reference to the legacy security realm and update the https-listener to use the ssl-context from Elytron:
batch
/subsystem=undertow/server=default-server/https-listener=https:undefine-attribute(name=security-realm)
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=twoWaySSC)
run-batch
Reload the server
reload
To complete the two-way SSL/TLS authentication, you need to import the server certificate into the client truststore and configure your client to present the client certificate.
Import the server certificate into the client truststore
$ keytool -importcert -keystore client.truststore.pkcs12 -storepass secret -alias localhost -trustcacerts -file /path/to/server.cer
Configure your client to use the client certificate
You need to configure your client to present the trusted client certificate to the server to complete the two-way SSL/TLS authentication. For example, if using a browser, you need to import the trusted certificate into the browser’s truststore.
Two-Way HTTPS is now enabled for applications.
4.3.3. Configure certificate revocation in trust-manager
Configure Certificate Revocation List:
You can configure your trust-manager to use certificate-revocation-list (CRL) to check revocation status of obtained certificates.
The supported attributes for a certificate-revocation-list include:
-
path: The path to the configuration file that is used to initialize the certificate revocation list.
-
relative-to: The base path of the certificate revocation list file.
-
maximum-cert-path: The maximum number of non-self-issued intermediate certificates that can exist in a certification path. The default value is 5. (Deprecated. Use maximum-cert-path in trust-manager).
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=certificate-revocation-list, value={})
This will use CRLs obtained from distribution points referenced in your certificates.
NOTE: To use a CRL your trust store must contain the certificate chain in order to check validity of both CRL list.
Override CRL location obtained from certificates:
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=certificate-revocation-list.path, value=intermediate.crl.pem)
Configure multiple certificate revocation lists
Alternatively, you can configure multiple certificate revocation lists in your trust-manager using the certificate-revocation-lists attribute as follows:
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=certificate-revocation-lists, value=[{path="PATH/TO/CRL"}, {path="PATH/TO/OTHER/CRL"}])
The supported attributes for certificate-revocation-lists include a list of certificate-revocation-list objects containing:
-
path: The path to the configuration file that is used to initialize the certificate revocation list.
-
relative-to: The base path of the certificate revocation list file.
Configure OCSP certificate revocation:
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=ocsp, value={})
This will enable OCSP certificate revocation by using OCSP responder inside the certificate. In case the responder is known but OCSP revocation status is unknown, the verification will fail.
Configure order of revocation mechanisms:
If both CRL and OCSP are defined, Elytron will use OCSP for obtaining revocation status as first by default. In case you want to prefer CRL:
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=ocsp.prefer-crls, value="true")
Other trust-manager configuration:
Configure trust-manager to only check leaf certificates for revocation status
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=only-leaf-cert, value="true")
Configure trust-manager to accept certificates with unknown revocation status
In case you want to accept certificates with unknown revocation status, you can enable soft-fail behaviour in your trust-manager.
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=soft-fail, value=true)
Set maximum number of intermediate certificates of trust-manager
Sets the value of the maximum number of non-self-issued intermediate certificates that may exist in a certification path with default value of 5.
/subsystem=elytron/trust-manager=twoWayTM:write-attribute(name=maximum-cert-path, value=10)
4.3.4. Enable One-way SSL/TLS for the Management Interfaces
There are a couple ways to enable one-way SSL/TLS for the management interfaces.
Using a security command:
The security enable-ssl-management command can be used to enable one-way SSL/TLS for the management interfaces. Example of wizard usage:
security enable-ssl-management --interactive
Please provide required pieces of information to enable SSL:
Key-store file name (default management.keystore): keystore.pkcs12
Password (blank generated): secret
What is your first and last name? [Unknown]: localhost
What is the name of your organizational unit? [Unknown]:
What is the name of your organization? [Unknown]:
What is the name of your City or Locality? [Unknown]:
What is the name of your State or Province? [Unknown]:
What is the two-letter country code for this unit? [Unknown]:
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct y/n [y]?
Validity (in days, blank default): 365
Alias (blank generated): localhost
Enable SSL Mutual Authentication y/n (blank n): n
SSL options:
key store file: keystore.pkcs12
distinguished name: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
password: secret
validity: 365
alias: localhost
Server keystore file keystore.pkcs12, certificate file keystore.pem and keystore.csr file
will be generated in server configuration directory.
Do you confirm y/n :y
NB: Once the command is executed, the CLI will reload the server and reconnect to it.
HTTPS is now enabled for the management interfaces.
Using Elytron subsystem commands:
Elytron subsystem commands can also be used to enable one-way SSL/TLS for the management interfaces.
Configure a key-store:
/subsystem=elytron/key-store=httpsKS:add(path=keystore.pkcs12,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=PKCS12)
NOTE: The above command uses relative-to to reference the location of the keystore file. Alternatively, you can specify the full path to the keystore in path and omit relative-to.
If the keystore file does not exist yet, the following commands can be used to generate an example key pair:
/subsystem=elytron/key-store=httpsKS:generate-key-pair(alias=localhost,algorithm=RSA,key-size=2048,validity=365,credential-reference={clear-text=secret},distinguished-name="CN=localhost")
/subsystem=elytron/key-store=httpsKS:store()
Create a key-manager and server-ssl-context.
/subsystem=elytron/key-manager=httpsKM:add(key-store=httpsKS,credential-reference={clear-text=secret})
/subsystem=elytron/server-ssl-context=httpsSSC:add(key-manager=httpsKM,protocols=["TLSv1.2"])
IMPORTANT: You need to determine what SSL/TLS protocols you want to support. The example commands above uses TLSv1.2.
4.3.5. Enable Two-way SSL/TLS for the Management Interfaces
First, obtain or generate your client keystore.
$ keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -validity 365 -keystore client.keystore.pkcs12 -dname "CN=client" -keypass secret -storepass secret
Export your client certificate.
$ keytool -exportcert -keystore client.keystore.pkcs12 -alias client -keypass secret -storepass secret -file /path/to/client.cer
There are a couple ways to enable two-way SSL/TLS for the management interfaces.
Using a security command:
The security enable-ssl-management command can be used to enable two-way SSL/TLS for the management interfaces. Example of wizard usage:
In the following example, we enter n when propmted with Validate certificate because the example uses a self-signed certificate. If you use Certificate Authority (CA) signed certificates, enter y when prompted. |
security enable-ssl-management --interactive
Please provide required pieces of information to enable SSL:
Key-store file name (default management.keystore): server.keystore.pkcs12
Password (blank generated): secret
What is your first and last name? [Unknown]: localhost
What is the name of your organizational unit? [Unknown]:
What is the name of your organization? [Unknown]:
What is the name of your City or Locality? [Unknown]:
What is the name of your State or Province? [Unknown]:
What is the two-letter country code for this unit? [Unknown]:
Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct y/n [y]?
Validity (in days, blank default): 365
Alias (blank generated): localhost
Enable SSL Mutual Authentication y/n (blank n): y
Client certificate (path to pem file): /path/to/client.cer
Validate certificate y/n (blank y): n
Trust-store file name (management.truststore): server.truststore.pkcs12
Password (blank generated): secret
SSL options:
key store file: server.keystore.pkcs12
distinguished name: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
password: secret
validity: 365
alias: localhost
client certificate: /path/to/client.cer
trust store file: server.trustore.pkcs12
trust store password: secret
Server keystore file server.keystore.pkcs12, certificate file server.pem and server.csr file will be generated in server configuration directory.
Server truststore file server.trustore.pkcs12 will be generated in server configuration directory.
Do you confirm y/n: y
NB: Once the command is executed, the CLI will reload the server and attempt to reconnect to it. To complete the two-way SSL/TLS authentication, you need to import the server certificate into the client truststore and configure your client to present the client certificate.
Using Elytron subsystem commands:
Elytron subsystem commands can also be used to enable two-way SSL/TLS for the management interfaces.
Obtain or generate your key stores.
Before enabling HTTPS in WildFly, you must obtain or generate the server key store and trust store you plan on using. To generate an example key store and trust store, use the following commands.
Configure a key-store.
/subsystem=elytron/key-store=twoWayKS:add(path=server.keystore.pkcs12,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=PKCS12)
/subsystem=elytron/key-store=twoWayKS:generate-key-pair(alias=localhost,algorithm=RSA,key-size=2048,validity=365,credential-reference={clear-text=secret},distinguished-name="CN=localhost")
/subsystem=elytron/key-store=twoWayKS:store()
NOTE: The above command uses relative-to to reference the location of the keystore file. Alternatively, you can specify the full path to the keystore in path and omit relative-to.
Export your server certificate.
/subsystem=elytron/key-store=twoWayKS:export-certificate(alias=localhost,path=/path/to/server.cer,pem=true)
Create a key-store for the server trust store and import the client certificate into the server trust store.
In the following example, we enter validate=false in the import-certificate command because the example uses a self-signed certificate. If you use Certificate Authority (CA) signed certificates, omit validate=false. |
/subsystem=elytron/key-store=twoWayTS:add(path=server.truststore.pkcs12,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=PKCS12)
/subsystem=elytron/key-store=twoWayTS:import-certificate(alias=client,path=/path/to/client.cer,credential-reference={clear-text=secret},trust-cacerts=true,validate=false)
/subsystem=elytron/key-store=twoWayTS:store()
Configure a key-manager, trust-manager, and server-ssl-context for the server key store and trust store.
/subsystem=elytron/key-manager=twoWayKM:add(key-store=twoWayKS,credential-reference={clear-text=secret})
/subsystem=elytron/trust-manager=twoWayTM:add(key-store=twoWayTS)
/subsystem=elytron/server-ssl-context=twoWaySSC:add(key-manager=twoWayKM,protocols=["TLSv1.2"],trust-manager=twoWayTM,want-client-auth=true,need-client-auth=true)
IMPORTANT: You need to determine what SSL/TLS protocols you want to support. The example commands above uses TLSv1.2.
Enable HTTPS on the management interface.
/core-service=management/management-interface=http-interface:write-attribute(name=ssl-context, value=twoWaySSC)
/core-service=management/management-interface=http-interface:write-attribute(name=secure-socket-binding, value=management-https)
Reload the WildFly instance.
reload
To complete the two-way SSL/TLS authentication, you need to import the server certificate into the client truststore and configure your client to present the client certificate.
Import the server certificate into the client truststore.
$ keytool -importcert -keystore client.truststore.pkcs12 -storepass secret -alias localhost -trustcacerts -file /path/to/server.cer
Configure your client to use the client certificate.
You need to configure your client to present the trusted client certificate to the server to complete the two-way SSL/TLS authentication. For example, if using a browser, you need to import the trusted certificate into the browser’s trust store.
Two-way SSL/TLS is now enabled for the management interfaces.
4.3.6. KeyStore manipulation operations
It is possible to perform various KeyStore manipulation operations on an Elytron key-store resource using the management CLI.
Generate a key pair
The generate-key-pair command generates a key pair and wraps the resulting public key in a self-signed X.509 certificate. The generated private key and self-signed certificate will be added to the KeyStore.
/subsystem=elytron/key-store=httpsKS:generate-key-pair(alias=example,algorithm=RSA,key-size=2048,validity=365,credential-reference={clear-text=secret},distinguished-name="CN=www.example.com")
Generate a certificate signing request
The generate-certificate-signing-request command generates a PKCS #10 certificate signing request using a PrivateKeyEntry from the KeyStore. The generated certificate signing request will be output to a file.
/subsystem=elytron/key-store=httpsKS:generate-certificate-signing-request(alias=example,path=server.csr,relative-to=jboss.server.config.dir,distinguished-name="CN=www.example.com",extensions=[{critical=false,name=KeyUsage,value=digitalSignature}],credential-reference={clear-text=secret})
Import a certificate or certificate chain
The import-certificate command imports a certificate or certificate chain from a file into an entry in the KeyStore.
/subsystem=elytron/key-store=httpsKS:import-certificate(alias=example,path=/path/to/certificate_or_chain/file,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},trust-cacerts=true)
Export a certificate
The export-certificate command exports a certificate from an entry in the KeyStore to a file.
/subsystem=elytron/key-store=httpsKS:export-certificate(alias=example,path=serverCert.cer,relative-to=jboss.server.config.dir,pem=true)
Change an alias
The change-alias command moves an existing KeyStore entry to a new alias.
/subsystem=elytron/key-store=httpsKS:change-alias(alias=example,new-alias=newExample,credential-reference={clear-text=secret})
Store changes made to key-stores
The store command persists any changes that have been made to the file that backs the KeyStore.
/subsystem=elytron/key-store=httpsKS:store()
Obtain a signed certificate from Let’s Encrypt
Before obtaining a signed certificate from Let’s Encrypt, you must configure a Let’s Encrypt account using the following commands.
Create a key-store to hold your Let’s Encrypt account key.
/subsystem=elytron/key-store=accountsKS:add(path=accounts.keystore.pkcs12,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=PKCS12)
Configure a Let’s Encrypt account
/subsystem=elytron/certificate-authority-account=myLEAccount:add(alias=example,key-store=accountsKS,contact-urls=[mailto:admin@example.org])
Note: Let’s Encrypt is the default certificate authority and therefore the certificate-authority
attribute can be omitted when creating a certificate-authority-account
.
It is also possible to configure an account with different certificate authority than Let’s Encrypt by adding custom certificate-authority
resource and passing it to certificate-authority-account
.
/subsystem=elytron/certificate-authority=myCA:add(url="https://my.example.url/acme/directory", staging-url="https://my.example.staging.url/acme/directory")
/subsystem=elytron/certificate-authority-account=myCAAccount:add(certificate-authority=myCA,alias=example,key-store=accountsKS,contact-urls=[mailto:admin@example.org])
Obtain a signed certificate from Let’s Encrypt
The obtain-certificate command creates an account with Let’s Encrypt, if such an account does not already exist, obtains a signed certificate from Let’s Encrypt, and stores it in the KeyStore.
/subsystem=elytron/key-store=httpsKS:obtain-certificate(alias=server,domain-names=[www.example.org],certificate-authority-account=myLEAccount,agree-to-terms-of-service=true,algorithm=RSA,key-size=2048,credential-reference={clear-text=secret})
Revoke a signed certificate
The revoke-certificate command revokes a certificate that was issued by Let’s Encrypt.
/subsystem=elytron/key-store=httpsKS:revoke-certificate(alias=server,reason=keyCompromise,certificate-authority-account=myLEAccount)
Check if a certificate is due for renewal
The should-renew-certificate command checks if a certificate is due for renewal. In particular, it will return true if the certificate expires in less than the given number of days and false otherwise.
/subsystem=elytron/key-store=httpsKS:should-renew-certificate(alias=server,expiration=7)
4.3.7. Certificate authority account operations
It is possible to perform various operations on an Elytron certificate-authority-account resource using the management CLI.
Create an account with the certificate authority
The create-account command creates an account with the certificate authority if one does not already exist.
/subsystem=elytron/certificate-authority-account=myLEAccount:create-account(agree-to-terms-of-service=true)
Update an account with the certificate authority
The update-account command updates an account with the certificate authority.
/subsystem=elytron/certificate-authority-account=myLEAccount:update-account(agree-to-terms-of-service=true)
Change the account key
The change-account-key command changes the key associated with the certificate authority account.
/subsystem=elytron/certificate-authority-account=myLEAccount:change-account-key()
4.3.8. Using an ldap-key-store
An ldap-key-store allows you to use a keystore stored in an LDAP server. You can use an ldap-key-store in same way you can use a key-store.
To create and use an ldap-key-store:
Configure a dir-context.
To connect to the LDAP server from WildFly, you need to configure a dir-context that provides the URL as well as the principal used to connect to the server.
Example dir-context
/subsystem=elytron/dir-context=exampleDC:add( \
url="ldap://127.0.0.1:10389", \
principal="uid=admin,ou=system", \
credential-reference={clear-text=secret} \
)
Configure an ldap-key-store.
When configure an ldap-key-store, you need to specify both the dir-context used to connect to the LDAP server as well as how to locate the keystore stored in the LDAP server. At a minimum, this requires you specify a search-path.
Example ldap-key-store
/subsystem=elytron/ldap-key-store=ldapKS:add( \
dir-context=exampleDC, \
search-path="ou=Keystores,dc=wildfly,dc=org" \
)
4.3.9. Using a filtering-key-store
A filtering-key-store allows you to expose a subset of aliases from an existing key-store, and use it in the same places you could use a key-store. For example, if a keystore contained alias1, alias2, and alias3, but you only wanted to expose alias1 and alias3, a filtering-key-store provides you several ways to do that.
To create a filtering-key-store:
Configure a key-store.
/subsystem=elytron/key-store=myKS:add( \
path=keystore.pkcs12, \
relative-to=jboss.server.config.dir, \
credential-reference={ \
clear-text=secret \
}, \
type=PKCS12 \
)
Configure a filtering-key-store.
When you configure a filtering-key-store, you specify which key-store you want to filter and the alias-filter for filtering aliases from the key-store. The filter can be specified in one of the following formats:
-
alias1,alias3, which is a comma-delimited list of aliases to expose.
-
ALL:-alias2, which exposes all aliases in the keystore except the ones listed.
-
NONE:+alias1:+alias3, which exposes no aliases in the keystore except the ones listed.
This example uses a comma-delimted list to expose alias1 and alias3.
/subsystem=elytron/filtering-key-store=filterKS:add( \
key-store=myKS, \
alias-filter="alias1,alias3" \
)
4.3.10. Reload a Keystore
You can reload a keystore configured in WildFly from the management CLI. This is useful in cases where you have made changes to certificates referenced by a keystore.
To reload a keystore.
/subsystem=elytron/key-store=httpsKS:load
4.3.11. Reinitialize a Key Manager
You can reinitialize a key-manager configured in WildFly from the management CLI. This is useful in cases where you have made changes in certificates provided by keystore resource and you want to apply this change to new SSL connections without restarting the server.
If the key-store is file based then it must be loaded first.
/subsystem=elytron/key-store=httpsKS:load()
To reinitialize a key-manager.
/subsystem=elytron/key-manager=httpsKM:init()
4.3.12. Reinitialize a Trust Manager
You can reinitialize a trust-manager configured in WildFly from the management CLI. This is useful in cases where you have made changes to certificates provided by keystore resource and you want to apply this change to new SSL connections without restarting the server.
If the key-store is file based then it must be loaded first.
/subsystem=elytron/key-store=httpsKS:load()
To reinitialize a trust-manager.
/subsystem=elytron/trust-manager=httpsTM:init()
4.3.13. Check the Content of a Keystore by Alias
If you add a keystore to the elytron subsystem using the key-store component, you can check the keystore’s contents using the alias child element and reading its attributes.
For example:
/subsystem=elytron/key-store=httpsKS/alias=localhost:read-attribute(name=certificate-chain)
{
"outcome" => "success",
"result" => [{
"type" => "X.509",
"algorithm" => "RSA",
"format" => "X.509",
"public-key" => "30:81:9f:30:0d:06:09:2a:8......
The following attributes can be read:
Attribute | Description |
---|---|
certificate |
The certificate associated with the alias. If the alias has a certificate chain this will always be undefined. |
certificate-chain |
The certificate chain associated with the alias. |
creation-date |
The creation date of the entry represented by this alias. |
entry-type |
The type of the entry for this alias. Available types: PasswordEntry, PrivateKeyEntry, SecretKeyEntry, TrustedCertificateEntry, and Other. Unrecognized types will be reported as Other. |
4.3.14. Custom Components
When configuring SSL/TLS in the elytron subsystem, you can provide and use custom implementations of the following components:
-
key-store
-
key-manager
-
trust-manager
-
client-ssl-context
-
server-ssl-context
-
certificate-authority-account
When creating custom implementations of Elytron components, they must present the appropriate capabilities and requirements.
4.3.15. Configuring a server SSLContext
Using the Elytron subsystem, it is possible to configure an SSLContext
for use on the server side of a connection.
Adding a server SSLContext
takes the general form:
/subsystem=elytron/server-ssl-context=test-server-ssl-context:add(...)
The following attributes can be specified when creating a server-ssl-context
:
- security-domain
-
(Optional) A reference to the
security-domain
to use for authentication during SSL session establishment. - key-manager
-
(Optional) A reference to the
KeyManager
to be used by thisSSLContext
. - trust-manager
-
(Optional) A reference to the
TrustManager
to be used by thisSSLContext
. - cipher-suite-filter
-
(Optional) The filter to be applied to the cipher suites for TLSv1.2 and below made available by this SSLContext. The format of this attribute is described in detail in org.wildfly.security.ssl.CipherSuiteSelector.fromString(selector). The default value is
DEFAULT
, which corresponds to all known cipher suites that do not have NULL encryption and excludes any cipher suites that have no authentication. - cipher-suite-names
-
(Optional) The enabled cipher suites for TLSv1.3. The format of this attribute is a simple colon (":") separated list of TLSv1.3 cipher suite names (e.g.,
TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
). This attribute must be specified in order for TLSv1.3 to be enabled. - protocols
-
(Optional) A space separated list of the protocols to be supported by this
SSLContext
. The default value isTLSv1 TLSv1.1 TLSv1.2 TLSv1.3
. Note that the TLSv1.3 protocol will only be usable when running against JDK 11 or higher. - want-client-auth
-
(Optional) To request (but not to require) a client certificate on SSL handshake. If a
security-domain
is configured and supports X509 evidence, this will be set totrue
automatically. Ignored whenneed-client-auth
is set. The default value isfalse
. - need-client-auth
-
(Optional) If
true
, a client certificate is required on SSL handshake. A connection without a trusted client certificate will be rejected. The default value isfalse
. - authentication-optional
-
(Optional) Rejection of the client certificate by the configured
security-domain
will not prevent the connection. This allows a fall through to use other authentication mechanisms (like form login) when the client certificate is rejected by thesecurity-domain
. This has an effect only when thesecurity-domain
is configured. The default value isfalse
. - use-cipher-suites-order
-
(Optional) If
true
, the cipher suites order defined on the server will be used. Iffalse
, the cipher suites order presented by the client will be used. The default value istrue
. - provider-name
-
(Optional) The name of the provider to use. If not specified, all providers from
providers
will be passed to theSSLContext
. - providers
-
(Optional) The name of the providers to obtain the
Provider[]
to use to load theSSLContext
. - maximum-session-cache-size
-
(Optional) The maximum number of SSL sessions to be cached. The default value
-1
indicates that the JVM default value should be used. A value of0
means there is no limit. - session-timeout
-
(Optional) The timeout for SSL sessions. The default value
-1
indicates that the JVM default value should be used. A value of0
means there is no limit. - wrap
-
(Optional) If
true
, the returnedSSLEngine
,SSLSocket
, andSSLServerSocket
instances will be wrapped to protect against further modification. The default value isfalse
. - pre-realm-principal-transformer
-
(Optional) A principal transformer to apply before the realm is selected.
- post-realm-principal-transformer
-
(Optional) A principal transformer to apply after the realm is selected.
- final-principal-transformer
-
(Optional) A final principal transformer to apply for this mechanism realm.
- realm-mapper
-
(Optional) The realm mapper to be used for SSL authentication.
4.3.16. Configuring a client SSLContext
Using the Elytron subsystem, it is possible to configure an SSLContext
for use on the client side of a connection.
Adding a client SSLContext
takes the general form:
/subsystem=elytron/client-ssl-context=test-client-ssl-context:add(...)
The following attributes can be specified when creating a client-ssl-context
:
- key-manager
-
(Optional) A reference to the
KeyManager
to be used by thisSSLContext
. - trust-manager
-
(Optional) A reference to the
TrustManager
to be used by thisSSLContext
. - cipher-suite-filter
-
(Optional) The filter to be applied to the cipher suites for TLSv1.2 and below made available by this SSLContext. The format of this attribute is described in detail in org.wildfly.security.ssl.CipherSuiteSelector.fromString(selector). The default value is
DEFAULT
, which corresponds to all known cipher suites that do not have NULL encryption and excludes any cipher suites that have no authentication. - cipher-suite-names
-
(Optional) The enabled cipher suites for TLSv1.3. The format of this attribute is a simple colon (":") separated list of TLSv1.3 cipher suite names (e.g.,
TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
). This attribute must be specified in order for TLSv1.3 to be enabled. - protocols
-
(Optional) A space separated list of the protocols to be supported by this
SSLContext
. The default value isTLSv1 TLSv1.1 TLSv1.2 TLSv1.3
. Note that the TLSv1.3 protocol will only be usable when running against JDK 11 or higher. - provider-name
-
(Optional) The name of the provider to use. If not specified, all providers from
providers
will be passed to theSSLContext
. - providers
-
(Optional) The name of the providers to obtain the
Provider[]
to use to load theSSLContext
.
WARNING It is possible to use TLSv1.3 with WildFly when running against JDK 11 or higher. However, if JDK 11
is in use and if there is a very large number of TLSv1.3 requests being made, it is possible that a drop in
performance (throughput and response time) will occur compared to TLSv1.2. Upgrading to newer JDK versions
should improve performance. For this reason, the use of TLSv1.3 is currently disabled by default. TLSv1.3 can
be enabled by configuring the new cipher-suite-names
attribute in the SSL Context resource definition in the
Elytron subsystem as described in the previous two sections. It is recommended to test for performance
degradation prior to enabling TLSv1.3 in a production environment. See WFWIP-160
for more details.
Note: When using TLSv1.3, it is important to keep in mind that session IDs have become essentially obsolete.
This means that the session ID can no longer reliably be used to test if a session was resumed. Instead,
creation time can be used to test if a session was resumed. Currently, clients need to read from the server after
the first handshake in order to receive the NewSessionTicket
that can be used for resumption. However, this may
change once JDK-8209953 is resolved.
4.3.17. Configuring SSLv2Hello
Older JDK versions use SSLv2Hello
during the initial SSL handshake message
where the SSL/TLS version that will be used for the rest of the communication is
negotiated.
Using SSLv2Hello
is discouraged, therefore newer JDK versions disable this protocol
on the client by default. However, they do provide the ability to re-enable it if necessary.
SSLv2Hello
can be configured as a supported protocol for the server SSL context as follows:
/subsystem=elytron/server-ssl-context=server-context:add(protocols=[SSLv2Hello,TLSv1], ...)
Similarly, it can be configured as a supported protocol for the client SSL context as follows:
/subsystem=elytron/client-ssl-context=client-context:add(protocols=[SSLv2Hello,TLSv1], ...)
WARNING:
-
The use of
SSLv2Hello
is strongly discouraged. -
SSLv2Hello
cannot be configured by itself, as its purpose is to determine which encryption protocols are supported by the server it connects to. It always needs to be configured along side another encryption protocol. -
Additionally, IBM JDK does not support specifying
SSLv2Hello
in its client, although a server side connection always accepts this protocol.
4.3.18. Default SSLContext
Many libraries that can be used within deployments may require SSL configuration for any connections they establish, these libraries tend to be configurable by the caller or if no configuration is provided fall back to using the default SSLContext
for the process available from: -
javax.net.ssl.SSLContext.getDefault();
By default this SSLContext
is configured using system properties, however within the WildFly Elytron subsystem it is possible to specify that one of the configured contexts should be associated and used as the default.
To make use of this feature configure your SSLContext
as normal, the following command can then be used to specify which SSLContext
should be used as the default.
/subsystem=elytron:write-attribute(name=default-ssl-context, value=client-context)
As existing services and deployments could have cached the default SSLContext
prior to this being set a reload is required to ensure the default gets set before the deployments are activated.
:reload
Note: If the default-ssl-context
attribute is subsequently 'undefined' the standard APIs do not provide us with a mechanism to revert the default so in this situation the Java process would need be restarted.
/subsystem=elytron:undefine-attribute(name=default-ssl-context)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-restart" => true,
"process-state" => "restart-required"
}
}
4.3.19. Configuring SNI
Using the WildFly Elytron subsystem it is possible to configure an SSL context which supports SNI. By supporting SNI if an SNI host name is available whilst the SSLSession is being negotiated a host specific SSLContext will be selected. If no host specific SSLContext is identified either because no host name was received or because there is no match a default SSLContext will be used instead. By identifying a host specific SSLContext it means that a certificate appropriate for that host can be used.
The following command demonstrates how an SNI aware SSLContext can be added: -
[standalone@localhost:9990 /] ./subsystem=elytron/server-ssl-sni-context=test-sni:add(default-ssl-context=jboss,host-context-map={localhost=localhost, wildfly.org=wildfly})
{"outcome" => "success"}
This example assumes that three SSLContexts have been previously defined following the steps available previously in this document, those contexts are jboss
, localhost
, and wildfly
.
During negotiation of the SSLSession if the SNI host name received is localhost
then the localhost
SSLContext will be used, if the SNI host name is wildfly.org
then the wildfly
SSLContext will be used. If no SNI host name is received or if we receive a name that does not match this will fallback and use the jboss
SSLContext.
The resulting resource looks like: -
[standalone@localhost:9990 /] ./subsystem=elytron/server-ssl-sni-context=test-sni:read-resource
{
"outcome" => "success",
"result" => {
"default-ssl-context" => "jboss",
"host-context-map" => {
"localhost" => "localhost",
"wildfly.org" => "wildfly"
}
}
}
Within the host-context-map
it is also possible to define wildcard mappings such as *
and *.wildfly.org
.
4.3.20. Use the Elytron Subsystem
For authentication in applications, you can use the application-security-domain property in the undertow subsystem to configure a security domain in the elytron subsystem.
/subsystem=undertow/application-security-domain=exampleApplicationDomain:add(http-authentication-factory=example-http-auth)
NOTE: This must match the security-domain configured in the jboss-web.xml of your application.
For enabling HTTPS using elytron you need to set the ssl-context attribute.
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=httpsSSC)
4.4. Creating Elytron Subsystem Components
4.4.1. Create an Elytron Security Realm
Security realms in the Elytron subsystem, when used in conjunction with security domains, are use for both core management authentication as well as for authentication with applications. Security realms are also specifically typed based on their identity store, for example jdbc-realm, filesystem-realm, properties-realm, etc.
Adding a security realm takes the general form:
/subsystem=elytron/type-of-realm=realmName:add(....)
Examples of adding specific realms, such as jdbc-realm, filesystem-realm, and properties-realm can be found in previous sections.
4.4.2. Create an Elytron Role Decoder
A role decoder converts attributes from the identity provided by the security realm into roles. Role decoders are also specifically typed based on their functionality, for example empty-role-decoder, simple-role-decoder, custom-role-decoder, and aggregate-role-decoder.
Adding a role decoder takes the general form:
/subsystem=elytron/ROLE-DECODER-TYPE=roleDeoderName:add(....)
Create an Elytron Role Decoder that makes use of the IP Address of the Remote Client
A role decoder that is configured on a security realm assigns roles to an identity based on attributes provided by the security realm. It is also possible to configure a role decoder that makes use of the IP address of the remote client when determining the roles associated with an identity. As an example, we might want to make use of the IP address of the remote client in order to assign a user a particular role when establishing a connection to the server from a corporate network and a different role when establishing a connection to the server from a different network.
Adding a role decoder that makes use of the remote client’s IP address takes the general form:
/subsystem=elytron/source-address-role-decoder=roleDecoderName:add(source-address="...", pattern="...", roles=[...])
In particular, a source-address-role-decoder has the following attributes:
-
source-address - The IP address of the remote client.
-
pattern - A regular expression that specifies the IP address to match.
-
roles - The list of roles to assign if the source IP address matches the given address.
Only one of source-address and pattern should be specified.
For example, the following source-address-role-decoder could be configured to specify that a user should be assigned the "Administrator" role when establishing a connection to the server from the 10.10.10.10 IP address:
/subsystem=elytron/source-address-role-decoder=decoder1:add(source-address="10.10.10.10", roles=["Administrator"])
Once a source-address-role-decoder has been configured, it can be referenced from a security-domain:
/subsystem=elytron/security-domain=exampleSD:add(..., role-decoder=decoder1)
It is also possible to assign permissions to an identity based on its roles using a simple permission mapper.
Example configuration:
/subsystem=elytron/source-address-role-decoder=decoder1:add(source-address="10.10.10.10", roles=["Administrator"])
/subsystem=elytron/simple-permission-mapper=ipPermissionMapper:add(permission-mappings=[{roles=["Administrator"], permission-sets=[{permission-set=login-permission}]}])
/subsystem=elytron/security-domain=exampleSD:add(..., role-decoder=decoder1, permission-mapper=ipPermissionMapper)
The above example configures a security domain with a source address role decoder that will assign the "Administrator" role when the IP address of the remote client matches the configured address and a simple permission mapper that only assigns the "LoginPermission" if the identity has the "Administrator" role. Thus, authentication will succeed if the IP address of the remote client matches the configured address.
It is also possible to configure an aggregate-role-decoder. This consists of two or more role-decoder elements where each element is a reference to a configured role decoder. Each role decoder will be applied and the returned value will be a union of the roles returned by each decoder.
Example configuration:
/subsystem=elytron/source-address-role-decoder=decoder1:add(source-address="10.10.10.10", roles=["Administrator"])
/subsystem=elytron/source-address-role-decoder=decoder2:add(source-address="12.12.12.12", roles=["Users"])
/subsystem=elytron/aggregate-role-decoder=aggregateDecoder:add(role-decoders=[decoder1, decoder2])
4.4.3. Create an Elytron Permission Set
Permission sets can be used to assign permissions to an identity.
Adding a permission set takes the general form:
/subsystem=elytron/permission-set=PermissionSetName:add(permissions=[{class-name="...", module="...", target-name="...", action="..."}...])
where permissions consists of a set of permissions, where each permission has the following attributes:
-
class-name is the fully qualified class name of the permission. This is the only permission attribute that is required.
-
module is the optional module to use to load the permission.
-
target-name is the optional target name to pass to the permission as it is constructed.
-
action is the optional action to pass to the permission as it is constructed.
4.4.4. Create an Elytron Permission Mapper
In addition to roles being assigned to a identity, permissions may also be assigned. A permission mapper assigns permissions to an identity. Permission mappers are also specifically typed based on their functionality, for example logical-permission-mapper, simple-permission-mapper, and custom-permission-mapper.
Adding a permission mapper takes the general form:
/subsystem=elytron/simple-permission-mapper=PermissionMapperName:add(...)
4.4.5. Create an Elytron Role Mapper
A role mapper maps roles after they have been decoded to other roles. Examples include normalizing role names or adding and removing specific roles from principals after they have been decoded. Role mappers are also specifically typed based on their functionality, for example add-prefix-role-mapper, add-suffix-role-mapper, and constant-role-mapper.
Adding a role mapper takes the general form:
/subsystem=elytron/ROLEM-MAPPER-TYPE=roleMapperName:add(...)
4.4.6. Create an Elytron Security Domain
Security domains in the Elytron subsystem, when used in conjunction with security realms, are use for both core management authentication as well as for authentication with applications.
Adding a security domain takes the general form:
/subsystem=elytron/security-domain=domainName:add(realms=[{realm=realmName,role-decoder=roleDecoderName}],default-realm=realmName,permission-mapper=permissionMapperName,role-mapper=roleMapperName,...)
4.4.7. Create an Elytron Authentication Factory
An authentication factory is an authentication policy used for specific
authentication mechanisms. Authentication factories are specifically
based on the authentication mechanism, for example
http-authentication-factory and
sasl-authentication-factory and kerberos-security-factory.
Adding an authentication factory takes the general form:
/subsystem=elytron/AUTH-FACTORY-TYPE=authFactoryName:add(....)
4.4.8. Create an Elytron Policy Provider
Elytron subsystem provides a specific resource definition that can be used to configure a default ruby Policy provider. The subsystem allows you to define multiple policy providers but select a single one as the default:
/subsystem=elytron/policy=policy-provider-a:add(custom-policy=\[{name=policy-provider-a, class-name=MyPolicyProviderA, module=x.y.z}\])
4.4.9. Create an Elytron Case Principal Transformer
Principal transformers can take a name and map it to another representation of the name or perform
some normalisation. A case-principal-transformer
converts a principal to upper or lower case.
As an example, we might want to convert our principal to upper case if the identities in our realm
are stored in upper case.
Adding a case-principal-transformer
that converts a principal to upper/lower case takes the
general form:
/subsystem=elytron/case-principal-transformer=transformerName:add(upper-case="...")
In particular, a case-principal-transformer
has the following attribute:
-
upper-case
- A boolean value to indicate whether the principal should be converted to upper case. Indicatingfalse
for this attribute converts the principal to lower case.
For example, the following case-principal-transformer
could be configured to specify that
a principal should be transformed to lower case:
/subsystem=elytron/case-principal-transformer=transformer1:add(upper-case="false")
On the other hand, a case-principal-transformer
could be configured by omitting the
upper-case
attribute, which is then set to true
by default:
/subsystem=elytron/case-principal-transformer=transformer2:add()
4.5. Component Documentation
4.5.1. Security Realms
The primary responsibility of a security realm is the ability to load identities with associated attributes, these identities are then used within the authentication process for credential validation and subsequently wrapped to represent the SecurityIdentity
instances using within applications for authorization.
Generally a security realm operates in one of three modes, in the first mode on loading an identity from its store the security realm also loads one or more credential representations from the store and holds these within the identity. During authentication the credentials within the identity can be used to verify the connection attempt. The reason multiple credentials are loaded is because specific types may be applicable to specific mechanisms so an appropriate credential representation can be selected at the time of authentication.
In the second mode the identity is loaded as in the first mode however no credentials are loaded, in this mode evidence can be passed in to the identity for verification. This mode is sometimes the only mode available where it is not possible to load the representation of a credential from its store.
In the third mode evidence is passed to the security realm and used to construct the resulting identity, this mode is predominantly used with certificate and token based authentication mechanisms where the verified certificate or token can be used to construct the resulting identity.
Once an identity has been loaded for authorization decisions the identity will have a set of associated attributes, these will subsequent be mapped using role decoders, role mappers, and permission mappers as required for identity specific authorization decisions.
4.5.2. Aggregate Security Realm
The aggregate security realm allows for two or more security realms to be aggregated into a single security realm allowing one to be used during the authentication steps and one or more to be used to assemble the identity used for authorization decisions and aggregating any associated attributes.
The aggregate-realm
resource contains the following attributes: -
-
authentication-realm
- This realm use used to load the credentials of the identity or perform evidence verification. -
authorization-realm
- The realm to use to load the identities attributes used for authorization. -
authorization-realms
- A list of one or more realms to use to load the identities attributes and aggregate into a single set of attributes. -
principal-transformer
- A principal transformer that can be used to transform the principal after loading the credentials for authentication but before loading the identities attributes for authorization. This attribute is optional.
authorization-realm and authorization-realms are mutually exclusive so exactly one of these must be specified.
|
Defining a Simple Aggregation
Assuming two realms properties-realm
and jdbc-realm
already exist an aggregate-realm
combining these two can be created with the following command.
/subsystem=elytron/aggregate-realm=combined:add(
authentication-realm=properties-realm,
authorization-realm=jdbc-realm)
Where this realm is used the properties-realm
will be used to load an identity’s credentials and the jdbc-realm
will be used to load the attributes for the identity.
The following command is exactly the same except the authorization-realms
attribute is used instead.
/subsystem=elytron/aggregate-realm=combined:add(
authentication-realm=properties-realm,
authorization-realms=[jdbc-realm])
The following command will reference a principal transformer defined in the mappers configuration to be used to transform the principal after authentication.
/subsystem=elytron/aggregate-realm=combined:add(
authentication-realm=properties-realm,
authorization-realms=[jdbc-realm],
principal-transformer=custom-transformer)
Aggregating Attributes
If a third realm ldap-realm
also exists that shoud also be used for the loading of attributes an aggregate-realm
can be defined using the following command: -
/subsystem=elytron/aggregate-realm=combined:add(
authentication-realm=properties-realm,
authorization-realms=[jdbc-realm, ldap-realm])
As before the properties-realm
will be used to load the identity’s credentials or perform evidence verification but the attributes for the identity will be loaded both from the jdbc-realm
and ldap-realm
then combined together.
The approach taken to combine attributes makes use of the first instance of each attribute, if the same attribute is loaded by multiple realms only the first occurrence will be used. Later occurrences of an existing attribtue will be ignored.
As an example if the following attributes were loaded from the jdbc-realm
: -
ATTRIBUTE | VALUES |
---|---|
groups |
Supervisor, User |
And the following attributes were loaded from the ldap-realm
: -
ATTRIBUTE | VALUES |
---|---|
phone |
0000 0000 0000 |
The resulting attributes would be: -
ATTRIBUTE | VALUES |
---|---|
groups |
Supervisor, User |
phone |
0000 0000 0000 |
4.5.3. Distributed Security Realm
The distributed realm allows combining identities located across multiple security realms of any type into a single security realm.
The distributed-realm
resource contains the following attributes: -
-
realms
- List of realms in the order they should be queried -
ignore-unavailable-realms
- Whether subsequent realms should be checked after an unavailable realm is reached. The default value is false. -
emit-events
- Whether a SecurityEvent signifying realm unavailability should be emitted. The default value is true.
Defining a Distributed Realm
As an example, assuming realms named properties-realm
, ldap-realm
and jdbc-realm
have already been configured, then a distributed-realm that references these three realms can be created as follows:
/subsystem=elytron/distributed-realm=distributedRealmExample:add(realms=[properties-realm, ldap-realm, jdbc-realm])
When configuring a distributed-realm
, it is possible to ignore any unavailable realms by setting ignore-unavailable-realms
to true. When retrieving an identity from a distributed-realm
, each of the configured realms is attempted sequentially until a realm containing the identity is found. If a security realm becomes unavailable for some reason, if ignore-unavailable-realms
is true, any subsequent realms will still be checked. If ignore-unavailable-realms
is true and searched identity happened to be stored in the unavailable realm, authentication fails, and 401
response wil be received. If ignore-unavailable-realms
is set to false or not set, and the realm happens to be unavailable then an org.wildfly.security.auth.server.RealmUnavailableException
will be thrown and no further realms will be attempted.
/subsystem=elytron/distributed-realm=distributedRealmExample:add(realms=[properties-realm, ldap-realm, jdbc-realm], ignore-unavailable-realms=true)
While ignore-unavailable-realms
is set to true, emit-events
is also set to true by default, so the SecurityEvent
will be emitted in case any of the attempted realms is unavailable.
This can be turned off by setting emit-events
to false.
/subsystem=elytron/distributed-realm=distributedRealmExample:add(realms=[properties-realm, ldap-realm, jdbc-realm], ignore-unavailable-realms=true, emit-events=false)
4.5.4. Failover Security Realm
The failover security realm allows for a realm to fail-over to another realm in case the first is unavailable.
The failover-realm
resource contains the following attributes: -
-
delegate-realm
- The realm to use as a default. -
failover-realm
- The realm to use in casedelegate-realm
is not available. -
emit-events
- Whenever to emit SecurityEvent in case thedelegate-realm
is unavailable.
Defining a Failover
Assuming two realms properties-realm
and jdbc-realm
already exist an failover-realm
combining these two can be created with the following command.
/subsystem=elytron/failover-realm=theRealm:add(delegate-realm=jdbc-realm, failover-realm=properties-realm)
The failover-realm keeps modifiability of the delegate-realm unless failover happens. In that case, the failover-realm
will function as unmodifiable.
|
4.5.5. Filesystem Security Realm
The filesystem security realm is a security realm developed to support storing of identities in a filesystem with the option of associating multiple credentials and multiple attributes with each identity. Both credentials and attributes can contain multiple values.
Create and Populate Filesystem Security Realm
Every identity is stored in an XML file where the name of the file is the name of the identity. It is sufficient to just specify the path to the directory where identity files will be stored. There are other optional attributes.
-
path
The actual filesystem path. Treated as an absolute path, unless the 'relative-to' attribute is specified, in which case the value is treated as relative to that path. -
relative-to
If 'relative-to' is provided, the value of the 'path' attribute is treated as relative to the path specified by this attribute. -
levels
The number of levels of directory hashing to apply. When this attribute is set to positive value, filesystem realm will store identities in directory structure where the name of subdirectories will be derived from first characters of identity name. For example, location of identity named "alex" could bea/l/alex.xml
. This is useful not only because some filesystems can limit the amount of files that can be stored in a single directory, but also for performance reasons, because that might be influenced by the number of entries in a single directory as well. Default value is 2. -
encoded
When encoding is set to true, the identity names will be BASE32 encoded before they are used as filenames. This is beneficial, because some filesystems are case-insensitive or might restrict set of characters allowed in a filename. Default value is true. -
hash-encoding
specifies the string format for the password if it is not stored in plain text. Set to BASE64 bt default, but HEX is also supported. -
hash-charset
specifies the character set to use when converting the password string to a byte array. Set to UTF-8 by default. -
credential-store
specifies the credential store where the secret key is stored if the filesystem realm is to be encrypted. This attribute is optional. -
secret-key
specifies the alias of the secret key that is stored in the previously specified credential store. This attribute is optional. -
key-store
specifies the key store where the key pair is stored if the integrity is to be enabled on the filesystem realm. This attribute is optional. -
key-store-alias
specifies the alias of the key pair that is stored in the previously specified key store. This attribute is optional.
Filesystem security realm can be added with WildFly management CLI or with Elytron API.
WildFly management CLI
The following command creates realm with name fsRealm
that resides in directory fs-realm-users
inside the base configuration directory defined by jboss.server.config.dir
property. The levels number is 2 and encoded value is true.
The charset is UTF-8 and the string encoding is BASE64.
The credential-store
being referenced is called mycredstore
and the alias of the secret-key
in that credential-store
is key
. The secret key will be used to encrypt and decrypt the filesystem realm.
The key-store being referenced is called keystore
and the alias of the key pair in that key-store is localhost
. The localhost
key pair will be used to sign identity files and verify the pre-existing signatures to ensure that the filesystem realm has not been tampered with.
/subsystem=elytron/filesystem-realm=fsRealm:add(path=fs-realm-users, relative-to=jboss.server.config.dir, credential-store=mycredstore, secret-key=key, key-store=keystore, key-store-alias=localhost)
Although identities stored within the same realm can be hashed using different algorithms, they are all hashed using the same character set and stored using the same string encoding across the whole realm. |
To add identity with the name "alex" to this filesystem realm:
/subsystem=elytron/filesystem-realm=fsRealm:add-identity(identity=alex)
To delete existing identity from filesystem realm:
/subsystem=elytron/filesystem-realm=fsRealm:remove-identity(identity=alex)
It’s possible to update the key-store
or key-store-alias
to use for integrity checking using the update-key-pair
command as shown below. These commands will also update the signature in any existing identity files.
/subsystem=elytron/filesystem-realm=fsRealm:write-attribute(name=key-store, value=newKeystore)
/subsystem=elytron/filesystem-realm=fsRealm:write-attribute(name=key-store-alias, value=newKeystoreAlias)
/subsystem=elytron/filesystem-realm=fsRealm:update-key-pair()
Programmatic approach
When creating filesystem realm with Elytron API, you can specify name rewriter
in the constructor. The name rewriter will be applied to identity names to transform them from one form to another. It can be used to normalize the case, trim extra whitespaces, map one naming scheme to another, remove realm component from identity name (e.g. "user@realm" to "user") or to perform validation step on the syntax of the name.
To enable encryption for a filesystem realm programmatically, you can specify a SecretKey
when instantiating the realm.
To enable integrity for the filesystem realm programmatically, you can specify a PublicKey and PrivateKey when instantiating the realm.
A org.wildfly.security.auth.realm.FileSystemSecurityRealm
can be instantiated using a builder()
method as shown in the example below.
FileSystemSecurityRealm fsRealm = FileSystemSecurityRealm.builder()
.setRoot(fsRealmPath)
.setNameRewriter(IDENTITY_REWRITER)
.setLevels(2)
.setEncoded(true)
.setProviders(ELYTRON_PASSWORD_PROVIDERS)
.setSecretKey(secretKey)
.setPublicKey(publicKey)
.setPrivateKey(privateKey)
.build();
org.wildfly.security.auth.server.NameRewriter.IDENTITY_REWRITER
is a simple identity name rewriter that does no rewriting. You can implement the NameRewriter
interface and use your own rewriter.
Class org.wildfly.security.auth.principal.NamePrincipal
represents principal comprised of a simple name. Handle for the identity can be obtained by passing the NamePrincipal
instance to the getRealmIdentity
or getRealmIdentityForUpdate
method. Received identity for the given principal might or might not exist in the filesystem realm. Method exists
can be called to check whether the received identity exists. If it does not, you cannot modify its credentials or attributes.
To add non existent identity to the filesystem realm, create
method must be called. After this call, credentials and roles of this identity are empty. The exception will be thrown if method create
is called on an existing identity.
ModifiableRealmIdentity modifiableIdentity = fsRealm.getRealmIdentityForUpdate(new NamePrincipal("alex"));
if (!modifiableIdentity.exists()) {
modifiableIdentity.create();
}
To obtain read only instance of identity:
RealmIdentity identity = fsRealm.getRealmIdentity(identityPrincipal);
To obtain identity that can be modified:
ModifiableRealmIdentity modifiableIdentity = fsRealm.getRealmIdentityForUpdate(identityPrincipal);
ModifiableRealmIdentity handle must be cleaned up by a call to dispose
when the modifying is done:
modifiableIdentity.dispose();
To delete the identity from the realm:
identity.delete();
Credentials
Supported password types for identity in filesystem realm are Bcrypt
, Clear
, Simple Digest
, Salted Simple Digest
, Scram Digest
, Digest
and OTP
.
WildFly management CLI
To set clear password to the identity:
/subsystem=elytron/filesystem-realm=fsRealm:set-password(clear={password="alexPassword"}, identity=alex)
The above command will store password in clear text in identity’s file. It is not recommended to use clear passwords in a production set up.
To set digest password to the identity:
/subsystem=elytron/filesystem-realm=fsRealm:set-password(digest={algorithm=digest-md5,password="demoPassword",realm=demoRealm},identity=alex)
Note: Operation set-password` replaces any existing credential(s) with the new value.
Programmatic approach
When using Elytron API, working with passwords require interaction with the org.wildfly.security.password.PasswordFactory
API. Here is the simplest example that will store password in clear text in identity’s file. Examples on how to work with other types of passwords can be found in Passwords section of this documentation.
PasswordFactory passwordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR);
PasswordCredential clearPassword = new PasswordCredential(passwordFactory.generatePassword(new ClearPasswordSpec("alexPassword".toCharArray())));
identity.setCredentials(Collections.singleton(clearPassword));
Note: Operation setCredentials
replaces any existing credential(s) with the new value.
Attributes
Attributes associated with identities can be read and updated through CLI as well as programmatically.
WildFly management CLI
To add attribute to the identity, you have to specify the name of the attribute and its values (values are of type LIST):
/subsystem=elytron/filesystem-realm=fsRealm:add-identity-attribute(
identity="alex",
name=Email,
value=["alex@email.com", "alex_email@email.com"])
{"outcome" => "success"}
To read the identity with its attributes:
/subsystem=elytron/filesystem-realm=fsRealm:read-identity(identity="alex")
{
"outcome" => "success",
"result" => {
"name" => "alex",
"attributes" => {"Email" => [
"alex@email.com",
"alex_email@email.com"
]}
}
}
To remove individual values of the attribute:
/subsystem=elytron/filesystem-realm=fsRealm:remove-identity-attribute(
identity=alex,
name=Email,
value=[alex@email.com])
{"outcome" => "success"}
/subsystem=elytron/filesystem-realm=fsRealm:read-identity(identity="alex")
{
"outcome" => "success",
"result" => {
"name" => "alex",
"attributes" => {"Email" => ["alex_email@email.com"]}
}
}
To remove the whole attribute:
/subsystem=elytron/filesystem-realm=fsRealm:remove-identity-attribute(identity=alex, name=Email)
{"outcome" => "success"}
/subsystem=elytron/filesystem-realm=fsRealm:read-identity(identity=alex)
{
"outcome" => "success",
"result" => {
"name" => "alex",
"attributes" => undefined
}
}
Programmatic approach
Interface org.wildfly.security.authz.Attributes
represents collection of string attributes. To get attributes associated with specific identity:
Attributes identityAttributes = identity.getAttributes();
To update attributes you can use ModifiableRealmIdentity
instance. Class org.wildfly.security.authz.MapAttributes
represents collection of attributes backed by java.util.Map
:
ModifiableRealmIdentity modifiableIdentity = fsRealm.getRealmIdentityForUpdate(identityPrincipal);
MapAttributes attributes = new MapAttributes();
attributes.addLast("email","alex@email.com");
modifiableIdentity.setAttributes(attributes);
modifiableIdentity.dispose();
Converting legacy properties file into Filesystem realm
WildFly can use authentication with a properties file based identity store. One properties file maps users to passwords and another maps users to roles. You can use Elytron Tool
to convert these properties files into a filesystem realm. Below is an example of how to run this tool from the command line:
$JBOSS_HOME/bin/elytron-tool.sh filesystem-realm -u conf/users.properties -r conf/roles.properties --output-location realms/example --summary -f example-fs-realm
This command creates new filesystem realm with users taken from users.properties file and roles taken from roles.properties file. Script example-fs-realm.sh
that contains the commands for WildFly CLI is generated as well. The script adds this filesystem realm to the Elytron subsystem and also adds new security domain that uses this filesystem realm as a default realm.
Converting an unencrypted filesystem realm into an encrypted filesystem realm
It is possible to convert an unencrypted filesystem realm into an encrypted one using the Elytron Tool. In particular, the filesystem-realm-encrypt
command can be used as shown below:
$JBOSS_HOME/bin/elytron-tool.sh filesystem-realm-encrypt -i ./standalone/configuration/fs-realm-plain -o ./standalone/configuration/fs-realm-enc -c ./mycredstore.cs
This command creates a new filesystem realm by taking the existing filesystem realm from the specified input location, ./standalone/configuration/fs-realm-plain
, and encrypting its contents using the secret key with alias key
from the specified credential store, ./mycredstore.cs
. The new filesystem realm is stored in the specified output location, ./standalone/configuration/fs-realm-enc
.
A .cli
script will also be generated at the root of the filesystem realm. The script contains WildFly CLI commands that can be used to configure a secret-key-credential-store
resource and a filesystem-realm
resource in the Elytron subsystem that makes use of the newly encrypted realm content.
Adding Integrity Checking for an Existing Filesystem Realm
Integrity checking can also be added to an existing filesystem realm using the WildFly Elytron Tool. This takes an existing filesystem realm and creates a new filesystem realm with integrity checking support. The integrity checking uses a public and private key pair. The key pair will be used to create a new filesystem realm where each identity will be signed using the private key. This makes it possible to verify that identities in the realm haven’t been tampered with.
The feature also creates a CLI script that can be used to add a key-store resource and a filesystem-realm resource in the Elytron subsystem that makes use of the newly signed realm content.
In order to use this feature, we can make use of the elytron-tool.sh
(or .bat for windows) script. The format for the command to add integrity checking is as follows:
$ WILDFLY_HOME/bin/elytron-tool.sh filesystem-realm-integrity --input-location <existing_path> --output-location <new_path> --keystore <ks_path> --password <ks_password> --summary
The required options include:
-
input-location: This is the path for the existing filesystem realm that does not have an integrity checking. This must be the path to the directory containing the filesystem. This can also be specified using
-i
. This must be an existing path. -
output-location: This attribute will be the path for the new filesystem realm. This filesystem realm will include integrity checking. This can also be specified using
-o
. This path is also a path to a directory. -
keystore: Path for the keystore that holds the key pair for integrity checking. Can also be specified using
-k
. -
password: This is the password for the keystore. This can also be specified using
-p
.
The elytron tool also allows for the conversion to be done in bulk, as in multiple filesystems can be converted all at once. This can be done using the following command:
$ WILDFLY_HOME/bin/elytron-tool.sh filesystem-realm-integrity --bulk-convert <descriptor>
In this case <descriptor>
is a file, which contains the instructions for bulk conversion. An example of a descriptor file is shown below. Notice that each block contains the options to use for converting a single realm. Additionally, in addition to specifying the input-location
, we have specified the realm-name
as well.
input-location:standalone/configuration/unsigned-realm-one
output-location:standalone/configuration/signed
keystore:myKeyStore
password:myPassword
key-pair:myKeyPair
realm-name:realm-one
input-location:standalone/configuration/unsigned-realm-two
output-location:standalone/configuration/signed
keystore:myKeyStore
password:myPassword
key-pair:myKeyPair
realm-name:realm-two
There are other options that can be specified for converting a filesystem realm, the details of which can be found using the ./bin/elytron-tool.sh filesystem-realm-integrity -h
command.
Updating WildFly Elytron Tool Generated CLI Scripts for Domain Mode
As mentioned above, some of the WildFly Elytron Tool commands create a CLI script that can be used to add resources to the Elytron subsystem. Some examples of these CLI scripts include:
-
The <FILESYSTEM-REALM-NAME>.cli script generated when the
filesystem-realm-encrypt
command is used to encrypt an existing filesystem realm. -
The <FILESYSTEM-REALM-NAME>.cli script generated when the
filesystem-realm-integrity
command is used to add integrity checking to existing filesystem realms. -
The <FILESYSTEM-REALM-NAME>.sh script generated when the
filesystem-realm
command is used to create a filesystem realm from a legacy properties file.
However, the generated CLI script can only be used directly if the server is running in Standalone mode. For Domain mode the script needs to be updated. The steps for updating the script are as follows:
-
Update the file contents to add a /profile=<PROFILE_NAME> in front of all occurrences of the /subsystem=… strings as found in the file (so in front of /subsystem=elytron…); where <PROFILE_NAME> is the profile name that you are using in your WildFly domain.xml (Hence, <PROFILE_NAME> can be either default, ha, full, full-ha, or any other profile-name defined and used in the domain.xml.)
-
Save and Exit the file editor.
Now you can use the CLI script to update server configurations in domain mode.
Please note that this same basic procedure can also apply to a Host Controller configuration (i.e. host.xml content).
However, in that case, instead of specifying profile=<PROFILE_NAME>
, we would use host=<HC_NAME>
to specify the Host
Controller name.
4.5.6. JAAS Security Realm
A JAAS realm utilizes a LoginContext initialized from a JAAS configuration file to authenticate and authorize users with custom Login Modules. Flags and options can be specified in a JAAS configuration file according to the Java documentation.
The jaas-realm
resource contains the following attributes: -
-
entry
JAAS configuration file entry name -
path
Path to the JAAS configuration file. You can also specify the location of the configuration with java system property "java.security.auth.login.config" or with java security property "login.config.url" -
relative-to
Optional base folder for thepath
. -
module
The WildFly module with Login Module implementations and Callback Handler implementation. -
callback-handler
Callback handler to use with the Login Context. Security property "auth.login.defaultCallbackHandler" can be used instead. The default callback handler of the realm will be used if none of these are defined.
All attributes but entry
are optional.
Example of jaas-realm
configuration via WildFly CLI:
/subsystem=elytron/jaas-realm=theRealm:add(
entry=Entry1,
path=/path/to/jaas/config/JAASConfig.conf,
module=myLoginModule,
callback-handler=my.custom.MyCallbackHandler)
Subject’s Principals to attributes mapping
Login Modules use Subjects to represent the user currently being authenticated. Subject’s principals are mapped to user’s attributes with the following rule:
-
key of the attribute is principal’s simple classname, so the value of
principal.getClass().getSimpleName())
-
value is principal’s name, so the result of
principal.getName()
call. For principals of the same type / key, the values will be appended to the collection under this attribute key.
Attributes can be used to associate roles in the Elytron subsystem. Roles
is the default attribute name used for this purpose. You can configure a role-decoder
to use a different attribute. This means you can add roles to authenticated user by associating principals with the Subject in a Login Module imlpementations.
4.5.7. JDBC Security Realm
The JDBC security realm is a security realm developed to support loading identities from a database with the option of multiple credentials and multiple attributes each with the option of containing multiple values.
When defining the JDBC security realm one or more principal queries can be defined, each of these can load a credential and / or attributes for the resulting identity. Each defined principal query is associated with it’s own datasource, this means quite a complex configuration can be created loading the different aspects of an identity from multiple locations.
Each of the examples documented within this section will be making use of pre-configured datasources, please refer to the datasources subsystem documentation for more information relating to how to define datasources.
Loading a Single Clear Text Password
The simplest configuration is to load a clear text password for an identity. This approach would not be recommended at all in a production set up, however it does make a suitable starting point to illustrate how the JDBC security realm can be configured.
NAME | PASSWORD |
---|---|
test |
myPassword |
A JDBC security realm can be defined as: -
/subsystem=elytron/jdbc-realm=demo-realm:add(
principal-query=[{data-source=Identities,
sql="select PASSWORD from IDENTITIES where NAME = ?",
clear-password-mapper={password-index=1}}])
This realm is defined within a single principal-query
against the Identities
datasource. For the user test
the result of the query would be: -
1 |
---|
myPassword |
The principal query can be defined with password mappers which define which columns should be used to construct the password for the identity being loaded, in this example a clear-password-mapper
is used: -
clear-password-mapper={password-index=1}
The index of the first column is 1 |
Alternative Password Mappers
The WildFly Elytron project supports a variety of password types as described within Passwords, some of these password types require multiple values to be loaded from the database to reconstruct the password so alternative password mappers are made available. More than one password mapper can be defined on a single principal-query
to support loading multiple passwords simultaneously.
Where the various password mappers load encoded representations of passwords and salts from the database these can either be encoded using Base64 or Hexadecimal, by default unless specified Base64 is assumed. |
clear-password-mapper
This mapper is used to load a clear text password directly from the database.
The following attribute is supported for this password mapper: -
-
password-index
- The index of the column containing the clear text password.
bcrypt-password-mapper
The bcrypt-password-mapper
can be used for passwords to be loaded using the bcrypt algorithm, as an iterated salted password type the iteration count and salt are also loaded from the database query.
-
password-index
- The index of the column containing the encoded password. -
hash-encoding
- The encoding of the hash, eitherbase64
orhex
. -
salt-index
- The index of the column containing the encoded salt. -
salt-encoding
- The encoding of the salt, eitherbase64
orhex
. -
iteration-count-index
- The index of the column containing the iteration count.
modular-crypt-mapper
The modular-crypt-mapper
can be used for passwords encoded using modular crypt, this encoding allows for multiple pieces of information to be encoded in single String such as the password type, the hash or digest, the salt, and the iteraction count.
Information on how to encode and decode modular crypt representations can be seen in Modular Crypt Encoding.
The following attribute is supported for this password mapper: -
-
password-index
- The index of the column containing the modular crypt encoded password.
salted-simple-digest-mapper
The salted-simple-digest-mapper
supports the password types hashed with a salt as described in Salted Digest, for this type of password the encoded form of the password is loaded in addition to the salt.
-
algorithm
- The algorithm of the password type, the supported values are listed at Salted Digest. -
password-index
- The index of the column containing the encoded password. -
hash-encoding
- The encoding of the hash, eitherbase64
orhex
. -
salt-index
- The index of the column containing the encoded salt. -
salt-encoding
- The encoding of the salt, eitherbase64
orhex
.
simple-digest-mapper
The simple-digest-mapper
supports the loading of passwords which have been simply hashed without any salt as described in Simple Digest.
-
algorithm
- The algorithm of the password type, the supported values are listed at Simple Digest. -
password-index
- The index of the column containing the encoded password. -
hash-encoding
- The encoding of the hash, eitherbase64
orhex
.
scram-mapper
The scram-mapper
supports the loading of SCRAM passwords which use both a salt and an interation count as described in Scram.
-
algorithm
- The algorithm of the password type, the supported values are listed at Scram. -
password-index
- The index of the column containing the encoded password. -
hash-encoding
- The encoding of the hash, eitherbase64
orhex
. -
salt-index
- The index of the column containing the encoded salt. -
salt-encoding
- The encoding of the salt, eitherbase64
orhex
. -
iteration-count-index
- The index of the column containing the iteration count.
Hash Character Sets
The various password mappers allow loading multiples values from the database to hash the client provided password in order to compare against the password stored in the database.
The JDBC realm supports specifying the character set via the attribute hash-charset
to use when converting
the client provided password string to a byte array. This is useful when our database is storing
hashed passwords using a charset other than UTF-8
, as the JDBC realm assumes that is the charset being used by default.
Although more than one password mapper can be defined on a single principal-query , only one hash-charset
can be defined across the whole realm.
|
For example, the following JDBC realm is configured using the GB2312 charset:
/subsystem=elytron/jdbc-realm=exampleDbRealm:add(principal-query=[{sql="SELECT password FROM all_users WHERE user=?",data-source=exampleDS,simple-digest-mapper={algorithm=password-salt-digest-md5,password-index=1}}])
Using a Hashed Password Representation
The same approach can be taken for all hashed password representations, for illustration purposes this section will illustrate how a bcrypt password can be prepared to be stored in a database and the subsequent realm configuration to make use of it. Examples uising the APIs for the different password types can be found in the Passwords section of this documentation.
The following example takes the password myPassword
, generates a random salt an produces a bcrypt
representation of the password.
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "myPassword";
public static void main(String[] args) throws Exception {
PasswordFactory passwordFactory = PasswordFactory.getInstance(BCryptPassword.ALGORITHM_BCRYPT, ELYTRON_PROVIDER);
int iterationCount = 10;
byte[] salt = new byte[BCryptPassword.BCRYPT_SALT_SIZE];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
IteratedSaltedPasswordAlgorithmSpec iteratedAlgorithmSpec = new IteratedSaltedPasswordAlgorithmSpec(iterationCount, salt);
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), iteratedAlgorithmSpec);
BCryptPassword original = (BCryptPassword) passwordFactory.generatePassword(encryptableSpec);
byte[] hash = original.getHash();
Encoder encoder = Base64.getEncoder();
System.out.println("Encoded Salt = " + encoder.encodeToString(salt));
System.out.println("Encoded Hash = " + encoder.encodeToString(hash));
}
This produces the following output, as the salt is randomly generated the output would differ each time the above code is executed.
Encoded Salt = 3bFOQwRU75to+yJ8Cv0g8w==
Encoded Hash = x9P/0cxfNz+Pf3HCinZ3dLCbNMnBeiU=
This could now be stored in a database table: -
NAME | PASSWORD | SALT | ITERATION_COUNT |
---|---|---|---|
test |
x9P/0cxfNz+Pf3HCinZ3dLCbNMnBeiU= |
3bFOQwRU75to+yJ8Cv0g8w== |
10 |
The JDBC security realm can instead be created with the following CLI command: -
/subsystem=elytron/jdbc-realm=demo-realm:add(
principal-query=[{data-source=Identities,
sql="select PASSWORD, SALT, ITERATION_COUNT from IDENTITIES where NAME = ?",
bcrypt-mapper={password-index=1, salt-index=2, iteration-count-index=3}}])
For the user test
the result of the query would be: -
1 | 2 | 3 |
---|---|---|
x9P/0cxfNz+Pf3HCinZ3dLCbNMnBeiU= |
3bFOQwRU75to+yJ8Cv0g8w== |
10 |
The bcrypt-password-mapper
is defined to load the encoded password, encoded salt and iteration count from the relevent columns in the query result.
bcrypt-mapper={password-index=1, salt-index=2, iteration-count-index=3}
Had the values been encoding using hexadecimal instead of Base64 the bcrypt-mapper
could have been defined as: -
bcrypt-mapper={password-index=1, hash-encoding=hex, salt-index=2, salt-encoding=hex, iteration-count-index=3}
It is worth noting that as the hash-encoding and salt-encoding are specified separately one could use Base64 whilst the other uses hexadecimal.
|
Loading Passwords from Different Queries / Datasources
It is also possible to combine both of the example so far and define two separate principal-query
instances to attempt to load both password types from different locations.
Here is an example configuration loading a clear text password from one datasource / table and loading a bcrypt password from a second datasource / table.
/subsystem=elytron/jdbc-realm=demo-realm:add(
principal-query=[
{data-source=LegacyIdentities,
sql="select PASSWORD from LEGACY_IDENTITIES where NAME = ?",
clear-password-mapper={password-index=1}},
{data-source=NewIdentities,
sql="select PASSWORD, SALT, ITERATION_COUNT from NEW_IDENTITIES where NAME = ?",
bcrypt-mapper={password-index=1, salt-index=2, iteration-count-index=3}}
])
It is not required that the identity is found from both of the queries, this can be useful in situations where identities are being migrated from one location to another or for aggregating two together. |
Loading Attributes
The examples so far have focussed on the loading of passwords from the database, the principal queries can also be used to load attributes for the resulting identities.
The loading of attributes can either be defined to happen within the principal queries being used to load the passwords or attribute specific principal queries can be defined, as each principal-query
can be defined with it’s own datasource
reference this means attributes can also be loaded from alternative locations.
The loaded attributes can then be used for mapping to roles and permissions which should be granted to the identity or they can be obtained programatically within the deployment to identify information about the currently authenticated identity.
Loading Attributes with Passwords
For single valued attributes these can often be loaded using the same principal-query
used to load an identities password, as an example if an identities e-mail address or department is to be loaded from the database these can be loaded at the same time as the password.
A table for this example could look like: -
NAME | PASSWORD | E_MAIL | Department |
---|---|---|---|
test |
myPassword |
Sales |
The realm can now be defined as: -
/subsystem=elytron/jdbc-realm=demo-realm:add(
principal-query=[
{data-source=Identities,
sql="select PASSWORD, E_MAIL, DEPARTMENT from IDENTITIES where NAME = ?",
clear-password-mapper={password-index=1},
attribute-mapping=[{index=2, to=email},{index=3,to=department}]
}])
For the user test
the result of the query would be: -
1 | 2 | 3 |
---|---|---|
myPassword |
Sales |
The configuration contained the following attribute mappings: -
attribute-mapping=[{index=2, to=email},{index=3,to=department}]
This means the contents of column 2 will be mapped to the email
attribute and the contents of column 3 will be mapped to the department
attribute.
Loading Attributes Separately.
For multi-valued attributes such as a list of groups it can often make sense to define a separate principal query.
A list of groups could be represented as follows in a table.
NAME | TEAM |
---|---|
test |
Users |
test |
Supervisors |
A realm can now be defined with a second principal query to load the groups into an attribute.
/subsystem=elytron/jdbc-realm=demo-realm:add(
principal-query=[
{data-source=Identities,
sql="select PASSWORD from IDENTITIES where NAME = ?",
clear-password-mapper={password-index=1}
},{data-source=Identities,
sql="select TEAM from MEMBERSHIP where NAME = ?",
attribute-mapping=[{index=1, to=groups}]
}])
Within this definition the second principal-query
will load the attribute groups
: -
{data-source=Identities,
sql="select TEAM from MEMBERSHIP where NAME = ?",
attribute-mapping=[{index=1, to=groups}]
}
For the user test
the results would be: -
1 |
---|
Users |
Supervisors |
The end result would be that the identity contains the attribute groups
with the values Users
, and Supervisors
5. Using Elytron within WildFly
5.1. Using the Out of the Box Elytron Components
5.1.1. Securing Management Interfaces
You can find more details on the enabling WildFly to use the out of the box Elytron components for securing the management interfaces in the Default Management Authentication Configuration section.
5.1.2. Securing Applications
The elytron subsystem provides application-security-domain by default which can be used to secure applications. For more details on how application-security-domain is configured, see the Default Application Authentication Configuration section.
To configure applications to use application-security-domain, see Set Up and Configure Authentication for Applications. You can also override the default behavior of all applications using the steps in Override an Application’s Authentication Configuration.
5.1.3. Using SSL/TLS
WildFly does provide a default one-way SSL/TLS configuration using the legacy core management authentication but does not provide one in the elytron subsystem. You can find more details on configuring SSL/TLS using the elytron subsystem for both the management interfaces as well as for applications in Configure SSL/TLS
5.1.4. Using Elytron with Other Subsystems
In addition to securing applications and management interfaces, Elytron also integrates with other subsystems in WildFly.
Subsystem | Details |
---|---|
batch-jberet |
You can configure the batch-jberet to run batch jobs using an Elytron security domain. |
datasources |
You can use a credential store or an Elytron security domain to provide authentication information in a datasource definition. |
messaging-activemq |
You can secure remote connections to the remote connections used by the messaging-activemq subsystem. |
iiop-openjdk |
You can use the elytron subsystem to configure SSL/TLS between clients and servers using the iiop-openjdk subsystem. |
You can use a credential store to provide authentication information in a server definition in the mail subsystem. |
|
undertow |
You can use the elytron subsystem to configure both SSL/TLS and application authentication. |
5.2. Undertow Subsystem
As a web application is deployed the name of the security domain required by that application will be identified, this will either be from within the deployment or if the deployment does not have a security domain the default-security-domain
as defined on the Undertow subsystem will be assumed. An application-security-domain
resource can be added to the Undertow subsystem which maps from the name of the security domain required by the application to the appropriate WildFly Elytron configuration.
As an example in it’s simplest form a mapping can be added as: -
/subsystem=undertow/application-security-domain=MyAppSecurity:add(security-domain=ApplicationDomain)
Which results in: -
<subsystem xmlns="urn:jboss:domain:undertow:7.0" ... default-security-domain="other">
...
<application-security-domains>
<application-security-domain name="MyAppSecurity" security-domain="ApplicationDomain"/>
</application-security-domains>
...
</subsystem>
Note: If the deployment was already deployed at this point the application server should be reloaded or the deployment redeployed for the application security domain mapping to take effect.
Here we are mapping from the applications security domain MyAppSecurity
to the WildFly Elytron defined domain ApplicationDomain
.
This simple form is suitable where a deployment is using the standard HTTP mechanisms as defined within the Servlet specification i.e. BASIC
, CLIENT_CERT
, DIGEST
, FORM
and authentication will be performed against the ApplicationDomain
security domain. This form is also suitable where an application is not using any authentication mechanisms and instead is using programatic authentication or even wishes to obtain the SecurityDomain associated with the deployment and use it directly.
An advanced form of the mapping can be added as: -
/subsystem=undertow/application-security-domain=MyAppSecurity:add(http-authentication-factory=application-http-authentication)
Which results in: -
<subsystem xmlns="urn:jboss:domain:undertow:7.0" ... default-security-domain="other">
...
<application-security-domains>
<application-security-domain name="MyAppSecurity" http-authentication-factory="application-http-authentication"/>
</application-security-domains>
...
</subsystem>
In this form of the configuration instead of referencing a security domain a http-authentication-factory
is referenced instead, this is the factory that will be used to obtain the instances of the authentication mechansisms and is in turn associated with the security domain. The standard mechanisms as defined in the Servlet specification can be used in this way but this approach also allows for other mechanisms to be used such as SPNEGO
which requires additional configuration or even plug-in custom mechanism implementations.
When the advanced form of the mapping is used a further configuration option is available: -
-
override-deployment-config
The referenced http-authentication-factory
can return a complete set of authentication mechanisms, by default these are filtered to just match the mechanisms requested by the application - if this option is set to true
then the mechanisms offered by the factory will override the mechanisms requested by the application. One example of where this could be useful is say an application has been developed to support FORM
authentication, by overriding the mechanisms the application could be updated to support SPNEGO
, and FORM
authentication without any modifications to the deployment.
The application-security-domain
resource also has one additional option enable-jacc
, if this is set to true JACC will be enabled for any deployments matching against this mapping.
5.2.1. Runtime Information
Where an application-security-domain
mapping is in use it can be useful to double check that deployments did match against it as expected, if the resource is read with include-runtime=true
the deployments that are associated with the mapping will also be shown: -
[standalone@localhost:9990 /] /subsystem=undertow/application-security-domain=MyAppSecurity:read-resource(include-runtime=true) { "outcome" => "success", "result" => { "enable-jacc" => false, "http-authentication-factory" => undefined, "override-deployment-config" => false, "referencing-deployments" => ["simple-webapp.war"], "security-domain" => "ApplicationDomain", "setting" => undefined } }
In this output the referencing-deployments
attribute shows that the deployment simple-webapp.war
has been deployed using this mapping.
5.3. Jakarta Enterprise Beans Subsystem
Configuration can be added to the Jakarta Enterprise Beans subsystem to map a security domain name referenced in a deployment to an Elytron security domain:
/subsystem=ejb3/application-security-domain=MyAppSecurity:add(security-domain=ApplicationDomain)
Which results in:
<subsystem xmlns="urn:jboss:domain:ejb3:5.0">
...
<application-security-domains>
<application-security-domain name="MyAppSecurity" security-domain="ApplicationDomain"/>
</application-security-domains>
...
</subsystem>
Note: If the deployment was already deployed at this point the application server should be reloaded or the deployment redeployed for the application security domain mapping to take effect.
An application-security-domain
has two main attributes:
-
name - the name of the security domain as specified in a deployment
-
security-domain - a reference to the Elytron security domain that should be used
There is another attribute called legacy-compliant-principal-propagation
.
If it is set to true and there is no incoming run-as identity,
then the principal of the local unsecured bean is the current authenticated identity. This was the case
for legacy PicketBox security. If this attribute is set to false, the behaviour will comply with the Elytron’s previous
behaviour and if there is no incoming run-as identity then the principal of the local unsecured bean is anonymous.
This attribute is optional and the default value is true.
When an application security domain mapping is configured for a bean in a deployment, this indicates that security should be handled by Elytron.
5.4. WebServices Subsystem
There is adapter in webservices subsystem to make authentication works for elytron security domain automatically. Like configure with legacy security domain, you can configure elytron security domain in deployment descriptor or annotation to secure webservice endpoint.
When Elytron security is enabled, JAAS subject or principal can be pushed to jbossws-cxf endpoint’s SecurityContext to propagate authenticated identity to Jakarta Enterprise Beans container. Here is a CXF interceptor example to propagate authenticated information to Jakarta Enterprise Beans container :
public class PropagateSecurityInterceptor extends WSS4JInInterceptor {
public PropagateSecurityInterceptor() {
super();
getAfter().add(PolicyBasedWSS4JInInterceptor.class.getName());
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
...
final Endpoint endpoint = message.getExchange().get(Endpoint.class);
final SecurityDomainContext securityDomainContext = endpoint.getSecurityDomainContext();
//push subject principal retrieved from CXF to ElytronSecurityDomainContext
securityDomainContext.pushSubjectContext(subject, principal, null)
}
}
6. Client Authentication with Elytron Client
WildFly Elytron uses the Elytron Client project to enable remote clients to authenticate using Elytron. Elytron Client has the following components:
Component | Description |
---|---|
Authentication Configuration |
Contains authentication information such as usernames, passwords, allowed SASL mechanisms, and the security realm to use during digest authentication. |
MatchRule |
Rule used for deciding which authentication configuration to use. |
Authentication Context |
Set of rules and authentication configurations to use with a client for establishing a connection. |
When a connection is established, the client makes use of an authentication context, which gives rules that match which authentication configuration is used with an outbound connection. For example, you could have a rule that use one authentication configuration when connecting to server1 and another authentication configuration when connecting with server2. The authentication context is comprised of a set of authentication configurations and a set of rules that define how they are selected when establishing a connection. An authentication context can also reference ssl-context and can be matched with rules.
To create a client that uses security information when establishing a connection:
-
Create one or more authentication configurations.
-
Create an authentication context by creating rule and authentication configuration pairs.
-
Create a runnable for establishing your connection.
-
Use your authentication context to run your runnable.
When you establish your connection, Elytron Client will use the set of rules provided by the authentication context to match the correct authentication configuration to use during authentication.
You can use one of the following approaches to use security information when establishing a client connection.
IMPORTANT: When using Elytron Client to make Jakarta Enterprise Beans calls, any hard-coded programatic authentication information, such as setting Context.SECURITY_PRINCIPAL in the javax.naming.InitialContext, will override the Elytron Client configuration.
6.1. The Configuration File Approach
The configuration file approach involves creating an XML file with your authentication configuration, authentication context, and match rules.
custom-config.xml
<configuration>
<authentication-client xmlns="urn:elytron:1.0">
<authentication-rules>
<rule use-configuration="monitor">
<match-host name="127.0.0.1" />
</rule>
<rule use-configuration="administrator">
<match-host name="localhost" />
</rule>
</authentication-rules>
<authentication-configurations>
<configuration name="monitor">
<allow-sasl-mechanisms names="DIGEST-MD5" />
<use-service-loader-providers />
<set-user-name name="monitor" />
<credentials>
<clear-password password="password1!" />
</credentials>
<set-mechanism-realm name="ManagementRealm" />
</configuration>
<configuration name="administrator">
<allow-sasl-mechanisms names="DIGEST-MD5" />
<use-service-loader-providers />
<set-user-name name="administrator" />
<credentials>
<clear-password password="password1!" />
</credentials>
<set-mechanism-realm name="ManagementRealm" />
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
The above example shows how the password for authentication can be specified in the clear. However, it’s also possible to use a masked password instead.
custom-config.xml
<configuration>
<authentication-client xmlns="urn:elytron:1.4">
<authentication-rules>
<rule use-configuration="monitor">
<match-host name="127.0.0.1" />
</rule>
<rule use-configuration="administrator">
<match-host name="localhost" />
</rule>
</authentication-rules>
<authentication-configurations>
<configuration name="monitor">
<allow-sasl-mechanisms names="DIGEST-MD5" />
<use-service-loader-providers />
<set-user-name name="monitor" />
<credentials>
<masked-password iteration-count="100" salt="12345678" masked-password="/Nym2s/dssMrabfdIGsZfQ==" />
</credentials>
<set-mechanism-realm name="ManagementRealm" />
</configuration>
<configuration name="administrator">
<allow-sasl-mechanisms names="DIGEST-MD5" />
<use-service-loader-providers />
<set-user-name name="administrator" />
<credentials>
<masked-password iteration-count="100" salt="12345678" masked-password="/Nym2s/dssMrabfdIGsZfQ==" />
</credentials>
<set-mechanism-realm name="ManagementRealm" />
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
You can then reference that file in your client’s code by setting a system property when running your client.
$ java -Dwildfly.config.url=/path/to/the.xml .....
IMPORTANT: If you use the The Programmatic Approach, it will override any provided configuration files even if the wildfly.config.url system property is set.
When creating rules, you can look for matches on various parameters such as hostname, port, protocol, or username. A full list of options for MatchRule are available in the Javadocs. Rules are evaluated in the order in which they are configured.
Common Rules
Rule | Description |
---|---|
match-domain |
Takes a single name attribute specifying the security domain to match against. |
match-host |
Takes a single name attribute specifying the hostname to match against. For example, the host 127.0.0.1 would match on http://127.0.0.1:9990/my/path . |
match-no-user |
Matches against URIs with no user. |
match-path |
Takes a single name attribute specifying the path to match against. For example, the path /my/path/ would match on http://127.0.0.1:9990/my/path . |
match-port |
Takes a single name attribute specifying the port to match against. For example, the port 9990 would match on http://127.0.0.1:9990/my/path . |
match-protocol |
Takes a single name attribute specifying the protocol to match against. For example, the protocol http would match on http://127.0.0.1:9990/my/path . |
match-purpose |
Takes a names attribute specifying the list of purposes to match against. |
match-urn |
Takes a single name attribute specifying the URN to match against. |
match-user |
Takes a single name attribute specifying the user to match against. |
6.2. The Programmatic Approach
The programatic approach configures all the Elytron Client configuration in the client’s code:
//create your authentication configuration
AuthenticationConfiguration adminConfig =
AuthenticationConfiguration.empty()
.useProviders(() -> new Provider[] { new WildFlyElytronProvider() })
.allowSaslMechanisms("DIGEST-MD5")
.useRealm("ManagementRealm")
.useName("administrator")
.usePassword("password1!");
//create your authentication context
AuthenticationContext context = AuthenticationContext.empty();
context = context.with(MatchRule.ALL.matchHost("127.0.0.1"), adminConfig);
//create your runnable for establishing a connection
Runnable runnable =
new Runnable() {
public void run() {
try {
//Establish your connection and do some work
} catch (Exception e) {
e.printStackTrace();
}
}
};
//use your authentication context to run your client
context.run(runnable);
The above example shows how the password for authentication can be specified in the clear. However, it’s also possible to use a masked password instead.
//create your authentication configuration
AuthenticationConfiguration adminConfig =
AuthenticationConfiguration.empty()
.useProviders(() -> new Provider[] { new WildFlyElytronProvider() })
.allowSaslMechanisms("DIGEST-MD5")
.useRealm("ManagementRealm")
.useName("administrator")
.usePassword("/Nym2s/dssMrabfdIGsZfQ==", null, null, "100", "12345678", null);
//create your authentication context
AuthenticationContext context = AuthenticationContext.empty();
context = context.with(MatchRule.ALL.matchHost("127.0.0.1"), adminConfig);
//create your runnable for establishing a connection
Runnable runnable =
new Runnable() {
public void run() {
try {
//Establish your connection and do some work
} catch (Exception e) {
e.printStackTrace();
}
}
};
//use your authentication context to run your client
context.run(runnable);
When adding configuration details to AuthenticationConfiguration and AuthenticationContext, each method call returns a new instance of that object. For example, if you wanted separate configurations when connecting over different hostnames, you could do the following:
//create your authentication configuration
AuthenticationConfiguration commonConfig =
AuthenticationConfiguration.empty()
.useProviders(() -> new Provider[] { new WildFlyElytronProvider() })
.allowSaslMechanisms("DIGEST-MD5")
.useRealm("ManagementRealm");
AuthenticationConfiguration administrator =
commonConfig
.useName("administrator")
.usePassword("password1!");
AuthenticationConfiguration monitor =
commonConfig
.useName("monitor")
.usePassword("password1!");
//create your authentication context
AuthenticationContext context = AuthenticationContext.empty();
context = context.with(MatchRule.ALL.matchHost("127.0.0.1"), administrator);
context = context.with(MatchRule.ALL.matchHost("localhost"), monitor);
Common Rules
Rule | Description |
---|---|
matchLocalSecurityDomain(String name) |
This is the same as match-domain in the configuration file approach. |
matchNoUser() |
This is the same as match-no-user in the configuration file approach. |
matchPath(String pathSpec) |
This is the same as match-path in the configuration file approach. |
matchPort(int port) |
This is the same as match-port in the configuration file approach. |
matchProtocol(String protoName) |
This is the same as match-port in the configuration file approach. |
matchPurpose(String purpose) |
Create a new rule which is the same as this rule, but also matches the given purpose name. |
matchPurposes(String… purposes) |
This is the same as match-purpose in the configuration file approach. |
matchUrnName(String name) |
This is the same as match-urn in the configuration file approach. |
matchUser(String userSpec) |
This is the same as match-user in the configuration file approach. |
Also, instead of starting with an empty authentication configuration, you can start with the current configured one by using captureCurrent().
//create your authentication configuration
AuthenticationConfiguration commonConfig = AuthenticationConfiguration.captureCurrent();
Using captureCurrent() will capture any previously established authentication context and use it as your new base configuration. An authentication context is established once it’s been activated by calling run(). If captureCurrent() is called and no context is currently active, it will try and use the default authentication if available. You can find more details about this in The Configuration File Approach, The Default Configuration Approach, and Using Elytron Client with Clients Deployed to WildFly sections.
Using AuthenticationConfiguration.empty() should only be used as a base to build a configuration on top of and should not be used on its own. It provides a configuration that uses the JVM-wide registered providers and enables anonymous authentication.
When specifying the providers on top of the AuthenticationConfiguration.empty() configuration, you can specify a custom list, but most users should use WildFlyElytronProvider() providers.
When creating an authentication context, using the context.with(…) will create a new context that merges the rules and authentication configuration from the current context with the provided rule and authentication configuration. The provided rule and authentication configuration will appear after the ones in the current context.
6.3. The Default Configuration Approach
The default configuration approach relies completely on the configuration provided by Elytron Client:
//create your runnable for establishing a connection
Runnable runnable =
new Runnable() {
public void run() {
try {
//Establish your connection and do some work
} catch (Exception e) {
e.printStackTrace();
}
}
};
// run runnable directly
runnable.run();
To provide a default configuration, Elytron Client tries to auto-discover a wildfly-config.xml file on the filesystem. It looks in the following locations:
-
Location specified by the wildfly.config.url system property set outside of the client code.
-
The classpath root directory.
-
The META-INF directory on the classpath.
If it does not find one, it will try and use the default wildfly-config.xml provided in the $WILDFLY_HOME/bin/client/jboss-client.jar.
default wildfly-config.xml
<configuration>
<authentication-client xmlns="urn:elytron:1.0">
<authentication-rules>
<rule use-configuration="default" />
</authentication-rules>
<authentication-configurations>
<configuration name="default">
<allow-all-sasl-mechanisms />
<set-mechanism-properties>
<property key="wildfly.sasl.local-user.quiet-auth" value="true" />
</set-mechanism-properties>
<use-service-loader-providers />
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
6.4. Using Elytron Client with Clients Deployed to WildFly
Clients deployed to WildFly can also make use of Elytron Client. In cases where you have included a wildfly-config.xml with your deployment or the system property has been set, an AuthenticationContext is automatically parsed and created from that file.
To load a configuration file outside of the deployment, you can use the parseAuthenticationClientConfiguration(URI) method. This method will return an AuthenticationContext which you can then use in your client’s code using the The Programmatic Approach.
Additionally, clients will also automatically parse and create an AuthenticationContext from the client configuration provided by the elytron subsystem. The client configuration in the elytron subsystem can also take advantage of other components defined in the elytron subsystem such as credential stores. If client configuration is provided by BOTH the deployment and the elytron subsystem, the elytron subsystem’s configuration is used.
6.5. Client configuration using wildfly-config.xml
Prior to WildFly 11, many WildFly client libraries used different configuration strategies. WildFly 11 introduces a new wildfly-config.xml
file which unifies all client configuration in a single place. In addition to being able to configure authentication using Elytron as described in the previous section, a wildfly-config.xml
file can also be used to:
6.5.1. Configure Jakarta Enterprise Beans client connections, global interceptors, and invocation timeout
Schema location: [https://github.com/wildfly/jboss-ejb-client/blob/4.0.2.Final/src/main/resources/schema/wildfly-client-ejb_3_0.xsd]
Example configuration:
<configuration>
...
<jboss-ejb-client xmlns="urn:jboss:wildfly-client-ejb:3.0">
<invocation-timeout seconds="10"/>
<connections>
<connection uri="remote+http://10.20.30.40:8080"/>
</connections>
<global-interceptors>
<interceptor class="org.jboss.example.ExampleInterceptor"/>
</global-interceptors>
</jboss-ejb-client>
...
</configuration>
6.5.2. Configure HTTP client
Schema location:[https://github.com/wildfly/wildfly-http-client/blob/1.0.2.Final/common/src/main/resources/schema/wildfly-http-client_1_0.xsd]
Example configuration:
<configuration>
...
<http-client xmlns="urn:wildfly-http-client:1.0">
<defaults>
<eagerly-acquire-session value="true" />
<buffer-pool buffer-size="2000" max-size="10" direct="true" thread-local-size="1" />
</defaults>
</http-client>
...
</configuration>
6.5.3. Configure a remoting endpoint
Schema location:[https://github.com/jboss-remoting/jboss-remoting/blob/5.0.1.Final/src/main/resources/schema/jboss-remoting_5_0.xsd]
Example configuration:
<configuration>
...
<endpoint xmlns="urn:jboss-remoting:5.0">
<connections>
<connection destination="remote+http://10.20.30.40:8080" read-timeout="50" write-timeout="50" heartbeat-interval="10000"/>
</connections>
</endpoint>
...
</configuration>
6.5.4. Configure the default XNIO worker
Schema location:[https://github.com/xnio/xnio/blob/3.5.1.Final/api/src/main/resources/schema/xnio_3_5.xsd]
Example configuration:
wildfly-config.xml
<configuration>
...
<worker xmlns="urn:xnio:3.5">
<io-threads value="10"/>
<task-keepalive value="100"/>
<stack-size value="5000"/>
</worker>
...
</configuration>
6.5.5. Configure RESTEasy client
RESTEasy client will automatically load credentials, bearer token and SSL context from wildfly-config.xml
. Credentials will be used for HTTP Basic authentication and bearer token for Bearer token authentication.
Example configuration:
wildfly-config.xml
<configuration>
<authentication-client xmlns="urn:elytron:client:1.4">
...
<set-user-name name="administrator" />
<credentials>
<bearer-token value="bearerTokenValue"/>
</credentials>
<key-stores>
<key-store name="truststore" type="PKCS12">
<file name="src/test/resources/org/jboss/resteasy/test/security/client-different-cert.truststore"/>
</key-store>
</key-stores>
<ssl-contexts>
<ssl-context name="client-context">
<trust-store key-store-name="truststore"/>
</ssl-context>
</ssl-contexts>
<ssl-context-rules>
<rule use-ssl-context="client-context"/>
</ssl-context-rules>
...
</authentication-client>
</configuration>
Note that WildFly client libraries do have reasonable default configuration. Thus, adding configuration for these clients to wildfly-config.xml isn’t mandatory.
|
7. Elytron and Java Authorization Contract for Containers (JACC)
This document will guide you on how to enable JACC for your deployments using the default policy defined in the Elytron subsystem.
7.1. Defining a JACC Policy Provider
Elytron subsystem provides a built-in policy provider based on JACC specification. This policy provider is active by default in the default configuration.
7.2. Enabling JACC to a Web Deployment
You can enable JACC to web deployments by executing the following command:
[standalone@localhost:9990 /] /subsystem=undertow/application-security-domain=other:add(http-authentication-factory=application-http-authentication,enable-jacc=true)
The command above defines a default security domain for applications if none is provided in jboss-web.xml. In case you already have a application-security-domain defined and just want to enable JACC you can execute a command as follows:
[standalone@localhost:9990 /] /subsystem=undertow/application-security-domain=my-security-domain:write-attribute(name=enable-jacc,value=true)
7.3. Enabling JACC to a EJB Deployment
You can enable JACC to EJB deployments by executing the following command:
[standalone@localhost:9990 /] /subsystem=ejb3/application-security-domain=other:add(security-domain=ApplicationDomain,enable-jacc=true)
The command above defines a default security domain for EJBs. In case you already have an application-security-domain defined and just want to enable JACC you can execute a command as follows:
[standalone@localhost:9990 /] /subsystem=ejb3/application-security-domain=my-security-domain:write-attribute(name=enable-jacc,value=true)
References in this document to Java Authorization Contract for Containers (JACC) refer to Jakarta Authorization unless otherwise noted. References in this document to Enterprise JavaBeans (EJB) refer to the Jakarta Enterprise Beans unless otherwise noted. |
8. Elytron and Java Authentication SPI for Containers (JASPI)
Starting from WildFly 15 an implementation of the Servlet profile from the Java Authentication SPI for Containers (JSR-196 / JASPI) is also provided by the WildFly Elytron subsystem allowing tighter integration with the security features provided by WildFly Elytron. This JASPI implementation is available out of the box with minimal steps required to use it for deployments, this section of the documentation describes how to make use of it and the features it provides.
8.1. Activation
Presently it is the Servlet profile from the JASPI specification which is supported by this integration, the following information applies to web applications deployed to WildFly.
For the JASPI integration to be enabled for a web application that web application needs to be associated with either an Elytron http-authentication-factory
or a security-domain
- by doing this the WildFly Elytron security handlers will be installed for the deployment and the WildFly Elytron security framework activated for the deployment. This is all that is required for a deployment to be 'securable' using a JASPI configuration.
Once the WildFly Elytron security framework is activate for a deployment at the time requests are being handled the globally registered AuthConfigFactory
will be queried to identify if an AuthConfigProvider
has been registered which should be used for that deployment - if an AuthConfigProvider
is found then JASPI authentication will be used instead of the deployments authentication configuration. If no AuthConfigProvider
is found then the authentication configuration for the deployment will be used instead, this could mean authentication mechanisms from a http-authentication-factory
are used or mechanisms specified in the web.xml
are used or it could even mean no authentication is performed if the application does not have any mechanisms defined.
Any updates made to the AuthConfigFactory
are immediately available, this means that if an AuthConfigProvider
is registered which is a match for an existing application it will start to be used immediately without requiring redeployment of the application.
All web applications deployed to WildFly have a security domain which will be resolved in the following order: -
-
From the deployment descriptors / annotations of the deployment being deployed.
-
The value defined on the
default-security-domain
attribute on the Undertow subsystem. -
Default to 'other'.
We assume that this security domain is a reference to a PicketBox security domain so the final step in activation is ensuring this is mapped to WildFly Elytron using an application-security-domain
resource in the Undertow subsystem. This mapping can either reference a WildFly Elytron security domain directly or it can reference a http-authentication-factory
resource to obtain instances of authentication mechanisms.
e.g.
/subsystem=undertow/application-security-domain=MyAppSecurity:add(security-domain=ApplicationDomain)
or
/subsystem=undertow/application-security-domain=MyAppSecurity:add(http-authentication-factory=application-http-authentication)
Although this latter form references a http-authentication-factory
that in turn will reference a security domain - for both examples the referenced security domain is associated with the deployment.
The minimal steps to enable the JASPI integration are: -
. Leave the default-security-domain
attribute on the Undertow subsystem undefined so it defaults to 'other'.
. Add an application-security-domain
mapping from 'other' to a WildFly Elytron security domain.
All deployments that do not specify their own security domain will be assigned this default mapping automatically which will activate the WildFly Elytron handlers and subsequently make JASPI available for that deployment.
The security domain associated with a deployment in these steps is the security domain that will be wrapped in a CallbackHandler to be passed into the ServerAuthModule
instances used for authentication.
8.1.1. Additional Options
On the application-security-domain
resource two additional attributes have been added to allow some further control of the JASPI behaviour.
-
enable-jaspi - Can be set to
false
to disable JASPI support for all deployments using this mapping. -
integrated-jaspi - By default all identities are loaded from the security domain, if set to
false
ad-hoc identities will be created instead.
8.2. Subsystem Configuration
One way to register a configuration which will result in an AuthConfigProvider
being returned for a deployment is to register a jaspi-configuration
in the Elytron subsystem.
The following command demonstrates how to add a configuration containing two ServerAuthModule
definitions: -
./subsystem=elytron/jaspi-configuration=simple-configuration:add(layer=HttpServlet, application-context="default-host /webctx", \
description="Elytron Test Configuration", \
server-auth-modules=[{class-name=org.wildfly.security.examples.jaspi.SimpleServerAuthModule, module=org.wildfly.security.examples.jaspi, flag=OPTIONAL, options={a=b, c=d}}, \
{class-name=org.wildfly.security.examples.jaspi.SecondServerAuthModule, module=org.wildfly.security.examples.jaspi}])
This results in the following configuration being persisted: -
<jaspi>
<jaspi-configuration name="simple-configuration" layer="HttpServlet" application-context="default-host /webctx" description="Elytron Test Configuration">
<server-auth-modules>
<server-auth-module class-name="org.wildfly.security.examples.jaspi.SimpleServerAuthModule" module="org.wildfly.security.examples.jaspi" flag="OPTIONAL">
<options>
<property name="a" value="b"/>
<property name="c" value="d"/>
</options>
</server-auth-module>
<server-auth-module class-name="org.wildfly.security.examples.jaspi.SecondServerAuthModule" module="org.wildfly.security.examples.jaspi"/>
</server-auth-modules>
</jaspi-configuration>
</jaspi>
The name
attribute is just a name that allows the resource to be referenced in the management model.
The layer
and application-context
attributes are used when registering this configuration with the AuthConfigFactory
- both of these attributes can be omitted allowing wildcard matching. The description
attribute is also optional and is used to provide a description to the AuthConfigFactory
.
Within the configuration one or more server-auth-module
instances can be defined with the following attributes.
* class-name - The fully qualified class name of the ServerAuthModule
.
* module - The module to load the ServerAuthModule
from.
* flag - The control flag to indicate how this module operates in relation to the other modules.
* options - Configuration options to be passed into the ServerAuthModule
on initialisation.
Configuration defined in this way is immediately registered with the AuthConfigFactory
so any existing deployments using the WildFly Elytron security framework that match against the layer
and application-context
will immediately start to make use of the configuration.
8.3. Programmatic Configuration
The APIs defined within the JASPI specification allow for applications to dynamically register custom AuthConfigProvider
instances, however the specification does not provide the actual implementations to use or a standard way to create instances of the implementations, the WildFly Elytron project contains a simple utility that can be used by deployments to help with this: -
org.wildfly.security.auth.jaspi.JaspiConfigurationBuilder
The following piece of code illustrates how this API can be used to register a similar configuration to the one illustrated in the subsystem.
String registraionId = JaspiConfigurationBuilder.builder("HttpServlet", servletContext.getVirtualServerName() + " " + servletContext.getContextPath())
.addAuthModuleFactory(SimpleServerAuthModule::new, Flag.OPTIONAL, Collections.singletonMap("a", "b"))
.addAuthModuleFactory(SecondServerAuthModule::new)
.register();
As an example this code could be executed within the init() method of a Servlet to register the AuthConfigProvider
specific for that deployment, in this code example the application context has also been assembled by consulting the ServletContext
.
The register method returns the resulting registration ID that can also be used to subsequently remove this registration directly from the AuthConfigFactory
.
As with the subsystem configuration this call has an immediate effect and will be live for all web applications using the WildFly Elytron security framework immediately.
8.4. Authentication Process
8.4.1. CallbackHandler
Based on the configuration on the application-security-domain
resource in the Undertow subsystem the CallbackHandler passed to the ServerAuthModule in an integrated or non-integrated mode.
8.4.2. Integrated
When operating in integrated mode although the ServerAuthModule instances will be handling the actual authentication the resulting identity will be loaded from the referenced SecurityDomain using the SecurityRealms referenced by that SecurityDomain, it is still possible in this mode to override the roles that will be assigned within the Servlet container.
The advantage of this mode is that ServerAuthModules are able to take advantage of the WildFly Elytron configuration for the loading of identities so identities stored in usual locations such as databases and LDAP can be loaded without the ServerAuthModule needing to be aware of these locations, additionally other WildFly Elytron configuration can be applied such as role and permission mapping. The referenced SecurityDomain can also be referenced in other places such as for SASL authentication or other non JASPI applications all backed by a common repository of identities.
In this mode the CallbackHandlers operate as follows: -
-
PasswordValidationCallback
The username and password will be used with the SecurityDomain to perform an authentication, if successful there is now an authenticated identity.
-
CallerPrincipalCallback
This Callback is used to establish the authorized identity / the identity that will be seen once the request reached the web application.
If an authenticated identity has already been established via the PasswordValidationCallback this Callback is interpreted as a run-as request and authorization checks are performed to ensure the authenticated identity is authorized to run as the identity specified in this Callback. If no authenticated identity has been established by a PasswordValidationCallback it is assumed the ServerAuthModule has handled the authentication step so this Callback will cause the specified identity to be loaded from the SecurityDomain and an authorization check to verify this identity has the LoginPermission.
If a Callback is received with a null Principal and name then if an authenticated identity has already been established authorization will be performed as that identity, if no identity has been established then authorization of the anonymous identity will be performed. Where authorization of the anonymous identity is performed the SecurityDomain must have been configured to grant the anonymous identity the LoginPermission otherwise authorization will fail.
-
GroupPrincipalCallback
By default in this mode the attribute loading, role decoding, and role mapping configured on the security domain will be used to establish the identity - if this Callback is received the groups specified will be taken as the roles that will be assigned to the identity whilst the request is in the servlet container. These roles will be visible in the servlet container only.
8.4.3. Non Integrated
When operating in non-integrated mode the ServerAuthModules are completely responsible for all authentication AND identity management, the Callbacks specified in the specification can be used to establish an identity. The resulting identity will be created on the SecurityDomain but it will be independent of any identities stored in referenced SecurityRealms.
The advantage of this mode is that JASPI configurations that are able to 100% handle the identities can be deployed to the application server without requiring anything beyond a simple SecurityDomain definitions, there is no need for this SecurityDomain to actually contain the identities that will be used at runtime. The disadvantage of this mode is that the ServerAuthModule is now reposible for all identity handling potenitally making the implementation much more complex.
In this mode the CallbackHandlers operate as follows: -
-
PasswordValidationCallback
The Callback is not supported in this mode, the purpose of this mode is for the ServerAuthModule to operate independently of the referenced SecurityDomain so requesting a password to be validated would not be suitable.
-
CallerPrincipalCallback
This Callback is used to establish the Principal for the resulting identity, as the ServerAuthModule is handling all of the identity checking requirements no checks are performed to verify if the identity exists in the security domain and no authorization checks are performed.
If a Callback is received with a null Principal and name then then the identity will be established as the anonymous identity, as the ServerAuthModule is making the decisions no authorizaton check will be performed with the SecurityDomain.
-
GroupPrincipalCallback
As the identity is created in this mode without loading from the SecurityDomain it will by default have no roles assigned, if this Callback is received the groups will be taken and assigned to the resulting identity whilst the request is in the servlet container. These roles will be visible in the servlet container only.
8.4.4. validateRequest
During the call to validateRequest
on the ServerAuthContext
the individual ServerAuthModule
instances will be called in the order they are defined. A control flag can also be specified for each module, this defines how the response should be interpreted and if processing should continue to the next auth module or return immediately.
Control Flags
Where the configuration was provided either within the WildFly Elytron subsystem or using the JaspiConfigurationBuilder
API it is possible to associate a control flag with each ServerAuthModule
- if one is not specified we assume REQUIRED
. The flags have the following meanings depending on their result.
Flag |
AuthStatus.SEND_SUCCESS |
AuthStatus.SEND_FAILURE, AuthStatus.SEND_CONTINUE |
Required |
Validation will continue to the remaining modules, provided the requirements of the remaining modules are satisfied the request will be allowed to proceed to authorization. |
Validation will continue to the remaining modules, however regardless of their outcome the validation is not successful so control will return to the client. |
Requisite |
Validation will continue to the remaining modules, provided the requirements of the remaining modules are satisfied the request will be allowed to proceed to authorization. |
The request will return immediately to the client. |
Sufficient |
Validation is deemed successful and complete, provided no previous Required or Requisite module has returned an AuthStatus other than AuthStatus.SUCCESS the request will proceed to authorization of the secured resource. |
Validation will continue down the list of remaining modules, this status will only affect the decision if there are no REQUIRED or REQUISITE modules. |
Optional |
Validation will continue to the remaining modules, provided no 'Required' or 'Requisite' modules have not returned SUCCESS this will be sufficient for validation to be deemed successful and for the request to proceed to the authorization stage and the secured resource. |
Validation will continue down the list of remaining modules, this status will only affect the decision if there are no REQUIRED or REQUISITE modules. |
For all ServerAuthModule
instances if they throw an AuthException
an error will be immediately reported to the client without further modules being called.
8.4.5. secureResponse
During the call to secureResponse
each ServerAuthMdoule
is called but this time in reverse order. Where a module only undertakes an action in secureResponse
if it undertook an action in validateResponse
it is the responsibility of the module to track this.
The control flag has no effect on secureResponse
processing, processing ends when one of the following is true: -
. All of the ServerAuthModule
instances have been called.
. A module returns AuthStatus.SEND_FAILURE
.
. A module throws an AuthException
.
8.4.6. SecurityIdentity
Once the authentication process has completed a org.wildfly.security.auth.server.SecurityIdentity
for the deployments SecurityDomain will have been created as a result of the Callbacks to the CallbackHandler, depending on the Callbacks this will either be an identity loaded directly from the SecurityDomain or will be an ad-hoc identity described by the callbacks. This SecurityIdentity will be associated with the request as we do for other authentication mechanisms
9. Elytron and Jakarta EE Security
Starting from WildFly 15 an implementation of the Servlet Profile from Java Authentication SPI for Containers (JSR-196 / JASPI) specification has been included in the application server, in addition to making JASPI available for deployments using WildFly Elytron it also makes it possible to make use of EE Security with WildFly Elytron.
9.1. Activation
EE Security with WildFly Elytron is available out of the box with just a couple of small steps required.
9.1.1. Define JACC Policy
The SecurityContext
API makes use of JACC to access the current authenticated identity, if deployments are going to make use of this API then a JACC policy needs to be activated in the WildFly Elytron subsystem. If this API is not being used then the activation can be skipped.
/subsystem=elytron/policy=jacc:add(jacc-policy={})
:reload
As is shown in the example no specific configuration is required other than the presence of a default JACC policy, additionally after making these changes the server needs to be reloaded to ensure the new policy activates correctly.
9.1.2. Add application-security-domain
mapping
As with all deployments a mapping is required from the security domain defined for the deployment to either a WildFly Elytron security domain or http authentication factory to activate security backed by WildFly Elytron.
All web applications deployed to WildFly have a security domain which will be resolved in the following order: -
-
From the deployment descriptors / annotations of the deployment being deployed.
-
The value defined on the
default-security-domain
attribute on the Undertow subsystem. -
Default to 'other'.
An application-security-domain
resource then needs to be added to map from the deployment’s security domain.
The simplest approach is to add a mapping for other
, then no further configuration will be required and provided the deployment does not define it’s own security domain and provided no alternative default is specified all deployments will match this mapping.
/subsystem=undertow/application-security-domain=other:add(security-domain=ApplicationDomain, integrated-jaspi=false)
The EE Security API is built on JASPI. Within JASPI we support two different modes of operation 'integrated', and 'non-integrated'. In integrated mode any identity being established during authentication is expected to exist in the associated security domain. With the EE Security APIs however it is quite likely an alternative store will be in use so configuration the mapping to use 'non-integrated' JASPI allows for identities to be dynamically created as required.
References in this document to Java Authorization Contract for Containers (JACC) refer to the Jakarta Authorization unless otherwise noted |
10. Using Keycloak SAML
To secure applications deployed to WildFly using Keycloak SAML, a Galleon feature pack provided
by the Keycloak project needs to be used. This feature pack automatically installs the Keycloak
SAML adapter that results in the keycloak-saml
subsystem being added to WildFly.
There are a few ways to install the Keycloak Galleon Feature Pack that provides the SAML adapter.
10.1. Galleon CLI
A server installation can be provisioned using the Galleon CLI.
More information can be found in the Provisioning WildFly with Galleon
documentation, but assuming you have provisioned a WildFly installation containing the web-server
layer
with a command similar to the following:
galleon.sh install wildfly:current \
--layers=web-server --dir=wildfly
The Keycloak SAML adapter can then be added to the WildFly installation with the following command:
galleon.sh install org.keycloak:keycloak-saml-adapter-galleon-pack:22.0.1 --layers=keycloak-client-saml --dir=wildfly
Unlike the WildFly feature pack, this Keycloak feature pack is not part of a universe and so a fully
qualified |
From this point, applications can be configured as described in the Keycloak documentation.
10.2. Bootable JAR
The next installation option is if you are creating a bootable JAR for your deployment and want to add the Keycloak SAML adapter to secure the deployment. More details about bootable JAR support can be found in the Bootable JAR Guide.
The following is an example plug-in configuration to create a bootable JAR for a web application
using both the web-server
and keycloak-client-saml
layers:
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-jar-maven-plugin</artifactId>
<version>${version.wildfly.jar.maven.plugin}</version>
<configuration>
<feature-packs>
<feature-pack>
<location>wildfly@maven(org.jboss.universe:community-universe):current</location>
</feature-pack>
<feature-pack>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-galleon-pack</artifactId>
<version>22.0.1</version>
</feature-pack>
</feature-packs>
<layers>
<layer>web-server</layer>
<layer>keycloak-client-saml</layer>
</layers>
<context-root>simple-webapp</context-root>
<cli-sessions>
<cli-session>
<script-files>
<script>configure-saml.cli</script>
</script-files>
</cli-session>
</cli-sessions>
</configuration>
<executions>
<execution>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
As with using the CLI, the latest version of WildFly in the universe can be dynamically selected but the
Keycloak feature pack requires the complete |
The approaches to configure the web application are the same as described in the
Keycloak documentation.
In this example, a CLI script, configure-saml.cli
, is executed to update the Keycloak SAML subsystem
with relevant configuration.
A sample script is shown below:
/subsystem=keycloak-saml/secure-deployment=simple-webapp.war/:add
/subsystem=keycloak-saml/secure-deployment=simple-webapp.war/SP="http://localhost:8090/simple-webapp"/:add(sslPolicy=EXTERNAL,logoutPage="logout")
/subsystem=keycloak-saml/secure-deployment=simple-webapp.war/SP="http://localhost:8090/simple-webapp"/IDP=idp/:add( \
SingleSignOnService={ \
signRequest=false, \
validateResponseSignature=false, \
validateAssertionSignature=false, \
requestBinding=POST, \
bindingUrl=http://localhost:8080/realms/myrealm/protocol/saml}, \
SingleLogoutService={ \
signRequest=false, \
signResponse=false, \
validateRequestSignature=false, \
validateResponseSignature=false, \
requestBinding=POST, \
responseBinding=POST, \
postBindingUrl=http://localhost:8080/realms/myrealm/protocol/saml, \
redirectBindingUrl=http://localhost:8080/realms/myrealm/protocol/saml} \
)
Instead of adding configuration in the Keycloak SAML subsystem, configuration can be added in the deployment
instead. In particular, the auth-method
of the web application could be set to KEYCLOAK-SAML
and
adapter configuration could be provided in a keycloak-saml.json
descriptor placed within the WEB-INF
directory
of the deployment.
10.3. WildFly Maven Plugin
The final installation option is to use the wildfly-maven-plugin
to provision a server containing
the Keycloak SAML subsystem.
The following is an example plug-in configuration to create a server for a web application
using both the web-server
and keycloak-client-saml
layers:
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>4.0.0.Final</version>
<configuration>
<feature-packs>
<feature-pack>
<location>wildfly@maven(org.jboss.universe:community-universe):current</location>
</feature-pack>
<feature-pack>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-galleon-pack</artifactId>
<version>22.0.1</version>
</feature-pack>
</feature-packs>
<layers>
<layer>web-server</layer>
<layer>keycloak-client-saml</layer>
</layers>
</configuration>
<executions>
<execution>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
As with the bootable JAR configuration, note that the latest version of WildFly in the universe can be dynamically
selected but the Keycloak feature pack requires the complete |
10.4. Propagating the Security Context to EJBs
The sample configuration in the above sections has referenced the keycloak-client-saml
layer.
If the security context for the application that is being secured with Keycloak SAML needs to
be propagated to the EJB tier, the keycloak-client-saml-ejb
layer should be used instead.
This layer adds an application-security-domain
mapping in the EJB3 subsystem to map the default
security domain name other
to the KeycloakDomain
that is installed by the Keycloak feature pack.
11. Using Keycloak with WildFly Elytron
The ability to secure applications using OpenID Connect is provided by the elytron-oidc-client subsystem. For details about securing applications deployed on WildFly with OpenID Connect, by using Keycloak as the OpenID provider, see the Elytron OIDC Client documentation.
12. Bearer Token Authorization
Bearer Token Authorization is the process of authorizing HTTP requests based on the existence and validity of a bearer token representing a subject and his access context, where the token provides valuable information to determine the subject of the call as well whether or not a HTTP resource can be accessed.
Elytron supports bearer token authorization by providing a BEARER_TOKEN
HTTP authentication mechanism based on RFC-6750 and
an specific realm
implementation, the token-realm
, to validate tokens using the JWT format (for instance, OpenID Connect ID Tokens) or opaque tokens issued by any OAuth2 compliant
authorization server.
12.1. How it Works
When a HTTP request arrives to your application, the BEARER_TOKEN
mechanism will check if a bearer token was provided by checking the existence of an Authorization
HTTP header with the following format:
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
If no bearer token was provided, the mechanism will respond with a 401
HTTP status code as follows:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example"
When a bearer token is provided, the mechanism will extract the token from the request (in the example above, the token is represented by the string mF_9.B5f-4.1JqM
) and pass it over
to the token-realm
in order to check if the token is valid and can be used to build a security context based on its information. Note that the BEARER_TOKEN
mechanism is only responsible to
check and extract bearer tokens from an HTTP request, whereas the token-realm
is the one responsible for validating the token.
If validation succeeds, a security context will be created based on the information represented by the token and the application can use the newly created
security context to obtain information about the subject making the request as well decide whether or not the request should be full filled. In case the validation fails,
the mechanism will respond with a 403
HTTP status code as follows:
HTTP/1.1 403 Forbidden
12.2. Validating JWT Tokens
Elytron provides built-in support for JWT tokens, which can be enabled by defining a realm
in the Elytron subsystem as follows:
<token-realm name="JwtRealm" principal-claim="sub">
<jwt issuer="as.example.com"
audience="api.example.com"
public-key="-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQAB-----END PUBLIC KEY-----"/>
</token-realm>
In the example above, the token-realm
is defined with a principal-claim
attribute. This attribute specifies which claim
within the token should be used to identity the principal. If you have a token as follows:
{
"iss": "as.example.com",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"exp": 1311281970,
"nbf": 1311280970,
}
Elytron will use the value associated with the sub
claim as the identifier of the subject represented by the token.
The jwt
element within the token-realm
specifies that tokens should be validated as JWT and provides different configuration options on how they
should be validated:
-
issuer
A list of strings representing the issuers supported by this configuration. During validation JWT tokens must have an "iss" claim that contains one of the values defined here
-
audience
A list of strings representing the audiences supported by this configuration. During validation JWT tokens must have an "aud" claim that contains one of the values defined here
-
public-key
A public key in PEM Format. During validation, if a public key is provided, signature will be verified based on the key you provided here. Alternatively, you can define a
key-store
andcertificate
attributes to configure the public key. This key will be used to verify tokens without "kid" claim. -
key-store
As an alternative to
public-key
, you can also define a key store from where the certificate with a public key should be loaded from -
certificate
The name of the certificate with a public key to load from the key store in case you defined the
key-store
attribute -
client-ssl-context
The SSL context to be used if if you want to use remote JSON Web Keys. This enables you to use url from "jku" token claim to fetch public keys for token verification.
-
host-name-verification-policy
A policy that defines how host names should be verified when using remote JSON Web Keys. Allowed values: "ANY", "DEFAULT".
For being able to use different key pairs for signing / verification and for easier key rotation you can define key map. This will take the "kid" claim from the token and use corresponding public key for verification.
<token-realm name="JwtRealm" principal-claim="sub">
<jwt issuer="as.example.com" audience="api.example.com">
<key kid="1" public-key="-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANc4VlnN6oZwe1PoQQeJsTwu7LGS+eEbgYMNYXahidga4+BhdGKwzMZU54ABFQ11tUMJSENQ6o3n1YKVgMnxvcMCAwEAAQ==-----END PUBLIC KEY-----"/>
<key kid="2" public-key="-----BEGIN PUBLIC KEY-----MFswDQYJKoZIhvcNAQEBBQADSgAwRwJAcNpXy6psxC21DdnTtAdlgsEwEuJh/earH3q7xJPjmsygmrlpC66MG4/A/J9Gai2Hp+QdCSEVpBWkIoVff3sIlwIDAQAB-----END PUBLIC KEY-----"/>
</jwt>
</token-realm>
This token-realm
will verify tokens with "kid" claims of value "1" or "2" using corresponding key.
Tokens without "kid" claim will be verified using the 'public-key' attribute of 'jwt', or in this case invalidated, as there is no key specified.
The jwt
validator performs different checks in order to determine the validity of a JWT:
-
Expiration checks based on the values of the
exp
andnbf
claims -
Signature checks based on the public key provided via
public-key
orcertificate
attributes, key map with named public keys, or by fetching remote JSON Web Key Set using providedclient-ssl-context
. You can skip token signature verification by not defining any of these.
It is strongly recommended that you use signed JWTs in order to guarantee authenticity of tokens and make sure they were not tampered.
12.3. Validating OAuth2 Bearer Tokens
Elytron provides built-in support for tokens issued by an OAuth2 compliant authorization server, where these tokens are validated using a token introspection endpoint as defined by OAuth2 specification.
<token-realm name="OAuth2Realm" principal-claim="sub">
<oauth2-introspection client-id="my-client-id"
client-secret="keep_it_secret"
introspection-url="https://as.example.com/token/introspect"
client-ssl-context="user-defined-ssl-context"
host-name-verification-policy="ANY" />
</token-realm>
The auth2-introspection
element within the token-realm
specifies that tokens should be validated using an OAuth2 Token Introspection Endpoint and provides different configuration options on how they
should be validated:
-
client-id
The identifier of the client on the OAuth2 Authorization Server
-
client-secret
The secret of the client
-
introspection-url
The URL of token introspection endpoint
-
client-ssl-context
The SSL context to be used if the introspection endpoint is using HTTPS.
-
host-name-verification-policy
A policy that defines how host names should be verified when using HTTPS. Allowed values: "ANY", "DEFAULT".
12.5. CLI Examples on How to Create a Token Realm
# Create a Key Store
/subsystem=elytron/key-store=my-keystore:add(path=/path/to/keystore.pkcs12,credential-reference={clear-text=secret},type=PKCS12)
# Create the realm
/subsystem=elytron/token-realm=jwt-realm:add(principal-claim=sub, jwt={issuer=["as.example.com"], audience=["api.example.com"], key-store=my-keystore, certificate=as.example.com})
# Create a Client SSLContext
/subsystem=elytron/key-store=default-trust-store:add(path=/path/to/keystore.pkcs12,credential-reference={clear-text=secret},type=PKCS12)
/subsystem=elytron/trust-manager=default-trust-manager:add(algorithm=PKIX, key-store=default-trust-store)
/subsystem=elytron/client-ssl-context=default-client-ssl-context:add(trust-manager=default-trust-manager)
# Create the realm
/subsystem=elytron/token-realm=oauth2-realm:add(principal-claim=preferred_username, oauth2-introspection={client-id=my-client-id, client-secret=keep_it_secret, client-ssl-context=default-client-ssl-context, introspection-url=https://as.example.com/token/introspect})
13. OpenSSL
13.1. Configuring WildFly to use the OpenSSL TLS provider
There are a couple ways you can configure WildFly to use the OpenSSL TLS provider.
13.1.1. Configure the Elytron subsystem to use the OpenSSL TLS provider by default
The following commands can be used to configure the Elytron subsystem so that the OpenSSL TLS provider is used by default:
/subsystem=elytron:write-attribute(name=initial-providers, value=combined-providers)
/subsystem=elytron:undefine-attribute(name=final-providers)
reload
13.1.2. Configure the OpenSSL TLS provider on an SSL context
Instead of configuring the Elytron subsystem to use the OpenSSL TLS provider by default,
it is also possible to specify that the OpenSSL TLS provider should be used for a specific
SSL context. For example, the following command can be used to create a server-ssl-context
that will use the OpenSSL TLS provider:
/subsystem=elytron/server-ssl-context=httpsSSC:add(providers=openssl, ...)
reload
13.2. OpenSSL Library Location
WildFly will search for the OpenSSL library using the standard system library search path. If you’d like to specify a
custom location for the OpenSSL library, the org.wildfly.openssl.path
property can be specified during WildFly startup.
If OpenSSL was loaded successfully, a message similar to the one below will occur in the server.log
file on startup:
15:37:59,814 INFO [org.wildfly.openssl.SSL] (MSC service thread 1-7) WFOPENSSL0002 OpenSSL Version OpenSSL 1.1.1d FIPS 10 Sep 2019
13.3. Enabling TLSv1.3
WARNING It is possible to use TLSv1.3 with the OpenSSL TLS provider when using OpenSSL 1.1.1 or higher and
when running against JDK 11 or higher. However, if JDK 11 is in use and if there is a very large number of
TLSv1.3 requests being made, it is possible that a drop in performance (throughput and response time) will occur
compared to TLSv1.2. For this reason, the use of TLSv1.3 is currently disabled by default. TLSv1.3 can be enabled
by configuring the cipher-suite-names
attribute for an SSL context. See the sections on
Configuring a server SSLContext and
Configuring a client SSLContext for more details. It is recommended to test
for performance degradation prior to enabling TLSv1.3 in a production environment.
13.4. Adding Additional Native Libraries
By default, WildFly includes WildFly OpenSSL native libraries for commonly used platforms (Linux x86_64, Windows x86_64, Mac OS X x86_64, and Linux s390). However, the WildFly OpenSSL Natives project contains modules that can be used to build WildFly OpenSSL native libraries for other platforms as well. The steps needed to build and make use of a WildFly OpenSSL native library for a different platform than the ones provided by default will be described below.
13.4.1. Building the Native Library
Make sure you have cloned the WildFly OpenSSL Natives project
locally. We’ll use $WILDFLY_OPENSSL_NATIVES
to denote the path to this project. Next, cd
to the module that you want
to build. In this example, we’ll use the linux-i386
module but similar steps can be used to build and make use of another
module instead.
cd $WILDFLY_OPENSSL_NATIVES/linux-i386
mvn clean install
Notice that this now results in a wildfly-openssl-linux-i386-VERSION.jar
in the $WILDFLY_OPENSSL_NATIVES/linux-i386/target
directory.
We’ll make use of this in the next step.
13.4.2. Overriding the Existing WildFly OpenSSL Module
We’re going to add a new module that will override the existing WildFly OpenSSL module that’s found in the
$WILDFLY_HOME/modules/system/layers/base/org/wildfly/openssl
directory. Notice that the existing module
contains a Java artifact in its main
directory and native libraries for the default platforms in its
main/lib
directory.
First, create a new module that contains the WildFly OpenSSL Java artifact from the existing module:
module add --name=org.wildfly.openssl --resources=$WILDFLY_HOME/modules/system/layers/base/org/wildfly/openssl/main/wildfly-openssl-java-VERSION.jar --dependencies=java.logging,jdk.unsupported
Next, add the native library that we already built to this newly created module:
mkdir $WILDFLY_HOME/modules/org/wildfly/openssl/main/lib
cd $WILDFLY_HOME/modules/org/wildfly/openssl/main/lib
jar xvf $WILDFLY_OPENSSL_NATIVES/linux-i386/target/wildfly-openssl-linux-i386-VERSION.jar linux-i386
You should now see a libwfssl.so
file in the $WILDFLY_HOME/modules/org/wildfly/openssl/main/lib/linux-i386
directory.
That’s it, WildFly will now make use of the newly added WildFly OpenSSL module instead of the existing one,
allowing it to make use of the new native library when running on the Linux i386 platform. Additional
native libraries can be added to the $WILDFLY_HOME/modules/org/wildfly/openssl/main/lib
directory
if desired.
14. Web Single Sign-On
This document will guide on how to enable single sign-on across different applications deployed into different servers, where these applications belong to same security domain.
14.1. Create a Server Configuration Template
For this document, you’ll need to run at least two server instances in order to check single sign-on and how it affect usability in your applications. Users should be able to log in once and have access to any application using the same security domain.
All configuration described in the next sections should be done with a server instance using standalone-ha.xml (or standalone-full-ha.xml).
Run a server instance using the following command:
bin/standalone.sh -c standalone-ha.xml
14.1.1. Create a HTTP Authentication Factory
If you already have a http-authentication-factory defined in Elytron subsystem and just want to use it to enable single sign-on to your applications, please skip this section.{info} First, you need a security-domain which we’ll use to authenticate users. Please, execute the following CLI commands: |
# Creates a FileSystem Realm, an identity store where users are stored in the local filesystem /subsystem=elytron/filesystem-realm=example-realm:add(path=/tmp/example-realm) # Creates a Security Domain /subsystem=elytron/security-domain=example-domain:add(default-realm=example-realm, permission-mapper=default-permission-mapper,realms=[{realm=example-realm, role-decoder=groups-to-roles}] # Creates an user that you can use to access your applications /subsystem=elytron/filesystem-realm=example-realm:add-identity(identity=alice) /subsystem=elytron/filesystem-realm=example-realm:add-identity-attribute(identity=alice, name=groups, value=["user"]) /subsystem=elytron/filesystem-realm=example-realm:set-password(identity=alice, clear={password=alice})
Now you can create a http-authentication-factory that you’ll use to actually protect your web applications using Undertow:
# Create a Http Authentication Factory
/subsystem=elytron/http-authentication-factory=example-http-authentication:add(security-domain=example-domain, http-server-mechanism-factory=global, mechanism-configurations=[{mechanism-name=FORM}]
14.1.2. Create a Application Security Domain in Undertow
If you already have a _application-security-domain_ defined in Undertow subsystem and just want to use it to enable single sign-on to your applications, please skip this section. |
In order to protect applications using the configuration defined in Elytron subsystem, you should create a application-security-domain definition in Undertow subsystem as follows:
/subsystem=undertow/application-security-domain=other:add(http-authentication-factory=example-http-authentication)
By default, if your application does not define any specific security-domain in jboss-web.xml, the application server will choose one with a name other.
14.1.3. Create a Key Store
In order to create a key-store in Elytron subsystem, first create a Java Key Store as follows:
keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -validity 365 -keystore keystore.pkcs12 -dname "CN=localhost" -keypass secret -storepass secret
Once the keystore.pkcs12 file is created, execute the following CLI commands to create a key-store definition in Elytron:
/subsystem=elytron/key-store=example-keystore:add(path=keystore.pkcs12, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=PKCS12)
14.1.4. Enable Single Sign-On
Single Sign-On is enabled to a specific application-security-domain definition in Undertow subsystem. It is important that the servers you will be using to deploy applications are using the same configuration.
To enable single-sign on, just change an existing application-security-domain in Undertow subsystem as follows:
/subsystem=undertow/application-security-domain=other/setting=single-sign-on:add(key-store=example-keystore, key-alias=localhost, domain=localhost, credential-reference={clear-text=secret})
After restarting the servers, users should be able to log in once and have access to any application using the same application-security-domain.
14.2. Create Two Server Instances
All configuration you did so far should be reflected in $JBOSS_HOME/standalone/standalone-ha.xml. You can now create two distinct server configuration directories_:\_
cp -r standalone standalone-a
cp -r standalone standalone-b
And you can run the two instances using the command below:
$JBOSS_HOME/bin/standalone.sh -c standalone-ha.xml -Djboss.node.name=node-a -Djboss.socket.binding.port-offset=200 -Djboss.server.base.dir=$JBOSS_HOME/standalone-a $JBOSS_HOME/bin/standalone.sh -c standalone-ha.xml -Djboss.node.name=node-b -Djboss.socket.binding.port-offset=300 -Djboss.server.base.dir=$JBOSS_HOME/standalone-b ----
14.3. Deploy an Application
For the sake of simplicity, these are the minimum files you need in your application:
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<security-constraint>
<display-name>SecurityConstraint</display-name>
<web-resource-collection>
<web-resource-name>All Resources</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/login.html</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>
<html>
<body>
<form method="post" action="j_security_check">
<input type="text" name="j_username">
<input type="password" name="j_password">
<input type="submit" value="Log In">
</form>
</body>
</html>
Make sure you have at least a welcome file (e.g.: index.html\|jsp). |
Deploy your application into both server instances and try to log in using the user you created at the beginning of this document:
-
Username: alice
-
Password: alice
15. Audit
WildFly Elytron supports audit using security event listeners - components which captures security events, like successful or unsuccessful login attempts.
15.1. File audit log
File audit log logs security events into a local file.
It requires to define path
to the log file, which can be relative-to
a system property.
It also allows to set the file format - human readable SIMPLE
or JSON
.
The encoding used by the audit file can be set using encoding
. The default value is UTF-8
. Possible values: UTF-8
, UTF-16BE
, UTF-16LE
, UTF-16
, US-ASCII
or ISO-8859-1
.
While in WildFly 14 there was only one attribute synchronized
, which had influence on data reliability, now there is possible more fine grained performance and reliability tunning:
* autoflush
defines whether should be output stream flushed after every audit event (guarantees that the log message is passed to the operating system immediately)
* synchonized
defines whether should be file descriptor synchronized after every audit event (guarantees that all system buffers are synchronized with the underlying device)
15.2. Syslog Audit Logging
Syslog audit logging logs security events to a syslog server using a transmission protocol.
WildFly Elytron supports using UDP, TCP, or TCP with SSL, with the latter protocol requiring
a
to be defined. When syslog audit logging is first defined, Elytron will send
an SSLContext
priority event to the defined syslog server containing the message
"Elytron audit logging enabled with RFC format: <format>", where INFORMATIONAL
is the
RFC format that the audit logging handler has been defined with, defaulting to <format>
.
If the given syslog server is not defined, resulting in Elytron being unable to send the
message, then Elytron will keep track of the amount of attempts that sending a message has
failed, up to a maximum defined by the RFC5424
parameter, before
closing the endpoint and reporting an error. It is possible to define this value
as infinite, by specifying reconnect-attempts
, in which case Elytron will never close the
endpoint and so will always attempt to send audit messages despite previous failures.-1
15.2.1. Required Parameters
Parameter | Value |
---|---|
|
String consisting of the IP address, or a name recognized by Java’s InetAddress.getByName(), of the syslog server |
|
|
|
String of the host name that will be embedded into all events sent to the syslog server |
|
The |
15.2.2. Optional Parameters
Parameter | Description | Possible Values | Default Value |
---|---|---|---|
|
The transport protocol to be used to connect to the syslog server |
String of: |
|
|
The format that audit events should be recorded in |
String of |
|
|
The RFC format to be used for describing the audit event |
String of |
|
|
The maximum number of times that elytron will attempt to send successive messages to a syslog server before closing the endpoint to disallow further attempts to send messages. |
|
|
15.2.3. Defining Syslog Audit Logging
Syslog audit logging can be defined under the
subsystem resource. Some
examples syslog audit logging resources can be created with the following commands:elytron
Minimum Resource Definition
Using the following command will generate a syslog audit logging resource that connects with TCP, records audit events in a simple format, and uses RFC5424 to describe the audit event.
/subsystem=elytron/syslog-audit-log=syslog-example:add(
server-address=127.0.0.1,
port=10857,
host-name=localhost
)
Default Format and Reconnect Attempts with UDP and RFC3164
Using the following command will generate a syslog audit logging resource that connects with UDP, does not send any further messages to the syslog server if there is an error sending, records audit events in a simple format, and uses RFC3164 to describe the audit event.
/subsystem=elytron/syslog-audit-log=syslog-example:add(
server-address=127.0.0.1,
port=10837,
host-name=localhost,
transport=UDP,
syslog-format=RFC3164
)
Full UDP Definition with RFC5424 Explicitly Set and 10 Reconnect Attempts
Using the following command will generate a syslog audit logging resource that connects with UDP, attempts to send messages 10 times if there is an error sending before no longer sending messages, records audit events in a simple format, and uses RFC5424 to describe the audit event.
/subsystem=elytron/syslog-audit-log=syslog-example:add(
server-address=127.0.0.1,
port=10837,
host-name=localhost,
transport=UDP,
syslog-format=RFC5424,
reconnect-attempts=10
)
Full UDP Definition with Infinite Reconnect Attempts and JSON Format
Using the following command will generate a syslog audit logging resource that connects with UDP, always attempts to send messages despite previous failures sending messages, records audit events in a JSON format, and uses RFC5424 to describe the audit event.
/subsystem=elytron/syslog-audit-log=syslog-example:add(
server-address=127.0.0.1,
port=10837,
host-name=localhost,
transport=UDP,
format=JSON,
reconnect-attempts=-1
)
16. Credential Store
One new component included with WildFly Elytron for the secure storage of credentials is the Credential Store. Previous versions of the application server made use of the Vault which was used for the secure storage of clear text strings; the credential store moves forward a step to focus on the secure storage of credentials. As with the Vault the stored credentials could be clear text passwords however other formats are also supported.
WildFly Elytron contains two default credential store implementations. The first implementation is the KeyStoreCredentialStore
which is an
implementation backed by a KeyStore
. The KeyStoreCredentialStore
implementation supports the storage of various credential types,
PasswordCredential
, KeyPairCredential
, and SecretKeyCredential
. The second implementation is the PropertiesCredentialStore
. This credential
store is dedicated to the storage of SecretKeyCredential
instances using a properties file. The PropertiesCredentialStore
does not offer any protection
of the credentials it stores so its primary purpose is to provide an initial key to a server environment.
This documentation is primarily focused on the KeyStoreCredentialStore
and PropertiesCredentialStore
; however the section Custom CredentialStore describes the SPIs for implementing a custom credential store and the section Migrating Existing Vaults describes how to convert a vault to a credential store.
It is possible to operate on credential stores using either a standalone command line tool elytron-tool.sh
which is included with WildFly or by using management operations through the jboss-cli.sh
, management console or other management client calling the management interface directly.
The elytron-tool.sh
script to execute the command line tool can be found within the bin
folder of the application server installation.
The WildFly Elytron tool supports a number of commands, one of which being credential-store
which operates on a credential store. The commands in this documentation are making use of the .sh
script on linux; the elytron-tool.bat
and elytron-tools.ps1
scripts can be used on Microsoft Windows.
The following command provides a summary of the command line arguments which can be used with the credential-store
command:
]$ bin/elytron-tool.sh credential-store
16.1. Credential Store Creation
16.1.1. KeyStoreCredentialStore / credential-store
The KeyStoreCredentialStore
supports the largest number of credential types which can be stored within a credential store, additionally as the implementation is backed by a Java KeyStore the storage is protected using the mechanisms provided by the KeyStore implementations.
Command Line
When using the credential-store
command of the elytron-tool.sh
script by default, this command assumes the type of the store is a KeyStoreCredentialStore
backed by a JCEKS
KeyStore
.
A new credential store can be created using the following command: -
]$ bin/elytron-tool.sh credential-store --create --location=standalone/configuration/mycredstore.cs
Credential store password:
Confirm credential store password:
Credential Store has been successfully created
This command prompts twice for the password that should be used to secure the store. Alternatively the password can be passed in using the --password
argument however that may mean the password is cached in the local command history or visible to other users viewing the list of running processes.
The command line tool only creates the instance of the credential store on the underlying file system; the management operation is still required to define the credential store in the elytron subsystem.
|
Management Operation
It is also possible to operate on a credential store using management operations against a running server using the application server’s CLI.
The following operation can be used to add a credential-store
resource to the elytron
subsystem referencing this newly created credential store.
/] /subsystem=elytron/credential-store=mycredstore:add(path=mycredstore.cs, relative-to=jboss.server.config.dir, credential-reference={clear-text=StorePassword})
{"outcome" => "success"}
In this example the resource was added to reference an existing credential store, however with a small change this command can also automatically create the store if it does not exist.
/] /subsystem=elytron/credential-store=mycredstore:add(path=mycredstore.cs, relative-to=jboss.server.config.dir, credential-reference={clear-text=StorePassword}, create=true)
{"outcome" => "success"}
The file representing the credential store is not created immediately, it will instead be created the first time a credential is added. |
Previous versions of the application server supported a single vault definition only; credential stores however are cross-referenced by name so multiple credential stores can be defined simultaneously.
16.1.2. PropertiesCredentialStore / secret-key-credential-store
This is a simple credential store implementation which can only be used to store SecretKey
instances. This credential store also does not offer any protection of the contents.
Within an application server environment it is always possible to get into a cycle of how is an initial secret provided to unlock further resources, this is primarily the purpose
of this credential store. The strategy in the past had been to use masking of a password using password based encryption, the down side of this approach was the password used for
the masking was stored in the source code and being an open source project this means the password is well known. By using the PropertiesCredentialStore
installations are back
in control of the initial secret. Although this credential store does not offer its own protection filesystem level access control can still be used to restrict access ideally to
just the application server process.
Command Line
A new credential store can be created using the following command: -
]$ bin/elytron-tool.sh credential-store --create --location=standalone/configuration/propcredstore.cs --type PropertiesCredentialStore
Credential Store has been successfully created
Management Operation
It is also possible to operate on a credential store using management operations against a running server using the application server’s CLI.
The following operation can be used to add a secret-key-credential-store
resource to the elytron
subsystem referencing this newly created credential store.
/] /subsystem=elytron/secret-key-credential-store=mycredstore:add(path=propcredstore.cs, relative-to=jboss.server.config.dir, create=false, populate=false)
{"outcome" => "success"}
In this example the resource was added to reference an existing credential store, however with a small change this command can also automatically create the store if it does not exist, and
populate it with a generated SecretKey under the alias key
if it does not already exist.
/] /subsystem=elytron/secret-key-credential-store=mycredstore:add(path=propcredstore.cs, relative-to=jboss.server.config.dir)
{"outcome" => "success"}
If you rely on automatic generation of a SecretKey be sure to create a backup of the key as soon as possible. If you were to loose the key we do not have a mechanism to decrypt anything
encrypted with the lost key.
|
16.2. Credential Manipulation
The contents of the credential stores can be manipulated using either the command line tool or by using management operations. Unlike key stores the credential store APIs allow for multiple entries to be stored under a single alias provided each entry is of a different credential type.
16.2.1. Adding Credentials
The different credential store implementations support different credential types as illustrated in this table.
Credential Type | KeyStoreCredentialStore | PropertiesCredentialStore |
---|---|---|
PasswordCredential |
Supported |
Unsupported |
KeyPairCredential |
Supported |
Unsupported |
SecretKeyCredential |
Supported |
Supported |
As with all the manipulation options it is possible to use either the command line tool or management operations against a running server to modify the contents of the store.
Care should be taken when using the command line tool to ensure the store is not currently in use by any other processes. If the store is in use by another process which updates the contents of the store changes made by the tool could be lost. |
PasswordCredential
Using the tooling it is possible to add a clear text password as a credential.
Command Line
The following command adds a new credential with an alias of example
to the store using the command line tool: -
]$ bin/elytron-tool.sh credential-store --add=example --location=standalone/configuration/mycredstore.cs
Credential store password:
Secret to store:
Confirm secret to store:
Alias "example" has been successfully stored
Management Operation
Using a management operation the following command can be used to add a new alias of example
to the credential store.
[standalone@localhost:9990 /] /subsystem=elytron/credential-store=mycredstore:add-alias(alias=example, secret-value=ExamplePassword)
{
"outcome" => "success",
"result" => undefined
}
KeyPairCredential
Command Line
The following command allows you to import a key pair credential with an alias of example
from a file containing
a private key in OpenSSH format:
]$ bin/elytron-tool.sh credential-store --import-key-pair example --private-key-location /home/user/.ssh/id_rsa --location=standalone/configuration/mycredstore.cs
Credential store password:
Confirm credential store password:
Passphrase to be used to decrypt private key (can be nothing if no passphrase was used to encrypt the key): secret
Confirm passphrase to be used to decrypt private key (can be nothing if no passphrase was used to encrypt the key): secret
Alias "example" has been successfully stored
The following command allows you to import a key pair credential with an alias of example
by specifying a private key in OpenSSH format :
]$ bin/elytron-tool.sh credential-store --import-key-pair example --private-key-string="-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCdRswttV
UNQ6nKb6ojozTGAAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz
dHAyNTYAAABBBAKxnsRT7n6qJLKoD3mFfAvcH5ZFUyTzJVW8t60pNgNaXO4q5S4qL9yCCZ
cKyg6QtVgRuVxkUSseuR3fiubyTnkAAADQq3vrkvuSfm4n345STr/i/29FZEFUd0qD++B2
ZoWGPKU/xzvxH7S2GxREb5oXcIYO889jY6mdZT8LZm6ZZig3rqoEAqdPyllHmEadb7hY+y
jwcQ4Wr1ekGgVwNHCNu2in3cYXxbrYGMHc33WmdNrbGRDUzK+EEUM2cwUiM7Pkrw5s88Ff
IWI0V+567Ob9LxxIUO/QvSbKMJGbMM4jZ1V9V2Ti/GziGJ107CBudZr/7wNwxIK86BBAEg
hfnrhYBIaOLrtP8R+96i8iu4iZAvcIbQ==
-----END OPENSSH PRIVATE KEY-----"
--location=standalone/configuration/mycredstore.cs
Credential store password:
Confirm credential store password:
Passphrase to be used to decrypt private key (can be nothing if no passphrase was used to encrypt the key): secret
Confirm passphrase to be used to decrypt private key (can be nothing if no passphrase was used to encrypt the key): secret
Alias "example" has been successfully stored
If specifying your key in PKCS format rather than OpenSSH format, you must specify both the private and public key. The PKCS private key must also not be encrypted with a passphrase. |
Alternatively to importing, you may use the command line tool to generate and store a key pair credential in a credential store.
The following command allows you to generate and store a key pair credential under the alias example
using the ecdsa algorithm:
]$ bin/elytron-tool.sh credential-store --generate-key-pair example --algorithm EC --location=standalone/configuration/mycredstore.cs
Credential store password:
Confirm credential store password:
Alias "example" has been successfully stored
You can then export the public key generated in OpenSSH format using the following command:
]$ bin/elytron-tool.sh credential-store --export-key-pair-public-key example
Credential store password:
Confirm credential store password:
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMfncZuHmR7uglb0M96ieArRFtp42xPn9+ugukbY8dyjOXoi
cZrYRyy9+X68fylEWBMzyg+nhjWkxJlJ2M2LAGY=
SecretKeyCredential
Command Line
Each of the examples in this section uses the KeyStoreCredentialStore
to use the PropertiesCredentialStore
this can be specified on the command line by adding the --type PropertiesCredentialStore
parameter to the command line.
A new secret key can be generated with the following command.
]$ bin/elytron-tool.sh credential-store --generate-secret-key=example --location=standalone/configuration/mycredstore.cs
Credential store password:
Alias "example" has been successfully stored
By default this will create a 256 bit secret key, if either a 128 bit or 192 bit secret key is desired this can be specified with the --size=128
or --size=192
parameters respectively.
An existing secret key can be exported with the following command.
]$ bin/elytron-tool.sh credential-store --export-secret-key=example --location=standalone/configuration/mycredstore.cs
Credential store password:
Exported SecretKey for alias example=RUxZAUucgH8RSMNvoUj/rMz+pBZddttGCuT9of4TgfYLnN5Z1w==
The exported key uses a custom representation to allow Elytron to recognise exported keys. |
Finally a previously exported secret key can be imported with the following command.
]$ bin/elytron-tool.sh credential-store --import-secret-key=imported --location=standalone/configuration/mycredstore.cs
Credential store password:
SecretKey to import: RUxZAUucgH8RSMNvoUj/rMz+pBZddttGCuT9of4TgfYLnN5Z1w==
Alias "imported" has been successfully stored
It is possible to also specify the key to import on the command line e.g. --key=RUxZAUucgH8RSMNvoUj/rMz+pBZddttGCuT9of4TgfYLnN5Z1w==
but this would be vieweable by others users that can consult running processes and might also be cached in the history of the shell executing the commands.
Management Operations
For secret key manipulation the same set of command are available for both the credential-store
resource and the secret-key-credential-store
resource.
A new secret key can be generated with the following command.
[standalone@localhost:9990 /] /subsystem=elytron/credential-store=mycredstore:generate-secret-key(alias=example)
{"outcome" => "success"}
To generate either a 128 bit key or 192 bit key the parameter key-size=128
or key-size=192
can be specified respectively.
An existing secret key can be exported with the following command.
[standalone@localhost:9990 /] /subsystem=elytron/credential-store=mycredstore:export-secret-key(alias=example)
{
"outcome" => "success",
"result" => {"key" => "RUxZAUs+Y1CzEPw0g2AHHOZ+oTKhT9osSabWQtoxR+O+42o11g=="}
}
Finally a previously exported secret key can be imported with the following commands.
[standalone@localhost:9990 /] history --disable
[standalone@localhost:9990 /] /subsystem=elytron/credential-store=mycredstore:import-secret-key(alias=imported, key="RUxZAUs+Y1CzEPw0g2AHHOZ+oTKhT9osSabWQtoxR+O+42o11g==")
{"outcome" => "success"}
[standalone@localhost:9990 /] history --enable
In this last example we also temporarily disable the history in the CLI to prevent the key from being cached in the CLI hisory.
16.2.2. Listing Aliases
It is possible to list the aliases contained within the credential store, however it is not possible to list the actual values stored.
16.2.3. Removing Credentials
Finally, it is also possible to remove an alias from the credential store.
Command Line
Using the WildFly Elytron Tool the following command will remove an alias from the store.
]$ bin/elytron-tool.sh credential-store --remove=example --location=standalone/configuration/mycredstore.cs
Credential store password:
Confirm credential store password:
Alias "example" has been successfully removed
Management Operation
The following management operation can be used to remove an alias from the credential store.
/] /subsystem=elytron/credential-store=mycredstore:remove-alias(alias=example)
{
"outcome" => "success",
"result" => undefined,
"response-headers" => {"warnings" => [{
"warning" => "Update dependent resources as alias 'example' does not exist anymore",
"level" => "WARNING",
"operation" => {
"address" => [
("subsystem" => "elytron"),
("credential-store" => "mycredstore")
],
"operation" => "remove-alias"
}
}]}
}
By default the credential-store
resource assumes the type to be removed is PasswordCredential
. If a different type is to be removed it can be specified with the entry-type=SecretKeyCredential
parameter. The secret-key-credential-store
only holds secret keys so the entry type never needs to be specified.
16.3. Referencing Credentials
After being able to populate and manipulate a credential store the next step is being able to reference the stored credential so that it can be used.
16.3.1. Management Model References
Various resources that make use of credentials across the application server’s management model contain credential-reference
attributes that can be used either to specify a clear-password
or to cross-reference a credential from within a configured credential store.
The following is an example of how to define a key-store
within the Elytron subsystem specifying a clear text password to access the store.
/] /subsystem=elytron/key-store=exampleKS:add(relative-to=jboss.server.config.dir, path=example.keystore, \
type=JCEKS, credential-reference={clear-text=ExamplePassword})
{"outcome" => "success"}
To reference a credential from the previously defined credential store the following command could be used instead.
/] /subsystem=elytron/key-store=exampleKS:add(relative-to=jboss.server.config.dir, path=example.keystore, type=JCEKS, credential-reference={store=mycredstore, alias=example})
{"outcome" => "success"}
The above command assumes that the referenced credential already exists in the previously defined credential store. The next section will describe how credentials can automatically be added to the previously defined credential store.
16.3.2. Automatic Updates of Credential Stores
Instead of needing to add a credential to a previously defined credential store in order to reference it from a credential-reference
,
it is possible to have the credential get added automatically to the previously defined credential store by specifying both
the store
and clear-text
attributes for the credential-reference
. In particular, when adding a new credential-reference
with both the store
and clear-text
attributes specified:
-
If the
alias
attribute is also specified, then one of the following will occur:-
If the previously defined credential store does not contain an entry for the given alias, a new entry will be added to the credential store to hold the clear text password that was specified. The
clear-text
attribute will then be removed from the management model. -
If the credential store does contain an entry for the given alias, the existing credential will be replaced with the clear text password that was specified. The
clear-text
attribute will then be removed from the management model.
-
-
If the
alias
attribute is not specified, an alias will be generated and a new entry will be added to the credential store to hold the clear text password that was specified. Theclear-text
attribute will then be removed from the management model.
As an example, the following CLI command will result in a new entry being added to the previously defined credential
store, mycredstore
, with alias myNewAlias
and credential myNewPassword
:
/subsystem=elytron/key-store=exampleKS:add(relative-to=jboss.server.config.dir, path=example.keystore, type=JCEKS, credential-reference={store=mycredstore, alias=myNewAlias, clear-text=myNewPassword})
{
"outcome" => "success",
"result" => {"credential-store-update" => {
"status" => "new-entry-added",
"new-alias" => "myNewAlias"
}}
}
When updating an existing credential-reference
that contains both the alias
and store
attributes to also specify
the clear-text
attribute:
-
The existing credential in the previously defined credential store will be replaced with the clear text password that was specified. The
clear-text
attribute will then be removed from the management model.
As an example, the following CLI command will result in updating the credential for the myNewAlias
entry that was just
added to the previously defined credential store:
/subsystem=elytron/key-store=exampleKS:write-attribute(name=credential-reference.clear-text,value=myUpdatedPassword)
{
"outcome" => "success",
"result" => {"credential-store-update" => {"status" => "existing-entry-updated"}},
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
If an operation that includes a parameter fails for any reason,
no automatic credential store update will take place, i.e., any credential store that was
specified via the attribute will contain the same contents as it
did before the operation was executed.
|
16.3.3. wildfly-config.xml
If you are making use of the wildfly-config.xml
descriptor it is also possible to define a credential store within this descriptor to obtain credentials without requiring them to be in-lined within the configuration.
As an example the CLI can be executed with a configuration:
]$ bin/jboss-cli.sh -c -Dwildfly.config.url=bin/wildfly-config.xml
Without using a credential store the username and credential can be specified in the clear e.g.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<authentication-client xmlns="urn:elytron:1.0">
<authentication-rules>
<rule use-configuration="default" />
</authentication-rules>
<authentication-configurations>
<configuration name="default">
<sasl-mechanism-selector selector="DIGEST-MD5" />
<providers>
<use-service-loader/>
</providers>
<set-user-name name="User" />
<credentials>
<clear-password password="UserPassword" />
</credentials>
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
However, it is possible to move this password to the credential store and update the configuration to load it from the store e.g.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<authentication-client xmlns="urn:elytron:1.0">
<credential-stores>
<credential-store name="mycredstore">
<attributes>
<attribute name="keyStoreType" value="JCEKS" />
<attribute name="location" value="standalone/configuration/mycredstore.cs" />
</attributes>
<protection-parameter-credentials>
<clear-password password="StorePassword" />
</protection-parameter-credentials>
</credential-store>
</credential-stores>
<authentication-rules>
<rule use-configuration="default" />
</authentication-rules>
<authentication-configurations>
<configuration name="default">
<sasl-mechanism-selector selector="DIGEST-MD5" />
<providers>
<use-service-loader/>
</providers>
<set-user-name name="User" />
<credentials>
<credential-store-reference store="mycredstore" alias="User" />
</credentials>
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
Within this second example the key changes being the addition of the <credential-stores />
section and updating the <credentials/>
section to use a <credential-store-reference/>
to specify which credential store should be used and which alias from that credential store should be used.
In the above example, the credential store’s protection parameter is specified as a clear password, but it is also possible to specify it as a masked password.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<authentication-client xmlns="urn:elytron:1.4">
<credential-stores>
<credential-store name="mycredstore">
<attributes>
<attribute name="keyStoreType" value="JCEKS" />
<attribute name="location" value="standalone/configuration/mycredstore.cs" />
</attributes>
<protection-parameter-credentials>
<masked-password masked-password="M3loEZ7uua1X1PiYCYJDpg==" iteration-count="100" salt="12345678"/>
</protection-parameter-credentials>
</credential-store>
</credential-stores>
<authentication-rules>
<rule use-configuration="default" />
</authentication-rules>
<authentication-configurations>
<configuration name="default">
<sasl-mechanism-selector selector="DIGEST-MD5" />
<providers>
<use-service-loader/>
</providers>
<set-user-name name="User" />
<credentials>
<credential-store-reference store="mycredstore" alias="User" />
</credentials>
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
16.4. CredentialStore APIs
It is also possible to make use of the CredentialStore APIs directly. This could be useful for applications that require access to securely stored credentials. This could also be an option for an application to populate a credential store for use elsewhere.
The following code demonstrates how to obtain an initialised instance of KeyStoreCredentialStore
so it can be used to store and retrieve credentials.
Password storePassword = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, "StorePassword".toCharArray());
ProtectionParameter protectionParameter = new CredentialSourceProtectionParameter(IdentityCredentials.NONE.withCredential(new PasswordCredential(storePassword)));
CredentialStore credentialStore = CredentialStore.getInstance("KeyStoreCredentialStore", CREDENTIAL_STORE_PROVIDER);
Map<String, String> configuration = new HashMap<>();
configuration.put("location", "mystore.cs");
configuration.put("create", "true");
credentialStore.initialize(configuration, protectionParameter);
The following code illustrates how a couple of different credential types can be added to a credential store:
Password clearPassword = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, "ExamplePassword".toCharArray());
credentialStore.store("clearPassword", new PasswordCredential(clearPassword));
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
credentialStore.store("secretKey", new SecretKeyCredential(secretKey));
These credentials can then be obtained again from the store:
Password password = credentialStore.retrieve("clearPassword", PasswordCredential.class).getPassword();
SecretKey secretKey = credentialStore.retrieve("secretKey", SecretKeyCredential.class).getSecretKey();
As the type is specified when retrieving a credential it is possible to store multiple credentials under the same alias. |
Please use the published javadoc for more information in relation to the APIs and the credential types supported within WildFly Elytron.
16.5. Migrating Existing Vaults
If migrating from a prior version of the application server it is possible that you already are making use of a PicketBox vault for the storage of clear text passwords. The tooling provided can be used to convert the vault to the format used by the KeyStoreCredentialStore
.
Within the WildFly Elytron command line tool an additional command vault
is available specifically for the conversion of legacy vaults to a credential store. A complete vault can be converted to a credential store with the following command: -
]$ bin/elytron-tool.sh vault --enc-dir standalone/configuration/vault --keystore standalone/configuration/vault.keystore --iteration 44 --salt 00000000 --alias vault \
--location standalone/configuration/newcredstore.cs
Vault password:
Confirm vault password:
Vault (enc-dir="standalone/configuration/vault";keystore="standalone/configuration/vault.keystore") converted to credential store "standalone/configuration/newcredstore.cs"
When executing this command the destination credential store must not already exist. The password used for the credential store will be the password originally used for the vault.
Entries stored within the vault would have been stored specifying a "block" and "alias" value; within the credential store the new alias will be block::alias
.
16.6. Custom Credential Store
It is also possible to provide custom credential store implementations. Overall the pattern to implementing a custom credential store is very similar to the pattern that would be followed to implement a custom key store.
-
Extend the SPI
-
Implement a
java.security.Provider
to register the implementation.
The SPI to be extended is org.wildfly.security.credential.store.CredentialStoreSpi
. The custom implementation will be required to implement the following methods.
public abstract void initialize(Map<String, String> attributes, CredentialStore.ProtectionParameter protectionParameter, Provider[] providers) throws CredentialStoreException;
This method is required to perform the initialisation of the credential store, by taking in a Map
of attributes it allows for custom configuration to be provided as required by the store.
public abstract boolean isModifiable();
A credential store needs to advertise if it supports modifications so clients can determine if the modification APIs can be used.
public abstract <C extends Credential> C retrieve(String credentialAlias, Class<C> credentialType, String credentialAlgorithm, AlgorithmParameterSpec parameterSpec, CredentialStore.ProtectionParameter protectionParameter) throws CredentialStoreException;
The retrieve
method is essential for all credential store implementations to retrieve credentials of a specific type using the alias specified.
In addition to retrieve
there are two more methods that can optionally be implemented.
public boolean exists(String credentialAlias, Class<? extends Credential> credentialType) throws CredentialStoreException;
public Set<String> getAliases() throws UnsupportedOperationException, CredentialStoreException;
A default implementation of exists
already is implemented which checks if a call to retrieve
returns a credential as requested. However it could be optimal to check the existence of a credential without actually loading it. The getAliases
method is optional as some implementations may only be able to retrieve a credential by name rather than query all available credentials.
The next set of methods to implement are the methods needed for updates to be applied to the underlying credential store.
public abstract void store(String credentialAlias, Credential credential, CredentialStore.ProtectionParameter protectionParameter)
throws CredentialStoreException, UnsupportedCredentialTypeException;
public abstract void remove(String credentialAlias, Class<? extends Credential> credentialType, String credentialAlgorithm, AlgorithmParameterSpec parameterSpec) throws CredentialStoreException;
public void flush() throws CredentialStoreException;
The store
and remove
methods either add credentials to a credential store or remove them. Implementing the flush
method is optional but this method can be used as a trigger for a store to persist its state.
The final stage is to provide an implementation of java.security.Provider
which can return an instance of the SPI for the CredentialStore
service type. The WildFly Elytron provider which makes the Elytron implementations available is org.wildfly.security.credential.store.WildFlyElytronCredentialStoreProvider
. The source code for this provider can be used as an example.
16.7. Reference
The previous sections have made use of either the WildFly Elytron Tool or the management operations and specified the arguments and configuration options required for the action being performed. These operations and tools however support a variety of other options so this section provides some additional detail.
16.7.1. Elytron Tool - credential-store
Command
Examples of how to structure calling the credential-store
command were provided earlier. When using the credential-store
command the following actions are possible: -
Action | Description |
---|---|
-a,--add <alias> |
Add a new entry to the credential store using the specified alias. |
-c,--create |
Create a new credential store instance. |
-e,--exists <alias> |
Test if the specified alias already exists in the credential store. |
-r,--remove <alias> |
Remove the alias specified from the credential store. |
-g,--generate-key-pair <alias> |
Generate a new key pair credential and add it as an entry to the credential store using the specified alias. |
--generate-secret-key <alias> |
Generate a new secret key credential and add it as an entry to the credential store using the specified alias. |
--export-secret-key <alias> |
Export a secret key credential identified using the specified alias. |
-xp,--export-key-pair-public-key <alias> |
Display the public key of a key pair credential entry under the specified alias in OpenSSH format. |
--import-secret-key <alias> |
Import a secret key credential and add it as an entry to the credential store using the specified alias. |
-ikp,--import-key-pair <alias> |
Add a new key pair credential entry to the credential store using the specified alias. |
--encrypt <alias> |
Encrypt a clear text string using the secret key stored under the specified alias. |
-v,--aliases |
Display all aliases |
-f,--summary |
Print summary, especially command how to create this credential store |
-h,--help |
Get help with usage of this command |
The following parameters can be provided for each action to specify how to load the store.
Parameter | Description |
---|---|
-d,--debug |
Print stack trace when error occurs. |
-i,--iteration <arg> |
Iteration count for final masked password of the credential store |
-l,--location <loc> |
Location of credential store storage file |
-n,--entry-type <type> |
Type of entry in credential store |
-o,--other-providers <providers> |
Comma separated list of Jakarta Connectors provider names. Providers will be supplied to the credential store instance. Each provider must be installed through java.security file or through service loader from properly packaged jar file on classpath. |
-p,--password <pwd> |
Password for credential store |
-q,--credential-store-provider <cs-provider> |
Provider name containing CredentialStoreSpi implementation. Provider must be installed through java.security file or through service loader from properly packaged jar file on classpath. |
-s,--salt <arg> |
Salt to apply for final masked password of the credential store |
-t,--type <type> |
Credential store type |
-u,--properties <arg> |
Implementation properties for credential store type in form of "prop1=value1; … ;propN=valueN" |
-x,--secret <secret to store> |
Password credential value |
The following parameters can be provided for the generate-key-pair
command:
Parameter | Description | Default Value |
---|---|---|
-k, --algorithm <algorithm name> |
The encryption algorithm to be used. One of: RSA, DSA, or EC |
RSA |
-j,--size <size in bytes> |
Size of the private key in bytes |
RSA: 2048, DSA: 2048, EC: 256 |
The following parameter can be provided for the generate-secret-key
command:
Parameter | Description | Default Value |
---|---|---|
--size |
Size of the secret key in bits, can be one of 128, 192, or 256. |
256 |
The following parameters can be provided for the import-key-pair
command:
Parameter | Description |
---|---|
-pvk, --private-key-string <private key to store> |
The private key as a string. Alternative to |
-pvl, --private-key-location <path> |
The path to a file containing a private key. Alternative to |
-pbk, --public-key-string <public key to store> |
The public key as a string. Alternative to |
-pbl, --public-key-location <path> |
The path to a file containing a public key. Alternative to |
-kp, --key-passphrase <passphrase> |
The passphrase used to decrypt the private key if needed. Can also be specified via prompt |
The following parameter can be provided for the import-secret-key
command:
Parameter | Description | Default Value |
---|---|---|
--key |
The secret key to be imported, if not specified the key will be prompted for. |
N/A |
The following parameters can be provided for the encrypt
command:
Parameter | Description | Default Value |
---|---|---|
--clear-text <clear text> |
The clear text string to encrypt, if omitted this wil be prompted for. |
N/AThe following parameters can be provided for the .generate-key-pair Parameters |
|Parameter |Description |Default Value
|-k, --algorithm <algorithm name> |The encryption algorithm to be used. One of: RSA, DSA, or EC |RSA
16.7.2. Elytron Tool - vault
Command
The vault
command is used to convert a legacy vault to a credential store and supports the following parameters.
Parameter | Description |
---|---|
-b,--bulk-convert <description file> |
Bulk conversion with options listed in description file. |
-d,--debug |
Print stack trace when error occurs. |
-e,--enc-dir <dir> |
Vault directory containing encrypted files (defaults to "vault") |
-f,--summary |
Print summary of conversion |
-h,--help |
Get help with usage of this command |
-i,--iteration <arg> |
Iteration count (defaults to "23") |
-k,--keystore <keystore> |
Vault keystore URL (defaults to "vault.keystore") |
-l,--location <loc> |
Location of credential store storage file (defaults to "converted-vault.cr-store" in vault encryption directory) |
-o,--other-providers <providers> |
Comma separated list of Jakarta Connectors provider names. Providers will be supplied to the credential store instance. Each provider must be installed through java.security file or through service loader from properly packaged jar file on classpath. |
-p,--keystore-password <pwd> |
Vault keystore password, used to open original vault key store, and used as password for new converted credential store |
-q,--credential-store-provider <cs-provider> |
Provider name containing CredentialStoreSpi implementation. Provider must be installed through java.security file or through service loader from properly packaged jar file on classpath. |
-s,--salt <salt> |
8 character salt (defaults to "12345678") |
-t,--type <type> |
Converted credential store type (defaults to "KeyStoreCredentialStore") |
-u,--properties <arg> |
Configuration parameters for credential store in form of: "parameter1=value1; … ;parameterN=valueN" |
-v,--alias <arg> |
Vault key alias within key store (defaults to "vault") |
16.7.3. KeyStoreCredentialStore
When configuring the KeyStoreCredentialStore
the following configuration options are supported.
Name | Default | Description |
---|---|---|
create |
false |
If the credential store does not exist should it be created? |
cryptographicAlgorithm |
AES/CBC/NoPadding |
The algorithm to use when using an external store. |
external |
false |
Should external storage be used? |
externalPath |
N/A |
Path to external storage. |
keyAlias |
cs_key |
The alias to use from the KeyStore when working with external storage. |
keyStoreType |
|
The type of the key store used for the credential store. |
location |
N/A |
The location of the credential store. |
modifiable |
true |
Should the store be modifiable via the exposed API. |
17. Encrypted Expressions
WildFly Elytron provides support for handling encrypted expressions in the management model using a SecretKey
from a CredentialStore
to decrypt the expression at runtime.
For information regarding how to create, configure, and populate credential stores including the manipulation of secret keys please refer to the Credential Store chapter.
Within the elytron
subsystem one or more resolvers
can be defined to handle the decryption of a previously encrypted expression. Each resolver will reference a single secret key in a
single credential store which it will use to decrypt the expressions. Encrypted expressions can take one of two forms:
-
${ENC::ResolverName:RUxZAUMQXUj3qP1hbXyO5PpmsbgkepjoscIf3tKXvGiPDXYqNAc=}
-
${ENC::RUxZAUMQXUj3qP1hbXyO5PpmsbgkepjoscIf3tKXvGiPDXYqNAc=}
In both cases the ENC
prefix is used to identify the expression is an encrypted expression. This prefix is the default however it is possible to define an alternative prefix for use
across the configuration.
Within the first example the ResolverName
is the name of an individual resolver definition. The name of the resolver has been omitted in the second example as it is also possible
to define a default resolver which will be used if no resolver is specified within the expression.
17.1. expression=encryption resource
Support for decrypting expressions is enabled by defining a singleton expression=encryption
resource within the elytron
subsystem.
This resource can be defined with the following attributes.
-
prefix
(DefaultENC
) - The prefix used within the encrypted expressions. -
default-resolver
(Optional) - For expressions that do not define a resolver, the default resolver to use. -
resolvers
- A lit of one more named resolver definitions.
An individual resolver is defined with three attributes:
-
name
- The name of the individual configuration used to reference it. -
credential-store
- Reference to the credential store instance that contains the secret key this resolver will use. -
secret-key
- The alias of the secret key within the credential store to use.
The following is an example CLI command to define an expression=encryption
resource in the elytron
subsystem with two resolver definitions one if which is a
default and an alternative prefix.
[standalone@localhost:9990 /] /subsystem=elytron/expression=encryption:add(prefix=ENCRYPTED, default-resolver=one, \
resolvers=[{name=one, credential-store=store-one, secret-key=key}, \
{name=two, credential-store=store-two,secret-key=key}])
{"outcome" => "success"}
17.2. Creating Expressions
The following section illustrates how to create an encrypted expression. If you repeat the same command for the same clear text it is normal that a different result is returned for the same key. This is because a unique initialisation vector is used for each call. |
17.2.1. Management Operation
Once the expression=encryption
resource has been defined the create-expression
management operation can be called to generate an expression using the referenced
secret key.
When using management operations to create expressions from a clear-text string remember to disable the history in the CLI first otherwise the clear text string will be cached.
|
In these examples the expression=encryption
resource has been configured to use the default prefix.
[standalone@localhost:9990 /] history --disable
[standalone@localhost:9990 /] /subsystem=elytron/expression=encryption:create-expression(resolver=two, clear-text=MyPassword)
{
"outcome" => "success",
"result" => {"expression" => "${ENC::two:RUxZAUMQcUVLxqjt8zh8FabcA+wcnux+mqfLE27sfmHfKNG9BcY=}"}
}
[standalone@localhost:9990 /] history --enable
When using the create-expression
operation the resolver
attribute can be ommited, in that case the default-resolver
will be used instead.
[standalone@localhost:9990 /] history --disable
[standalone@localhost:9990 /] /subsystem=elytron/expression=encryption:create-expression(clear-text=MyPassword)
{
"outcome" => "success",
"result" => {"expression" => "${ENC::RUxZAUMQu7biKBAwtUi+to+BlQnbjK3URUHMUDh8ReTlN0Alao0=}"}
}
[standalone@localhost:9990 /] history --enable
In both cases the resulting expression can be used on any attribute of a resource which supports the use of expressions.
17.2.2. Command Line Tool
Once a credential store has been populated with a secret key the credential-store
command of the elytron-tool
can also be used to create the encrypted string to include in an expression.
]$ bin/elytron-tool.sh credential-store --location standalone/configuration/store-one.cs --type PropertiesCredentialStore --encrypt key
Clear text value:
Confirm clear text value:
Clear text encrypted to token 'RUxZAUMQvGzk6Vaadp2cahhZ6rlPhHOZcWyjXALlAthrENvRTvQ=' using alias 'key'.
The --encrypt
action is used with the credential-store
command, the argument to this action is the alias of the secret key to use. In this form the tool will prompt
twice for the clear text which is being encrypted. When using the command line tool the output is just the Base64 encoded encrypted ciphertext. To use this in the management model
it will need to be included in an expression as described earlier i.e. using the appropriate prefix and if required resolver name.
When using the --encrypt
action it is also possible to pass in --clear-text
parameter to pass in the clear text directly but this may be visible to other users and may also
be cached in the command history of your shell.
17.3. Domain Mode
When using encrypted expressions in domain mode things are slightly different to how the legacy vault may have been used in the past.
To make use of encrypted expressions in the host controller configuration the expression=encryption
resource and relevant *credential-store
definitions must be defined
within the elytron
subsystem definition of the host controller i.e. in the same host.xml
configuration.
For expressions within a domain profile being used to configure one or more servers the expression=encryption
resource and relevant *credential-store
definitions must be defined within the elytron
subsystem
definition of the same profile.
The runtime management operations are not supported against the expression=encryption
or *credential-store
when defined within a domain profile so for these environments the credential store and relevant
expressions should be created offline before defining in the model. It is possible to reference a common credential store file shared between the host controller management model and the domain profile but after making
any updates to the credential store from the host controller the application server processes will need to be restarted to force them to reload the credential store.
18. Custom Components
WildFly Elytron subsystem allows adding custom implementation of different components in form of WildFly modules into the WildFly instance and use them by the same way as built-in Elytron subsystem components. For example, you can create custom security event listener to develop custom audit mechanisms and store information about user authentication attempts in custom storage structure. Or you can create custom security realm to authenticate users against your own identities storage.
To find what types of custom components you can implement you can use Tab completion:
[standalone@localhost:9990 /] /subsystem=elytron/custom- custom-credential-security-factory custom-realm custom-evidence-decoder custom-realm-mapper custom-modifiable-realm custom-role-decoder custom-permission-mapper custom-role-mapper custom-principal-decoder custom-security-event-listener custom-principal-transformer
18.1. Security event listener
This section describes how to add a custom security event listener. Similar steps can be followed to add another custom component type.
To create custom security event listener you need to implement java.util.function.Consumer<org.wildfly.security.auth.server.event.SecurityEvent>
interface.
Resulting class needs to be packed into JAR and WildFly module created.
You can create appropriate directory structure and module descriptor manually, or you can use following command of WildFly CLI:
bin/jboss-cli.sh module add --name=my.module --resources=my-listener.jar --dependencies=org.wildfly.security.elytron
Check Class loading doc for more information how to create WildFly module.
When appropriate module is on place, you can start using it by adding Elytron subsystem resource:
/subsystem=elytron/custom-security-event-listener=myListener:add( module=my.module, class-name=my.module.MyAuditListener)
Now you can start to use it as any other security event listener - typically to set it as listener of ApplicationDomain
:
/subsystem=elytron/security-domain=ApplicationDomain:write-attribute(name=security-event-listener, value=myListener)
After server reload the listener will receive all security events from given security domain.
18.2. Configurable custom components
You can also provide some component configuration from the subsystem, if class of your component will implement following method:
public void initialize(Map<String, String> configuration)
Afterwards you can provide configuration into your component from the subsystem using attribute configuration
:
/subsystem=elytron/custom-...=my-component:add(module=..., class-name=..., configuration={myAttribute="myValue"})
After the component construction, the initialize
method will be called with the configuration.
19. Elytron WildFly Java Security Manager
19.1. Overview
19.1.1. General introduction
The Elytron WildFly Java Security Manager is build on top of and within the Java SE Platform Security Architecture.
The Java Enterprise Edition (EE) 7 specification introduced a new feature which allows application developers to specify a Java Security Manager (JSM) policy for their Java EE applications, when deployed to a compliant Java EE Application Server such as WildFly. Until now, writing JSM policies has been pretty tedious. Now a new tool has been developed which allows the generation of a JSM policy for deployments running on WildFly. It is possible that running with JSM enabled may affect performance, JEP 232 indicates the performance impact would be 10-15%, but it is still recommended to test the impact per application.
Elytron WildFly Java Security Manager basic enhancements are
-
Support of JavaEE 7 (and newer) Java EE application permissions schema to allow declarative security per application deployment.
-
Support of minimum and maximum permissions per server instance.
-
Support of a "log-only" mode to ease development and testing of permissions.
-
Output of debug information to detect missing permissions.
-
Output of trace information to analyse special situations with protection domains, classloaders, and codesources.
Why Run with the Java Security Manager Enabled?
Running a JSM will not fully protect the server from malicious attackers exploiting security vulnerabilities. It does, however, offer another layer of protection which can help reduce the impact of serious security vulnerabilities, such as deserialization attacks. For example, most of the recent attacks against Jackson Databind rely on making a Socket connection to an attacker-controlled JNDI Server to load malicious code. This article provides information on how this issue potentially affects an application written for JBoss EAP, which would be same for WildFly. The Security Manager could block the socket creation, and potentially thwart the attack.
JavaSE 17 Deprecate the Security Manager for Removal
The community decided to deprecate the Java Security Manager with Java 17. The full details are described in JEP 411: Deprecate the Security Manager for Removal. WildFly 31 still tests and actively supports the Security Manager. Used in conjunction with other tools such as Serialization Filtering the JSM is still a good defense in depth measure.
Multi-Layer Security
The Elytron WildFly Java Security Manager is just one piece in a multi-layer security with Defense in depth. Take care to harden your environment and minimize rights of the process for example with SELinux and systemd service hardening.
19.2. The Security manager subsystem
19.2.1. Enabling the Security Manager
You can enable the security manager with one of the following options:
./standalone.sh -secmgr # command line argument or
./standalone.sh SECMGR=true
The server startup log should indicate this
INFO [org.jboss.as] (MSC service thread 1-8) WFLYSRV0235: Security Manager is enabled
The use of
as described in Managing Applets and Applications has been removed from WildFly.-Djava.security.manager
19.2.2. Security Policy Support
You can still utilize a custom security policy file with the -Djava.security.policy
option. This is useful for special cases, for example a java agent. The default is the JVM provided policy.
./standalone.sh -secmgr -Djava.security.policy=file://$JBOSS_HOME/standalone/configuration/customjsm.policy
-Djava.security.policy=
with one equal sign (=
) utilizes the default policy plus the custom policy.
-Djava.security.policy==
with two equal signs (==
) utilizes only the custom policy, take care for the necessary rights of the JVM in this case.
Property replacement in security policies
You can use variables in custom security policies which are resolved at runtime. You have to specify them on the command line or in the
file. Properties in the server configuration are not available at the time of variable resolution on startup for the JSM.$JBOSS_HOME/bin/standalone.conf
19.2.3. Log-only mode
The log-only mode could be described as JSM simulation mode: Every permission check will be done but the result will not be propagated to the system - no SecurityException will be thrown. It is NOT about enabling or disabling log entries, which is solely driven by the logging configuration. The log-only mode is controlled with the -Dorg.wildfly.security.manager.log-only
option. The default is false.
./standalone.sh -secmgr -Dorg.wildfly.security.manager.log-only=true
Do NOT use this option in production environments.
19.2.4. Logging Debug and trace
You can find a detailed description and how to about the command line interface and the logging configuration in the Admin Guide. The logger name to configure for the Elytron WildFly Security Manager is
.org.wildfly.security.access
WildFly Security Manager Debug logging
Debug is enabled via the following CLI commands
/subsystem=logging/logger=org.wildfly.security.access:add
/subsystem=logging/logger=org.wildfly.security.access:write-attribute(name=level,value=DEBUG)
A sample output will look like this:
DEBUG [org.wildfly.security.access] (Batch Thread - 1) Permission check failed (permission "("java.util.PropertyPermission" "java.io.tmpdir" "read")" in code source "(vfs:/content/batch-processing.war/WEB-INF/classes <no signer certificates>)" of "ModuleClassLoader for Module "deployment.batch-processing.war" from Service Module Loader")
WildFly Security Manager Trace logging
You might have a case where you need to find out what exactly caused a certain permission request. Or you have an error case with an null
classloader or null
codesource. To get a stacktrace you can enable org.wildfly.security.access
on log level TRACE
.
/subsystem=logging/logger=org.wildfly.security.access:add
/subsystem=logging/logger=org.wildfly.security.access:write-attribute(name=level,value=TRACE)
Attention: This generates a lot of log output and has a severe performance impact. It’s not intended for permanent activation in development or testing but for special cases only.
19.2.5. Minimum and maximum permissions
The
subsystem configures a maximum-set with the security-manager
by default:AllPermission
<deployment-permissions>
<maximum-set>
<permission class="java.security.AllPermission"/>
</maximum-set>
</deployment-permissions>
If you cannot configure a third-party blackbox deployment unit or you want to share a common set of permissions across multiple deployments you can add a
of permissions.
You could modify the minimum-set
, remove the AllPermission and setup further restrictions to permissions you are willing to grant to deployments.maximum-set
19.2.6. Property replacement in permissions.xml
Expression resolution in EE security manager deployment descriptors (permissions.xml and jboss-permissions.xml) is supported since WildFly 19. You can use the familiar
syntax in these deployment descriptors to allow customization of settings at runtime.${foo:true}
Enable in standalone mode
[standalone@localhost:9990 /] /subsystem=ee:write-attribute(name=jboss-descriptor-property-replacement,value=TRUE)
[standalone@localhost:9990 /] /subsystem=ee:write-attribute(name=spec-descriptor-property-replacement,value=TRUE)
Enable in domain mode
[domain@localhost:9990 /] /profile=*/subsystem=ee:write-attribute(name=jboss-descriptor-property-replacement, value=TRUE)
[domain@localhost:9990 /] /profile=*/subsystem=ee:write-attribute(name=spec-descriptor-property-replacement, value=TRUE)
19.2.7. Sample permissions.xml
The permissions.xml file has to be placed below META-INF of deployment unit. The following example shows some entries, including property replacement. This is especially useful for immutable artifacts.
<?xml version="1.0" encoding="UTF-8"?>
<permissions xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/permissions_10.xsd"
version="10">
<permission>
<class-name>java.util.PropertyPermission</class-name>
<name>*</name> <!-- many 3rd party APIs cache and require access to all properties -->
<actions>read, write</actions>
</permission>
<permission>
<class-name>java.lang.RuntimePermission</class-name>
<name>getClassLoader</name>
</permission>
<permission>
<class-name>java.io.FilePermission</class-name>
<name>${install.app.home}/a/folder/-</name> <!-- recursive in and below folder -->
<actions>read</actions> <!-- but not write, delete, execute -->
</permission>
<permission>
<class-name>java.io.FilePermission</class-name>
<name>${install.app.home}/b/folder</name> <!-- folder itself -->
<actions>read, write, delete</actions> <!-- but not execute -->
</permission>
<permission>
<class-name>java.io.FilePermission</class-name>
<name>${install.app.home}/b/folder/*</name> <!-- all IN the folder -->
<actions>read, write, delete</actions> <!-- but not execute -->
</permission>
<permission>
<class-name>java.net.URLPermission</class-name>
<name>${myserver.prot}://${myserver.hostname}:${myserver.port}/c/path/-</name> <!-- recursive in and below path -->
<actions>POST,GET,DELETE:*</actions> <!-- refer to JavaDoc for more samples -->
</permission>
</permissions>
19.3. How to generate a Java Security Manager Policy
19.3.1. Prerequisites
-
Java EE EAR or WAR file to add policies to;
-
Targeting WildFly 11 or later;
-
the Gradle build tool https://gradle.org/install/
-
Comprehensive test plan which exercises every "normal" function of the application.
If a comprehensive test plan isn’t available, a policy could be generated in a production environment, as long as some extra disk space for logging is available and there is confidence the security of the application is not going to be compromised while generating policies.
19.3.2. Setup 'Log Only' mode and 'debug' logging for the Security Manager
Please enable the log-only mode and debug logging.
19.3.3. Test the application to generate policy violations
For this example we’ll use the batch-processing quickstart. Follow the README to deploy the application and access it running on the application server at
. Click the 'Generate a new file and start import job' button in the Web UI and notice some policy violations are logged to the http://localhost:8080/batch-processing
file, for example:$JBOSS_HOME/standalone/log/server.log
DEBUG [org.wildfly.security.access] (Batch Thread - 1) Permission check failed (permission "("java.util.PropertyPermission" "java.io.tmpdir" "read")" in code source "(vfs:/content/batch-processing.war/WEB-INF/classes <no signer certificates>)" of "ModuleClassLoader for Module "deployment.batch-processing.war" from Service Module Loader")
19.3.4. Generate a policy file for the application
Checkout the source code for the wildfly-policygen project written by Red Hat Product Security.
git clone git@github.com:jasinner/wildfly-policygen.git
Set the location of the
file which contains the generated security violations in the server.log
script, i.e.:build.gradle
task runScript (dependsOn: 'classes', type: JavaExec) {
main = 'com.redhat.prodsec.eap.EntryPoint'
classpath = sourceSets.main.runtimeClasspath
args '$JBOSS_HOME/standalone/log/server.log'
}
Run wildfly-policygen using gradle, i.e.:
gradle runScript
A
file should be generated in the current directory. Using the example application, the file is called permissions.xml
. Copy that file to batch-processing.war.permissions.xml
, build, and redeploy the application, for example:src/main/webapp/META-INF/permissions.xml
cp batch-processing.war.permissions.xml $APP_HOME/src/main/webapp/META-INF/permissions.xml
Where APP_HOME is an environment variable pointing to the batch-processing application’s home directory.
19.3.5. Run with the security manager in enforcing mode
Recall that we set the org.wildfly.security.manager.log-only system property in order to log permission violations. Remove that system property or set it to
in order to enforce the JSM policy that’s been added to the deployment. Once that line has been changed or removed from false
, restart the application server, build, and redeploy the application.bin/standalone.conf
Also go ahead and remove the extra logging category that was added previously using the CLI, e.g.:
/subsystem=logging/logger=org.wildfly.security.access:remove
This time there shouldn’t be any permission violations logged in the server.log file.
19.4. Conclusion
While the Java Security Manager will not prevent all security vulnerabilities possible against an application deployed to WildFly, it will add another layer of protection, which could mitigate the impact of serious security vulnerabilities such as deserialization attacks. If running with Security Manager enabled, be sure to check the impact on the performance of the application to make sure it’s within acceptable limits. Finally, use of the wildfly-policygen tool is not officially supported by Red Hat, however issues can be raised for the project in Github or in the WildFly User Forum.
19.5. Further background
Additionally to the documentation of JBoss Modules and the Security Manager the following explanations have been extracted from an WildFly developer conversation.
19.5.1. Elytron WildFly Security Manager and Protection Domains
Within WildFly there are two types of ProtectionDomain:
-
Server module (everything under the modules folder).
-
Deployment.
Server modules automatically have the AllPermission granted. Deployments have a combination of their permission.xml as well as possibly the minimal permission set from the security manager subsystem.
Permission checks
As a call progresses from class to class, module to module, deployment to module a list of all of the protection domains of each of these builds up. When a permission check is performed it will only succeed if each and every protection domain on the call stack has been granted the permission.
This is where doPrivileged comes in, this is effectively saying "At this point in the callstack it is verified safe, forget the protection domains that called me." So when a deployment calls into a server module and that server module calls doPrivileged then the deployments module will not longer be a part of the permission check.
Adding a doPrivileged seems an obvious solution but when added one also need to think about how deployments could abuse this to get their protection domain dropped from the permissions check.
Privileged blocks in dependencies
Adding the privileged block in a dependency used by WildFly works because the protection domain of the server modules has all the permissions. It is important to understand how module all permissions and doPrivileged checks work combined for a deployment.
One point to start with there is nothing special about a PrivilegedAction: PrivilegedAction is an interface which allows a Runnable class to be passed in and also has a return type. If this was added later it could have been implemented with the functional interfaces. The special part is the doPrivileged call. Under normal circumstances every jar would be represented by it’s own protection domain and each jar could be assigned it’s own permissions. When the security manager performs it’s permissions check it would make sure every protection domain in the call stack has been granted that permission.
The WildFly modules case is slightly special as all the modules just get granted AllPermission, so we end up in a situation where deployments have a defined set of permissions and the WildFly modules have all - by default the security manager checks the permissions of both of these during a permission check that spans them - the WildFly module will of course always pass.
Because of WildFly’s model it is easy to get into the assumption that it is the doPrivileged call which is doing something special to bypass the security manager permissions check, it is not - all it is doing is dropping the protection domains from the call stack prior to that point, so we just end up with the protection domains for WildFly modules on the call stack which have the all permission.
So where you say the current protection domain where the dependency is has all permissions granted - yes that is it - the remaining protections domains on the call stack have the appropriate permissions granted.
The documentation might lead to the view the doPriviledge does some magical things that allowed your code run as trusted code that can bypass the permissions, for example at What It Means to Have Privileged Code, with sentences as:
Marking code as "privileged" enables a piece of trusted code to temporarily enable access to more resources than are available directly to the code that called it. This is necessary in some situations. For example, an application may not be allowed direct access to files that contain fonts, but the system utility to display a document must obtain those fonts, on behalf of the user. In order to do this, the system utility becomes privileged while obtaining the fonts.
This is all part of the argument for removing it, the confusion around how to apply the APIs - one could say the following sentence is correct but hides the important detail. "Marking code as "privileged" enables a piece of trusted code to temporarily enable access to more resources than are available directly to the code that called it."
If we have module A calling module B - by default the permissions check checks both. If module B contains a doPrivileged before the permission check then only module B’s protection domain will be checked.
The reason for using the doPrivileged is the assumption module B has the greater (or more appropriate permissions) and just want these compared without those from A.
But it is also possible that A actually had the required permissions and B does not do even though one adds a doPrivileged it still fails as B does not have sufficient permissions anyway.
The reason for using the doPrivileged is the assumption module B has the greater (or more appropriate permissions) and just want these compared without those from A.
Privileged blocks implications for third party libraries
It is not a requirement for a developer of a third-party library to add a doProviledge block and configure the corresponding protection domain with the permissions the library requires for doing the work.
A third-party developer of a library could add a doPrivileged block to allow the consumers of the library to have the freedom to not require all the permissions needed available on all the protection domains. However, consumers have to at least give the required permissions to the protection domain where the third-party library is. This is just to add a possibility for the consumers of the library.
Privileged blocks implications for WildFly modules
Without WildFly’s special AllPermission assignment each module should specify the permissions that it needs.
But this is where the permissions get even more complex. Let’s say in that example module B needs to read a file, the developer of module B may not know where that file will exist as it is not until the module is used in another project (like an app server we know file locations).
One way is avoiding doPrivileged:
-
Module B has the permission to access all files.
-
Module A has the permission to access just a specific file.
Combined a permission check would pass.
Second is module B contains a doPrivileged.
If module B still has the permission to access all files but module A has no file permission. If module A can pass the path of the file to module B one now has the problem that modules can potentially use this to bypass permissions and get access to all files.
The next variation is the permissions for module B need adjusting in context, i.e. it now needs to specify which file module B can access so there is no way for A to abuse it.
WildFly developers should not need to specify permissions for all modules in the app server, and if they need context that would be even more difficult.
The first example would be the equivalent of all deployments now needing the permissions granted but deployments are not supposed to need to be aware of the inner workings of the application server to decide what permissions they need.
So it ends up in that middle ground where if WildFly developers are not careful it could be open to abuse from deployments.
20. Migrate Legacy Security to Elytron Security
20.2. Properties Based Authentication / Authorization
20.2.1. PicketBox Based Configuration
This migration example assumes a deployed web application is configured to require authentication using FORM based authentication and is referencing a PicketBox based security domain using the UsersRolesLoginModule to load user information from a pair or properties files.
Original Configuration
A security domain can be defined in the legacy security subsystem using the following management operations: -
./subsystem=security/security-domain=application-security:add
./subsystem=security/security-domain=application-security/authentication=classic:add(login-modules=[{code=UsersRoles, flag=Required, module-options={usersProperties=file://${jboss.server.config.dir}/example-users.properties, rolesProperties=file://${jboss.server.config.dir}/example-roles.properties}}])
This would result in a security domain definition: -
<security-domain name="application-security">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name="usersProperties" value="file://${jboss.server.config.dir}/example-users.properties"/>
<module-option name="rolesProperties" value="file://${jboss.server.config.dir}/example-roles.properties"/>
</login-module>
</authentication>
</security-domain>
Intermediate Configuration
It is possible to take a previously defined PicketBox security domain and expose it as an Elytron security realm so it can be wired into a complete Elytron based configuration, if only properties based authentication was to be migrated it would be recommended to jump to the fully migration configuration and avoid the unnecessary dependency on the legacy security subsystem but for situations where that is not immediately possible these commands illustrate an intermediate solution.
These steps assume the original configuration is already in place.
The first step is to add a mapping to an Elytron security realm within the legacy security subsystem.
./subsystem=security/elytron-realm=application-security:add(legacy-jaas-config=application-security)
This results in the following configuration.
<subsystem xmlns="urn:jboss:domain:security:2.0">
...
<elytron-integration>
<security-realms>
<elytron-realm name="application-security" legacy-jaas-config="application-security"/>
</security-realms>
</elytron-integration>
...
</subsystem>
Within the Elytron subsystem a security domain can be defined which references the exported security realm and also a http authentication factory which supports FORM based authentication.
./subsystem=elytron/security-domain=application-security:add(realms=[{realm=application-security}], default-realm=application-security, permission-mapper=default-permission-mapper)
./subsystem=elytron/http-authentication-factory=application-security-http:add(http-server-mechanism-factory=global, security-domain=application-security, mechanism-configurations=[{mechanism-name=FORM}])
And the resulting configuration: -
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
...
<security-domain name="application-security" default-realm="application-security" permission-mapper="default-permission-mapper">
<realm name="application-security"/>
</security-domain>
</security-domains>
...
<http>
...
<http-authentication-factory name="application-security-http" http-server-mechanism-factory="global" security-domain="application-security">
<mechanism-configuration>
<mechanism mechanism-name="FORM"/>
</mechanism-configuration>
</http-authentication-factory>
...
</http>
...
</subsystem>
Finally configuration needs to be added to the Undertow subsystem to map the security domain referenced by the deployment to the newly defined http authentication factory.
./subsystem=undertow/application-security-domain=application-security:add(http-authentication-factory=application-security-http)
Which results in: -
<subsystem xmlns="urn:jboss:domain:undertow:4.0">
...
<application-security-domains>
<application-security-domain name="application-security" http-authentication-factory="application-security-http"/>
</application-security-domains>
...
</subsystem>
Note: If the deployment was already deployed at this point the application server should be reloaded or the deployment redeployed for the application security domain mapping to take effect.
The following command can then be used to verify the mapping was applied to the deployment.
[standalone@localhost:9990 /] ./subsystem=undertow/application-security-domain=application-security:read-resource(include-runtime=true)
{
"outcome" => "success",
"result" => {
"enable-jacc" => false,
"http-authentication-factory" => "application-security-http",
"override-deployment-config" => false,
"referencing-deployments" => ["HelloWorld.war"],
"setting" => undefined
}
}
The deployment being tested here is 'HelloWorld.war' and the output from the previous command shows this deployment is referencing the mapping.
At this stage the previously defined security domain is used for it’s LoginModule configuration but this is wrapped by Elytron components which take over authentication.
Fully Migrated Configuration
Alternatively the configuration can be completely defined within the Elytron subsystem, in this case it is assumed none of the previous commands have been executed and this is started from a clean configuration - however if the security domain definition does exist in the legacy security subsystem that will remain completely independent.
First a new security realm can be defined within the Elytron subsystem referencing the files referenced previously: -
./subsystem=elytron/properties-realm=application-properties:add(users-properties={path=example-users.properties, relative-to=jboss.server.config.dir, plain-text=true, digest-realm-name="Application Security"}, groups-properties={path=example-roles.properties, relative-to=jboss.server.config.dir}, groups-attribute=Roles)
As before a security domain and http authentication factory can be defined.
./subsystem=elytron/security-domain=application-security:add(realms=[{realm=application-properties}], default-realm=application-properties, permission-mapper=default-permission-mapper)
./subsystem=elytron/http-authentication-factory=application-security-http:add(http-server-mechanism-factory=global, security-domain=application-security, mechanism-configurations=[{mechanism-name=FORM}])
This results in the following overall configuration.
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
...
<security-domain name="application-security" default-realm="application-properties" permission-mapper="default-permission-mapper">
<realm name="application-properties"/>
</security-domain>
</security-domains>
<security-realms>
...
<properties-realm name="application-properties" groups-attribute="Roles">
<users-properties path="example-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="Application Security" plain-text="true"/>
<groups-properties path="example-roles.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
</security-realms>
...
<http>
...
<http-authentication-factory name="application-security-http" http-server-mechanism-factory="global" security-domain="application-security">
<mechanism-configuration>
<mechanism mechanism-name="FORM"/>
</mechanism-configuration>
</http-authentication-factory>
...
</http>
...
</subsystem>
As before the application-security-domain mapping should be added to the Undertow subsystem and the server reloaded or the deployment redeployed as required.
./subsystem=undertow/application-security-domain=application-security:add(http-authentication-factory=application-security-http)
Which results in: -
<subsystem xmlns="urn:jboss:domain:undertow:4.0">
...
<application-security-domains>
<application-security-domain name="application-security" http-authentication-factory="application-security-http"/>
</application-security-domains>
...
</subsystem>
At this stage the authentication is the equivalent of the original configuration however now Elytron components are used exclusively.
20.2.2. Migrating to FileSystemRealm Based Authentication
An alternative to using a legacy properties-realm in Elytron is to use the new filesystem-realm. An Elytron filesystem-realm will use file-backed authentication methods to secure the server. It is now easy to migrate from a legacy properties-realm to an Elytron filesystem-realm by using the Elytron Tool. The new Elytron Tool command, FileSystemRealmCommand, will convert the given properties files and create an Elytron FileSystemRealm, along with a script with the WildFly CLI commands for registering the FileSystemRealm and Security Domain on the WildFly server. After using the tool, it will still be necessary to configure an authentication-factory and an application-security-domain, as in the steps above.
Single User-Roles Conversion
To convert a single user-roles properties files combination, the parameters can be passed directly into the command:
./bin/elytron-tool.sh filesystem-realm --users-file users.properties --roles-file roles.properties --output-location filesystem_realm_dir
This will then configure a filesystem-realm in filesystem_realm_dir and will create a script converted-properties-filesystem-realm.sh in filesystem_realm_dir with the WildFly CLI commands to register the filesystem-realm and the security-domain, with the security-domain named converted-properties-security-domain. To customize the filesystem-realm name and the security-domain name, the --filesystem-realm-name and --security-domain-name parameters can be used.
Use elytron-tool.sh filesystem-realm --help to get description of all parameters.
Notes:
-
The short form options, as shown in the --help option, can be used, such as -u in place of --users-file.
-
When the --summary parameter is specified, an output of operations performed during conversion, including warnings and errors, will be shown once the command finishes conversion.
-
When the --silent parameter is specified, Elytron Tool will not give no information output, as compared to normal operation where warnings are shown. The --silent command will not override --summary, resulting in the ability to hide output until the command finishes conversion.
-
Elytron Tool will not configure the filesystem-realm and security-domain within WildFly itself, it will just provide the necessary commands in the output script file.
Bulk User-Roles Conversion
It is possible to convert multiple users-roles files combinations at once by using --bulk-convert parameter with a descriptor file.
An example descriptor-file from our tests is:
users-file:/home/jucook/Documents/WildFly/Git_Projects/WildFly-Elytron/wildfly-elytron-tool/target/test-classes/filesystem-realm/users/users-5.properties
roles-file:/home/jucook/Documents/WildFly/Git_Projects/WildFly-Elytron/wildfly-elytron-tool/target/test-classes/filesystem-realm/roles/roles-5.properties
output-location:./target/test-classes/filesystem-realm/output-5-bulk
filesystem-realm-name:nameOfFileSystemRealm5
security-domain-name:nameOfSecurityDomain5
users-file:/home/jucook/Documents/WildFly/Git_Projects/WildFly-Elytron/wildfly-elytron-tool/target/test-classes/filesystem-realm/users/users-6.properties
roles-file:/home/jucook/Documents/WildFly/Git_Projects/WildFly-Elytron/wildfly-elytron-tool/target/test-classes/filesystem-realm/roles/roles-6.properties
output-location:/home/jucook/Documents/WildFly/Git_Projects/WildFly-Elytron/wildfly-elytron-tool/target/test-classes/filesystem-realm/output-6-bulk
filesystem-realm-name:nameOfFileSystemRealm6
security-domain-name:nameOfSecurityDomain6
Each blank line starts a new conversion operation. As with a single conversion, the users-file, roles-file, and output-location are required parameters while the filesystem-realm-name and security-domain-name are optional parameters.
Execute the following command to convert with the descriptor file:
./bin/elytron-tool.sh filesystem-realm --bulk-convert descriptor-file
Notes:
-
For bulk conversion, only the long form option can be used, unlike the CLI mode where both long and short form options can be used.
-
The --summary and --silent parameters can be used here too. However, they must be specified while executing the command and apply to all conversions specified in the descriptor file.
-
If the --summary parameter is used, then a summary will be provided after each execution as opposed to after the command finishes all conversions.
-
As with the single conversion, absolute or relative paths can be used for users-file, roles-file, and output-location.
-
Each execution of the command will produce a separate script in the given output-location directory.
-
Repeated output-location paths can result in an error
-
If there is an error in one users-roles files combination then Elytron Tool will report the issue, such as a missing required parameter, and continue with the conversion of all remaining combinations.
20.2.3. Legacy Security Realm
Original Configuration
A legacy security realm can be defined using the following commands to load users passwords and group information from properties files.
./core-service=management/security-realm=ApplicationSecurity:add
./core-service=management/security-realm=ApplicationSecurity/authentication=properties:add(relative-to=jboss.server.config.dir, path=example-users.properties, plain-text=true)
./core-service=management/security-realm=ApplicationSecurity/authorization=properties:add(relative-to=jboss.server.config.dir, path=example-roles.properties)
This results in the following realm definition.
<security-realm name="ApplicationSecurity">
<authentication>
<properties path="example-users.properties" relative-to="jboss.server.config.dir" plain-text="true"/>
</authentication>
<authorization>
<properties path="example-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
A legacy security realm would typically be used to secure either the management interfaces or remoting connectors.
Migrated Configuration
One of the motivations for adding the Elytron based security to the application server is to allow a consistent security solution to be used across the server, to replace the security realm the same steps as described in the previous 'Fully Migrated' section can be followed again up until the http-authentication-factory is defined.
A legacy security realm can also be used for SASL based authentication so a sasl-authentication-factory should also be defined.
./subsystem=elytron/sasl-authentication-factory=application-security-sasl:add(sasl-server-factory=elytron, security-domain=application-security, mechanism-configurations=[{mechanism-name=PLAIN}])
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<sasl>
...
<sasl-authentication-factory name="application-security-sasl" sasl-server-factory="elytron" security-domain="application-security">
<mechanism-configuration>
<mechanism mechanism-name="PLAIN"/>
</mechanism-configuration>
</sasl-authentication-factory>
...
</sasl>
</subsystem>
This can be associated with a Remoting connector to use for authentication and the existing security realm reference cleared.
./subsystem=remoting/http-connector=http-remoting-connector:write-attribute(name=sasl-authentication-factory, value=application-security-sasl)
./subsystem=remoting/http-connector=http-remoting-connector:undefine-attribute(name=security-realm)
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
...
<http-connector name="http-remoting-connector" connector-ref="default" sasl-authentication-factory="application-security-sasl"/>
</subsystem>
If this new configuration was to be used to secure the management interfaces more suitable names should be chosen but the following commands illustrate how to set the two authentication factories and clear the existing security realm reference.
./core-service=management/management-interface=http-interface:write-attribute(name=http-authentication-factory, value=application-security-http)
./core-service=management/management-interface=http-interface:write-attribute(name=http-upgrade.sasl-authentication-factory, value=application-security-sasl)
./core-service=management/management-interface=http-interface:undefine-attribute(name=security-realm)
<management-interfaces>
<http-interface http-authentication-factory="application-security-http">
<http-upgrade enabled="true" sasl-authentication-factory="application-security-sasl"/>
<socket-binding http="management-http"/>
</http-interface>
</management-interfaces>
20.3. LDAP Authentication Migration
The section describing how to migrate from properties based authentication using either PicketBox or legacy security realms to Elytron also contained a lot of additional information regarding defining security domains, authentication factories, and how these are mapped to be used for authentication. This section will illustrate some equivalent LDAP configuration using legacy security realms and PicketBox security domains and show the equivalent configuration using Elytron but will not repeat the steps to wire it all together covered in the previous section.
These configuration examples are developed against a test LDAP sever with user entries like: -
dn: uid=TestUserOne,ou=users,dc=group-to-principal,dc=wildfly,dc=org objectClass: top objectClass: inetOrgPerson objectClass: uidObject objectClass: person objectClass: organizationalPerson cn: Test User One sn: Test User One uid: TestUserOne userPassword: {SSHA}UG8ov2rnrnBKakcARVvraZHqTa7mFWJZlWt2HA==
The group entries then look like: -
dn: uid=GroupOne,ou=groups,dc=group-to-principal,dc=wildfly,dc=org objectClass: top objectClass: groupOfUniqueNames objectClass: uidObject cn: Group One uid: GroupOne uniqueMember: uid=TestUserOne,ou=users,dc=group-to-principal,dc=wildfly,dc=org
For authentication purposes the username will be matched against the 'uid' attribute, also the resulting group name will be taken from the 'uid' attribute of the group entry.
20.3.1. Legacy Security Realm
A connection to the LDAP server and related security realm can be created with the following commands: -
batch
./core-service=management/ldap-connection=MyLdapConnection:add(url="ldap://localhost:10389", search-dn="uid=admin,ou=system", search-credential="secret")
./core-service=management/security-realm=LDAPRealm:add
./core-service=management/security-realm=LDAPRealm/authentication=ldap:add(connection="MyLdapConnection", username-attribute=uid, base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org")
./core-service=management/security-realm=LDAPRealm/authorization=ldap:add(connection=MyLdapConnection)
./core-service=management/security-realm=LDAPRealm/authorization=ldap/username-to-dn=username-filter:add(attribute=uid, base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org")
./core-service=management/security-realm=LDAPRealm/authorization=ldap/group-search=group-to-principal:add(base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org", iterative=true, prefer-original-connection=true, principal-attribute=uniqueMember, search-by=DISTINGUISHED_NAME, group-name=SIMPLE, group-name-attribute=uid)
run-batch
This results in the following configuration.
<management>
<security-realms>
...
<security-realm name="LDAPRealm">
<authentication>
<ldap connection="MyLdapConnection" base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org">
<username-filter attribute="uid"/>
</ldap>
</authentication>
<authorization>
<ldap connection="MyLdapConnection">
<username-to-dn>
<username-filter base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org" attribute="uid"/>
</username-to-dn>
<group-search group-name="SIMPLE" iterative="true" group-name-attribute="uid">
<group-to-principal search-by="DISTINGUISHED_NAME" base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org" prefer-original-connection="true">
<membership-filter principal-attribute="uniqueMember"/>
</group-to-principal>
</group-search>
</ldap>
</authorization>
</security-realm>
</security-realms>
<outbound-connections>
<ldap name="MyLdapConnection" url="ldap://localhost:10389" search-dn="uid=admin,ou=system" search-credential="secret"/>
</outbound-connections>
...
</management>
20.3.2. PicketBox LdapExtLoginModule
The following commands can create a PicketBox security domain configured to use the LdapExtLoginModule to verify a username and password.
./subsystem=security/security-domain=application-security:add
./subsystem=security/security-domain=application-security/authentication=classic:add(login-modules=[{code=LdapExtended, flag=Required, module-options={ \
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, \
java.naming.provider.url=ldap://localhost:10389, \
java.naming.security.authentication=simple, \
bindDN="uid=admin,ou=system", \
bindCredential=secret, \
baseCtxDN="ou=users,dc=group-to-principal,dc=wildfly,dc=org", \
baseFilter="(uid={0})", \
rolesCtxDN="ou=groups,dc=group-to-principal,dc=wildfly,dc=org",\
roleFilter="(uniqueMember={1})", \
roleAttributeID="uid" \
}}])
This results in the following configuration.
<subsystem xmlns="urn:jboss:domain:security:2.0">
...
<security-domains>
...
<security-domain name="application-security">
<authentication>
<login-module code="LdapExtended" flag="required">
<module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
<module-option name="java.naming.provider.url" value="ldap://localhost:10389"/>
<module-option name="java.naming.security.authentication" value="simple"/>
<module-option name="bindDN" value="uid=admin,ou=system"/>
<module-option name="bindCredential" value="secret"/>
<module-option name="baseCtxDN" value="ou=users,dc=group-to-principal,dc=wildfly,dc=org"/>
<module-option name="baseFilter" value="(uid={0})"/>
<module-option name="rolesCtxDN" value="ou=groups,dc=group-to-principal,dc=wildfly,dc=org"/>
<module-option name="roleFilter" value="(uniqueMember={1})"/>
<module-option name="roleAttributeID" value="uid"/>
</login-module>
</authentication>
</security-domain>
</security-domains>
</subsystem>
20.3.3. Migrated
Within the Elytron subsystem a directory context can be defined for the connection to LDAP: -
./subsystem=elytron/dir-context=ldap-connection:add(url=ldap://localhost:10389, principal="uid=admin,ou=system", credential-reference={clear-text=secret})
Then a security realm can be created to search LDAP and verify the supplied password: -
./subsystem=elytron/ldap-realm=ldap-realm:add(dir-context=ldap-connection, \
direct-verification=true, \
identity-mapping={search-base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org", \
rdn-identifier="uid", \
attribute-mapping=[{filter-base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org",filter="(uniqueMember={1})",from="uid",to="Roles"}]})
In the prior two examples information is loaded from LDAP to use directly as groups or roles, in the Elytron case information can be loaded from LDAP to associate with the identity as attributes - these can subsequently be mapped to roles but attributes can be loaded for other purposes as well.
By default, if no role-decoder is defined for given security-domain ,
identity attribute " `Roles`" is mapped to the identity roles.
|
This leads to the following configuration.
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-realms>
...
<ldap-realm name="ldap-realm" dir-context="ldap-connection" direct-verification="true">
<identity-mapping rdn-identifier="uid" search-base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org">
<attribute-mapping>
<attribute from="uid" to="Roles" filter="(uniqueMember={1})" filter-base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org"/>
</attribute-mapping>
</identity-mapping>
</ldap-realm>
</security-realms>
...
<dir-contexts>
<dir-context name="ldap-connection" url="ldap://localhost:10389" principal="uid=admin,ou=system">
<credential-reference clear-text="secret"/>
</dir-context>
</dir-contexts>
</subsystem>
20.4. Composite Stores Migration
When using either PicketBox or the legacy security realms it is possible to define a configuration where authentication is performed against one identity store whilst the information used for authorization is loaded from a different store, when using WildFly Elytron this can be achieved by using an aggregate security realm.
The example here makes use of a properties file for authentication and then searches LDAP to load group / role information. Both of these are based on the previous examples within this document so the environmental information is not repeated here.
20.4.1. PicketBox Based Configuration
./subsystem=security/security-domain=application-security:add
./subsystem=security/security-domain=application-security/authentication=classic:add(login-modules=[ \
{code=UsersRoles, flag=Required, module-options={ \
password-stacking=useFirstPass, \
usersProperties=file://${jboss.server.config.dir}/example-users.properties, \
rolesProperties=file://${jboss.server.config.dir}/example-roles.properties}} \
{code=LdapExtended, flag=Required, module-options={ \
password-stacking=useFirstPass, \
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, \
java.naming.provider.url=ldap://localhost:10389, \
java.naming.security.authentication=simple, \
bindDN="uid=admin,ou=system", \
bindCredential=secret, \
baseCtxDN="ou=users,dc=group-to-principal,dc=wildfly,dc=org", \
baseFilter="(uid={0})", \
rolesCtxDN="ou=groups,dc=group-to-principal,dc=wildfly,dc=org",\
roleFilter="(uniqueMember={1})", \
roleAttributeID="uid" \
}}])
This results in the following domain definition
<security-domain name="application-security">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
<module-option name="usersProperties" value="file://${jboss.server.config.dir}/example-users.properties"/>
<module-option name="rolesProperties" value="file://${jboss.server.config.dir}/example-roles.properties"/>
</login-module>
<login-module code="LdapExtended" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
<module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
<module-option name="java.naming.provider.url" value="ldap://localhost:10389"/>
<module-option name="java.naming.security.authentication" value="simple"/>
<module-option name="bindDN" value="uid=admin,ou=system"/>
<module-option name="bindCredential" value="secret"/>
<module-option name="baseCtxDN" value="ou=users,dc=group-to-principal,dc=wildfly,dc=org"/>
<module-option name="baseFilter" value="(uid={0})"/>
<module-option name="rolesCtxDN" value="ou=groups,dc=group-to-principal,dc=wildfly,dc=org"/>
<module-option name="roleFilter" value="(uniqueMember={1})"/>
<module-option name="roleAttributeID" value="uid"/>
</login-module>
</authentication>
</security-domain>
During an authentication attempt the 'UsersRoles' login module will first be called to perform authentication based on the supplied credential, then the 'LdapExtLoginModule' will be called which will proceed to query LDAP to load the roles for the identity.
./core-service=management/ldap-connection=MyLdapConnection:add(url="ldap://localhost:10389", search-dn="uid=admin,ou=system", search-credential="secret")
./core-service=management/security-realm=ApplicationSecurity:add
./core-service=management/security-realm=ApplicationSecurity/authentication=properties:add(path=example-users.properties, relative-to=jboss.server.config.dir, plain-text=true)
batch
./core-service=management/security-realm=ApplicationSecurity/authorization=ldap:add(connection=MyLdapConnection)
./core-service=management/security-realm=ApplicationSecurity/authorization=ldap/username-to-dn=username-filter:add(attribute=uid, base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org")
./core-service=management/security-realm=ApplicationSecurity/authorization=ldap/group-search=group-to-principal:add(base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org", iterative=true, prefer-original-connection=true, principal-attribute=uniqueMember, search-by=DISTINGUISHED_NAME, group-name=SIMPLE, group-name-attribute=uid)
run-batch
This results in the following realm definition:
<security-realm name="ApplicationSecurity">
<authentication>
<properties path="example-users.properties" relative-to="jboss.server.config.dir" plain-text="true"/>
</authentication>
<authorization>
<ldap connection="MyLdapConnection">
<username-to-dn>
<username-filter base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org" attribute="uid"/>
</username-to-dn>
<group-search group-name="SIMPLE" iterative="true" group-name-attribute="uid">
<group-to-principal search-by="DISTINGUISHED_NAME" base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org" prefer-original-connection="true">
<membership-filter principal-attribute="uniqueMember"/>
</group-to-principal>
</group-search>
</ldap>
</authorization>
</security-realm>
<outbound-connections>
<ldap name="MyLdapConnection" url="ldap://localhost:10389" search-dn="uid=admin,ou=system" search-credential="secret"/>
</outbound-connections>
As with the PicketBox example, authentication is first performed using the properties file - then group searching is performed against LDAP.
20.4.2. Migrated WildFly Elytron Configuration
The equivalent WildFly Elytron configuration can be defined with the following commands:
./subsystem=elytron/dir-context=ldap-connection:add(url=ldap://localhost:10389, principal="uid=admin,ou=system", credential-reference={clear-text=secret})
./subsystem=elytron/ldap-realm=ldap-realm:add(dir-context=ldap-connection, \
direct-verification=true, \
identity-mapping={search-base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org", \
rdn-identifier="uid", \
attribute-mapping=[{filter-base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org",filter="(uniqueMember={1})",from="uid",to="Roles"}]})
./subsystem=elytron/properties-realm=application-properties:add(users-properties={path=example-users.properties, relative-to=jboss.server.config.dir, plain-text=true, digest-realm-name="Application Security"}, groups-properties={path=example-roles.properties, relative-to=jboss.server.config.dir}, groups-attribute=Roles)
./subsystem=elytron/aggregate-realm=combined-realm:add(authentication-realm=application-properties, authorization-realm=ldap-realm)
./subsystem=elytron/security-domain=application-security:add(realms=[{realm=combined-realm}], default-realm=combined-realm, permission-mapper=default-permission-mapper)
./subsystem=elytron/http-authentication-factory=application-security-http:add(http-server-mechanism-factory=global, security-domain=application-security, mechanism-configurations=[{mechanism-name=BASIC}])
This results in the following realm definition:
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
...
<security-domain name="application-security" default-realm="combined-realm" permission-mapper="default-permission-mapper">
<realm name="combined-realm"/>
</security-domain>
</security-domains>
<security-realms>
<aggregate-realm name="combined-realm" authentication-realm="application-properties" authorization-realm="ldap-realm"/>
...
<properties-realm name="application-properties" groups-attribute="Roles">
<users-properties path="example-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="Application Security" plain-text="true"/>
<groups-properties path="example-roles.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
<ldap-realm name="ldap-realm" dir-context="ldap-connection" direct-verification="true">
<identity-mapping rdn-identifier="uid" search-base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org">
<attribute-mapping>
<attribute from="uid" to="Roles" filter="(uniqueMember={1})" filter-base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org"/>
</attribute-mapping>
</identity-mapping>
</ldap-realm>
</security-realms>
...
<http>
...
<http-authentication-factory name="application-security-http" http-server-mechanism-factory="global" security-domain="application-security">
<mechanism-configuration>
<mechanism mechanism-name="BASIC"/>
</mechanism-configuration>
</http-authentication-factory>
...
</http>
...
<dir-contexts>
<dir-context name="ldap-connection" url="ldap://localhost:10389" principal="uid=admin,ou=system">
<credential-reference clear-text="secret"/>
</dir-context>
</dir-contexts>
</subsystem>
Within the WildFly Elytron example a new security realm 'aggregate-realm' has been defined, this definition specifies which of the defined security realms should be used for the authentication step and which of the security realms should be used for the loading of the identity used for subsequent authorization decisions.
20.5. Database Authentication
The section describing how to migrate from database accessible via JDBC datasource based authentication using PicketBox to Elytron. This section will illustrate some equivalent configuration using PicketBox security domains and show the equivalent configuration using Elytron but will not repeat the steps to wire it all together covered in the previous sections.
These configuration examples are developed against a test database with users table like:
CREATE TABLE User (
id BIGINT NOT NULL,
username VARCHAR(255),
password VARCHAR(255),
role ENUM('admin', 'manager', 'user'),
PRIMARY KEY (id),
UNIQUE (username)
)
For authentication purposes the username will be matched against the ' `username’ column, password will be expected in hex-encoded MD5 hash in ' `password’ column. User role for authorization purposes will be taken from ' `role’ column.
20.5.1. PicketBox Database LoginModule
The following commands can create a PicketBox security domain configured to use database accessible via JDBC datasource to verify a username and password and to assign roles.
./subsystem=security/security-domain=application-security/:add
./subsystem=security/security-domain=application-security/authentication=classic:add(login-modules=[{code=Database, flag=Required, module-options={ \
dsJndiName="java:jboss/datasources/ExampleDS", \
principalsQuery="SELECT password FROM User WHERE username = ?", \
rolesQuery="SELECT role, 'Roles' FROM User WHERE username = ?", \
hashAlgorithm=MD5, \
hashEncoding=base64 \
}}])
This results in the following configuration.
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
...
<security-domain name="application-security">
<authentication>
<login-module code="Database" flag="required">
<module-option name="dsJndiName" value="java:jboss/datasources/ExampleDS"/>
<module-option name="principalsQuery" value="SELECT password FROM User WHERE username = ?"/>
<module-option name="rolesQuery" value="SELECT role, 'Roles' FROM User WHERE username = ?"/>
<module-option name="hashAlgorithm" value="MD5"/>
<module-option name="hashEncoding" value="base64"/>
</login-module>
</authentication>
</security-domain>
</security-domains>
</subsystem>
20.5.2. Migrated
Within the Elytron subsystem to use database accesible via JDBC you need
to define jdbc-realm
:
./subsystem=elytron/jdbc-realm=jdbc-realm:add(principal-query=[{ \
data-source=ExampleDS, \
sql="SELECT role, password FROM User WHERE username = ?", \
attribute-mapping=[{index=1, to=Roles}] \
simple-digest-mapper={algorithm=simple-digest-md5, password-index=2}, \
}])
This results in the following overall configuration:
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-realms>
...
<jdbc-realm name="jdbc-realm">
<principal-query sql="SELECT role, password FROM User WHERE username = ?" data-source="ExampleDS">
<attribute-mapping>
<attribute to="Roles" index="1"/>
</attribute-mapping>
<simple-digest-mapper password-index="2"/>
</principal-query>
</jdbc-realm>
...
</security-realms>
...
</subsystem>
In comparison with PicketBox solution, Elytron jdbc-realm
use one SQL
query to obtain all user attributes and credentials. Their extraction
from SQL result specifies mappers.
20.5.3. N-M relation beetween user and roles
When using a n:m-relation beetween user and roles (which means: the user has multiple roles), the previous configuration does not work.
The database:
CREATE TABLE User (
id BIGINT NOT NULL,
username VARCHAR(255),
password VARCHAR(255),
PRIMARY KEY (id),
UNIQUE (username)
)
CREATE TABLE Role(
id BIGINT NOT NULL,
rolename VARCHAR(255),
PRIMARY KEY (id),
UNIQUE (rolename)
)
CREATE TABLE Userrole(
userid BIGINT not null,
roleid BIGINT not null,
PRIMARY KEY (userid, roleid),
FOREIGN KEY (userid) references User(id,
FOREIGN KEY (roleid) references Role(id)
)
Here you need two configure two principal queries:
<jdbc-realm name="jdbc-realm">
<principal-query sql="SELECT PASSWORD FROM USER WHERE USERNAME = ?" data-source="ExampleDS">
<clear-password-mapper password-index="1"/>
</principal-query>
<principal-query sql="SELECT R.ROLENAME from ROLE AS R, USERROLE AS UR, USER AS U WHERE U.USERNAME=? AND UR.ROLEID = R.ID AND UR.USERID = U.ID" data-source="ExampleDS">
<attribute-mapping>
<attribute to="roles" index="1"/>
</attribute-mapping>
</principal-query>
</jdbc-realm>
The second query needs an attribute mapping to decode the selected rolename column (index 1):
<mappers>
...
<simple-role-decoder name="from-roles-attribute" attribute="roles"/>
...
</mappers>
The role decoder is referenced by the security domain:
<security-domain name="MyDomain" default-realm="jdbc-realm" permission-mapper="default-permission-mapper">
<realm name="MyDbRealm" role-decoder="from-roles-attribute"/>
</security-domain>
20.6. Kerberos Authentication Migration
When working with Kerberos configuration it is possible for the application server to rely on configuration from the environment or the key configuration can be specified using system properties, for the purpose of these examples I define system properties - these properties are applicable to both the legacy configuration and the migrated Elytron configuration.
./system-property=sun.security.krb5.debug:add(value=true)
./system-property=java.security.krb5.realm:add(value=ELYTRON.ORG)
./system-property=java.security.krb5.kdc:add(value=kdc.elytron.org)
The first line makes debugging easier but the last two lines specify the Kerberos realm in use and the address of the KDC.
<system-properties>
<property name="sun.security.krb5.debug" value="true"/>
<property name="java.security.krb5.realm" value="ELYTRON.ORG"/>
<property name="java.security.krb5.kdc" value="kdc.elytron.org"/>
</system-properties>
20.6.1. HTTP Authentication
Legacy Security Realm
A legacy security realm can be define so that SPNEGO authentication can be enabled for the HTTP management interface.
./core-service=management/security-realm=Kerberos:add
./core-service=management/security-realm=Kerberos/server-identity=kerberos:add
./core-service=management/security-realm=Kerberos/server-identity=kerberos/keytab=HTTP\/test-server.elytron.org@ELYTRON.ORG:add(path=/home/darranl/src/kerberos/test-server.keytab, debug=true)
./core-service=management/security-realm=Kerberos/authentication=kerberos:add(remove-realm=true)
This results in the following configuration: -
<security-realms>
...
<security-realm name="Kerberos">
<server-identities>
<kerberos>
<keytab principal="HTTP/test-server.elytron.org@ELYTRON.ORG" path="/home/darranl/src/kerberos/test-server.keytab" debug="true"/>
</kerberos>
</server-identities>
<authentication>
<kerberos remove-realm="true"/>
</authentication>
</security-realm>
</security-realms>
Application SPNEGO
Alternatively deployed applications would make use of a pair of security domains.
./subsystem=security/security-domain=host:add
./subsystem=security/security-domain=host/authentication=classic:add
./subsystem=security/security-domain=host/authentication=classic/login-module=1:add(code=Kerberos, flag=Required, module-options={storeKey=true, useKeyTab=true, principal=HTTP/test-server.elytron.org@ELYTRON.ORG, keyTab=/home/darranl/src/kerberos/test-server.keytab, debug=true}
./subsystem=security/security-domain=SPNEGO:add
./subsystem=security/security-domain=SPNEGO/authentication=classic:add
./subsystem=security/security-domain=SPNEGO/authentication=classic/login-module=1:add(code=SPNEGO, flag=requisite, module-options={password-stacking=useFirstPass, serverSecurityDomain=host})
./subsystem=security/security-domain=SPNEGO/authentication=classic/login-module=1:write-attribute(name=module, value=org.jboss.security.negotiation)
./subsystem=security/security-domain=SPNEGO/authentication=classic/login-module=2:add(code=UsersRoles, flag=required, module-options={password-stacking=useFirstPass, usersProperties=file:///home/darranl/src/kerberos/spnego-users.properties, rolesProperties=file:///home/darranl/src/kerberos/spnego-roles.properties, defaultUsersProperties=file:///home/darranl/src/kerberos/spnego-users.properties, defaultRolesProperties=file:///home/darranl/src/kerberos/spnego-roles.properties})
This results in: -
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
...
<security-domain name="host">
<authentication>
<login-module name="1" code="Kerberos" flag="required">
<module-option name="storeKey" value="true"/>
<module-option name="useKeyTab" value="true"/>
<module-option name="principal" value="HTTP/test-server.elytron.org@ELYTRON.ORG"/>
<module-option name="keyTab" value="/home/darranl/src/kerberos/test-server.keytab"/>
<module-option name="debug" value="true"/>
</login-module>
</authentication>
</security-domain>
<security-domain name="SPNEGO">
<authentication>
<login-module name="1" code="SPNEGO" flag="requisite" module="org.jboss.security.negotiation">
<module-option name="password-stacking" value="useFirstPass"/>
<module-option name="serverSecurityDomain" value="host"/>
</login-module>
<login-module name="2" code="UsersRoles" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
<module-option name="usersProperties" value="file:///home/darranl/src/kerberos/spnego-users.properties"/>
<module-option name="rolesProperties" value="file:///home/darranl/src/kerberos/spnego-roles.properties"/>
<module-option name="defaultUsersProperties" value="file:///home/darranl/src/kerberos/spnego-users.properties"/>
<module-option name="defaultRolesProperties" value="file:///home/darranl/src/kerberos/spnego-roles.properties"/>
</login-module>
</authentication>
</security-domain>
</security-domains>
</subsystem>
An application can now be deployed referencing the SPNEGO security domain and secured with SPNEGO mechanism.
Migrated SPNEGO
The equivalent configuration can be achieved with WildFly Elytron by first defining a security realm which will be used to load identity information.
./subsystem=elytron/properties-realm=spnego-properties:add(users-properties={path=/home/darranl/src/kerberos/spnego-users.properties, plain-text=true, digest-realm-name=ELYTRON.ORG}, groups-properties={path=/home/darranl/src/kerberos/spnego-roles.properties})
Next a Kerberos security factory is defined which allows the server to load it’s own Kerberos identity.
./subsystem=elytron/kerberos-security-factory=test-server:add(path=/home/darranl/src/kerberos/test-server.keytab, principal=HTTP/test-server.elytron.org@ELYTRON.ORG, debug=true)
As with the previous examples we define a security realm to pull together the policy as well as a HTTP authentication factory for the authentication policy.
./subsystem=elytron/security-domain=SPNEGODomain:add(default-realm=spnego-properties, realms=[{realm=spnego-properties, role-decoder=groups-to-roles}], permission-mapper=default-permission-mapper)
./subsystem=elytron/http-authentication-factory=spnego-http-authentication:add(security-domain=SPNEGODomain, http-server-mechanism-factory=global,mechanism-configurations=[{mechanism-name=SPNEGO, credential-security-factory=test-server}])
Overall this results in the following configuration: -
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
...
<security-domain name="SPNEGODomain" default-realm="spnego-properties" permission-mapper="default-permission-mapper">
<realm name="spnego-properties" role-decoder="groups-to-roles"/>
</security-domain>
</security-domains>
<security-realms>
...
<properties-realm name="spnego-properties">
<users-properties path="/home/darranl/src/kerberos/spnego-users.properties" digest-realm-name="ELYTRON.ORG" plain-text="true"/>
<groups-properties path="/home/darranl/src/kerberos/spnego-roles.properties"/>
</properties-realm>
</security-realms>
<credential-security-factories>
<kerberos-security-factory name="test-server" principal="HTTP/test-server.elytron.org@ELYTRON.ORG" path="/home/darranl/src/kerberos/test-server.keytab" debug="true"/>
</credential-security-factories>
...
<http>
...
<http-authentication-factory name="spnego-http-authentication" http-server-mechanism-factory="global" security-domain="SPNEGODomain">
<mechanism-configuration>
<mechanism mechanism-name="SPNEGO" credential-security-factory="test-server"/>
</mechanism-configuration>
</http-authentication-factory>
...
</http>
...
</subsystem>
Now, to enable SPNEGO authentication for the HTTP management interface,
update this interface to reference the http-authentication-factory
defined above, as described in the
properties
authentication section.
Alternatively, to secure an application using SPNEGO authentication, an
application security domain can be defined in the Undertow subsystem to
map security domains to the http-authentication-factory
defined above,
as described in the
properties
authentication section.
20.6.2. Remoting / SASL Authentication
Legacy Security Realm
It is also possible to define a legacy security realm for Kerberos / GSSAPI SASL authenticatio for Remoting authentication such as the native management interface.
./core-service=management/security-realm=Kerberos:add
./core-service=management/security-realm=Kerberos/server-identity=kerberos:add
./core-service=management/security-realm=Kerberos/server-identity=kerberos/keytab=remote\/test-server.elytron.org@ELYTRON.ORG:add(path=/home/darranl/src/kerberos/remote-test-server.keytab, debug=true)
./core-service=management/security-realm=Kerberos/authentication=kerberos:add(remove-realm=true)
<management>
<security-realms>
...
<security-realm name="Kerberos">
<server-identities>
<kerberos>
<keytab principal="remote/test-server.elytron.org@ELYTRON.ORG" path="/home/darranl/src/kerberos/remote-test-server.keytab" debug="true"/>
</kerberos>
</server-identities>
<authentication>
<kerberos remove-realm="true"/>
</authentication>
</security-realm>
</security-realms>
...
</management>
Migrated GSSAPI
The steps to define the equivalent Elytron configuration are very similar to the HTTP example.
First define the security realm to load the identity from: -
./path=kerberos:add(relative-to=user.home, path=src/kerberos)
./subsystem=elytron/properties-realm=kerberos-properties:add(users-properties={path=kerberos-users.properties, relative-to=kerberos, digest-realm-name=ELYTRON.ORG}, groups-properties={path=kerberos-groups.properties, relative-to=kerberos})
Then define the Kerberos security factory for the server’s identity.
./subsystem=elytron/kerberos-security-factory=test-server:add(relative-to=kerberos, path=remote-test-server.keytab, principal=remote/test-server.elytron.org@ELYTRON.ORG)
Finally define the security domain and this time a SASL authentication factory.
./subsystem=elytron/security-domain=KerberosDomain:add(default-realm=kerberos-properties, realms=[{realm=kerberos-properties, role-decoder=groups-to-roles}], permission-mapper=default-permission-mapper)
./subsystem=elytron/sasl-authentication-factory=gssapi-authentication-factory:add(security-domain=KerberosDomain, sasl-server-factory=elytron, mechanism-configurations=[{mechanism-name=GSSAPI, credential-security-factory=test-server}])
This results in the following subsystem configuration: -
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
...
<security-domain name="KerberosDomain" default-realm="kerberos-properties" permission-mapper="default-permission-mapper">
<realm name="kerberos-properties" role-decoder="groups-to-roles"/>
</security-domain>
</security-domains>
<security-realms>
...
<properties-realm name="kerberos-properties">
<users-properties path="kerberos-users.properties" relative-to="kerberos" digest-realm-name="ELYTRON.ORG"/>
<groups-properties path="kerberos-groups.properties" relative-to="kerberos"/>
</properties-realm>
</security-realms>
<credential-security-factories>
<kerberos-security-factory name="test-server" principal="remote/test-server.elytron.org@ELYTRON.ORG" path="remote-test-server.keytab" relative-to="kerberos"/>
</credential-security-factories>
...
<sasl>
...
<sasl-authentication-factory name="gssapi-authentication-factory" sasl-server-factory="elytron" security-domain="KerberosDomain">
<mechanism-configuration>
<mechanism mechanism-name="GSSAPI" credential-security-factory="test-server"/>
</mechanism-configuration>
</sasl-authentication-factory>
...
</sasl>
</subsystem>
The management interface or Remoting connectors can now be updated to reference the SASL authentication factory.
The two Elytron examples defined here could also be combined into one to use a shared security domain and security realm and just use protocol specific authentication factories each referencing their own Kerberos security factory.
20.7. Caching Migration
Where a PicketBox based security domain is defined it is possible to enable caching for that security domain, this enables subsequent hits to the identity store to be avoided as an in memory cache can be used instead, this example demonstrates how caching can be used with a WildFly Elytron based configuration.
The purpose of this chapter is to highlight the migration of a configuration with caching enabled, this example is based in the previous LDAP example but with caching enabled.
20.7.1. PicketBox Example
A PicketBox based security domain can be defined with the following commands.
./subsystem=security/security-domain=application-security:add(cache-type=default)
./subsystem=security/security-domain=application-security/authentication=classic:add(login-modules=[{code=LdapExtended, flag=Required, module-options={ \
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, \
java.naming.provider.url=ldap://localhost:10389, \
java.naming.security.authentication=simple, \
bindDN="uid=admin,ou=system", \
bindCredential=secret, \
baseCtxDN="ou=users,dc=group-to-principal,dc=wildfly,dc=org", \
baseFilter="(uid={0})", \
rolesCtxDN="ou=groups,dc=group-to-principal,dc=wildfly,dc=org",\
roleFilter="(uniqueMember={1})", \
roleAttributeID="uid" \
}}])
Resulting in the following security domain definition:
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
...
<security-domain name="application-security" cache-type="default">
<authentication>
<login-module code="LdapExtended" flag="required">
<module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
<module-option name="java.naming.provider.url" value="ldap://localhost:10389"/>
<module-option name="java.naming.security.authentication" value="simple"/>
<module-option name="bindDN" value="uid=admin,ou=system"/>
<module-option name="bindCredential" value="secret"/>
<module-option name="baseCtxDN" value="ou=users,dc=group-to-principal,dc=wildfly,dc=org"/>
<module-option name="baseFilter" value="(uid={0})"/>
<module-option name="rolesCtxDN" value="ou=groups,dc=group-to-principal,dc=wildfly,dc=org"/>
<module-option name="roleFilter" value="(uniqueMember={1})"/>
<module-option name="roleAttributeID" value="uid"/>
</login-module>
</authentication>
</security-domain>
</security-domains>
</subsystem>
20.7.2. Migrated Example
When using WildFly Elytron where caching is required the individual security realm is wrapped using a cache, a migrated configuration can be defined with the following commands:
./subsystem=elytron/dir-context=ldap-connection:add(url=ldap://localhost:10389, principal="uid=admin,ou=system", credential-reference={clear-text=secret})
./subsystem=elytron/ldap-realm=ldap-realm:add(dir-context=ldap-connection, \
direct-verification=true, \
identity-mapping={search-base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org", \
rdn-identifier="uid", \
attribute-mapping=[{filter-base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org",filter="(uniqueMember={1})",from="uid",to="Roles"}]})
./subsystem=elytron/caching-realm=cached-ldap:add(realm=ldap-realm)
These can then be used in a security domain and subsequently an authentication factory.
./subsystem=elytron/security-domain=application-security:add(realms=[{realm=cached-ldap}], default-realm=cached-ldap, permission-mapper=default-permission-mapper)
./subsystem=elytron/http-authentication-factory=application-security-http:add(http-server-mechanism-factory=global, security-domain=application-security, mechanism-configurations=[{mechanism-name=BASIC}])
In this final step it is very important that the caching-realm is referenced rather than the original realm otherwise caching will be bypassed.
This results in the following definitions:
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
...
<security-domain name="application-security" default-realm="cached-ldap" permission-mapper="default-permission-mapper">
<realm name="cached-ldap"/>
</security-domain>
</security-domains>
<security-realms>
...
<ldap-realm name="ldap-realm" dir-context="ldap-connection" direct-verification="true">
<identity-mapping rdn-identifier="uid" search-base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org">
<attribute-mapping>
<attribute from="uid" to="Roles" filter="(uniqueMember={1})" filter-base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org"/>
</attribute-mapping>
</identity-mapping>
</ldap-realm>
<caching-realm name="cached-ldap" realm="ldap-realm"/>
</security-realms>
...
<http>
...
<http-authentication-factory name="application-security-http" http-server-mechanism-factory="global" security-domain="application-security">
<mechanism-configuration>
<mechanism mechanism-name="BASIC"/>
</mechanism-configuration>
</http-authentication-factory>
...
</http>
...
<dir-contexts>
<dir-context name="ldap-connection" url="ldap://localhost:10389" principal="uid=admin,ou=system">
<credential-reference clear-text="secret"/>
</dir-context>
</dir-contexts>
</subsystem>
20.9. Application Client Migration
20.9.1. Naming Client
This migration example assumes a client application performs a remote
JNDI lookup using an InitialContext
backed by the
org.wildfly.naming.client.WildFlyInitialContextFactory
class.
Original Configuration
An InitialContext
backed by the
org.wildfly.naming.client.WildFlyInitialContextFactory
class can be
created by specifying properties that contain the URL of the naming
provider to connect to along with appropriate user credentials:
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
properties.put(Context.PROVIDER_URL, "http-remoting://127.0.0.1:8080");
properties.put(Context.SECURITY_PRINCIPAL, "bob");
properties.put(Context.SECURITY_CREDENTIALS, "secret");
InitialContext context = new InitialContext(properties);
Bar bar = (Bar) context.lookup("foo/bar");
...
Migrated Configuration
An InitialContext
backed by the
org.wildfly.naming.client.WildFlyInitialContextFactory
class can be
created by specifying a property that contains the URL of the naming
provider to connect to. The user credentials can be specified using a
WildFly client configuration file or programmatically.
Configuration File Approach
A wildfly-config.xml
file that contains the user credentials to use
when establishing a connection to the naming provider can be added to
the client application’s META-INF
directory:
wildfly-config.xml
<configuration>
<authentication-client xmlns="urn:elytron:1.0">
<authentication-rules>
<rule use-configuration="namingConfig">
<match-host name="127.0.0.1"/>
</rule>
</authentication-rules>
<authentication-configurations>
<configuration name="namingConfig">
<set-user-name name="bob"/>
<credentials>
<clear-password password="secret"/>
</credentials>
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
An InitialContext
can then be created as follows:
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
properties.put(Context.PROVIDER_URL, "remote+http://127.0.0.1:8080");
InitialContext context = new InitialContext(properties);
Bar bar = (Bar) context.lookup("foo/bar");
...
Programmatic Approach
The user credentials to use when establishing a connection to the naming provider can be specified directly in the client application’s code:
// create your authentication configuration
AuthenticationConfiguration namingConfig = AuthenticationConfiguration.empty().useName("bob").usePassword("secret");
// create your authentication context
AuthenticationContext context = AuthenticationContext.empty().with(MatchRule.ALL.matchHost("127.0.0.1"), namingConfig);
// create a callable that creates and uses an InitialContext
Callable<Void> callable = () -> {
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
properties.put(Context.PROVIDER_URL, "remote+http://127.0.0.1:8080");
InitialContext context = new InitialContext(properties);
Bar bar = (Bar) context.lookup("foo/bar");
...
return null;
};
// use your authentication context to run your callable
context.runCallable(callable);
20.9.2. EJB Client
This migration example assumes a client application is configured to
invoke an EJB deployed on a remote server using a
jboss-ejb-client.properties
file.
Original Configuration
A jboss-ejb-client.properties
file that contains the information
needed to connect to the remote server can be specified in a client
application’s META-INF
directory:
jboss-ejb-client.properties
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=default remote.connection.default.host=127.0.0.1 remote.connection.default.port = 8080 remote.connection.default.username=bob remote.connection.default.password=secret
An EJB can then be looked up and a method can be invoked on it as follows:
// create an InitialContext
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext context = new InitialContext(properties);
// look up an EJB and invoke one of its methods
RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
"ejb:/ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
int sum = statelessRemoteCalculator.add(101, 202);
Migrated Configuration
The information needed to connect to the remote server can be specified using a WildFly client configuration file or programmatically.
Configuration File Approach
A wildfly-config.xml
file that contains the information needed to
connect to the remote server can be added to the client application’s
META-INF
directory:
wildfly-config.xml
<configuration>
<authentication-client xmlns="urn:elytron:1.0">
<authentication-rules>
<rule use-configuration="ejbConfig">
<match-host name="127.0.0.1"/>
</rule>
</authentication-rules>
<authentication-configurations>
<configuration name="ejbConfig">
<set-user-name name="bob"/>
<credentials>
<clear-password password="secret"/>
</credentials>
</configuration>
</authentication-configurations>
</authentication-client>
<jboss-ejb-client xmlns="urn:jboss:wildfly-client-ejb:3.0">
<connections>
<connection uri="remote+http://127.0.0.1:8080" />
</connections>
</jboss-ejb-client>
</configuration>
An EJB can then be looked up and a method can be invoked on it as follows:
// create an InitialContext
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
InitialContext context = new InitialContext(properties);
// look up an EJB and invoke one of its methods (same as before)
RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
"ejb:/ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
int sum = statelessRemoteCalculator.add(101, 202);
Programmatic Approach
The information needed to connect to the remote server can be specified directly in the client application’s code:
// create your authentication configuration
AuthenticationConfiguration ejbConfig = AuthenticationConfiguration.empty().useName("bob").usePassword("secret");
// create your authentication context
AuthenticationContext context = AuthenticationContext.empty().with(MatchRule.ALL.matchHost("127.0.0.1"), ejbConfig);
// create a callable that invokes an EJB
Callable<Void> callable = () -> {
// create an InitialContext
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
properties.put(Context.PROVIDER_URL, "remote+http://127.0.0.1:8080");
InitialContext context = new InitialContext(properties);
// look up an EJB and invoke one of its methods (same as before)
RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
"ejb:/ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
int sum = statelessRemoteCalculator.add(101, 202);
...
return null;
};
// use your authentication context to run your callable
context.runCallable(callable);
References in this document to Enterprise JavaBeans (EJB) refer to the Jakarta Enterprise Beans unless otherwise noted. :leveloffset: -1 |
20.9.4. Security Vault Migration
Security Vault is primarily used in legacy configurations, a vault is used to store sensitive strings outside of the configuration files. WildFly server may only contain a single security vault.
Credential Store introduced in WildFly 11 is meant to expand Security Vault in terms of storing different credential types and introduce easy to implemnent SPI which allows to deploy custom implemenations of CredentialStore SPI. Credentials are stored safely encrypted in storage file outside WildFly configuration files. Each WildFly server may contain multiple credential stores.
To easily migrate vault content into credential store we have added "vault" command into WildFly Elytron Tool. The tool could be found at $JBOSS_HOME/bin directory. It has several scripts named "elytron-tool.*" dependent on your platform of choice.
Single Security Vault Conversion
To convert single security vault credential store use following example:
-
to get sample vault use testing resources of Elytron Tool project from GitHub
Command to run actual conversion:
Output:
Use elytron-tool.sh vault --help to get description of all parameters.
Notes:
-
Elytron Tool cannot handle very first version of Security Vault data file.
-
--keystore-password can come in two forms (1) masked as shown in the example or (2) clear text. Parameter --salt and --iteration are there to supply information to decrypt the masked password or to generate masked password in output. In case --salt and --iteration are omitted default values are used.
-
When --summary parameter is specified, one can see nice output with CLI command to be used in WildFly console to add converted credential store to the configuration.
Bulk Security Vault Conversion
There is possibility to convert multiple vaults to credential store
using --bulk-convert parameter with description file.
Example of description file from our tests:
After each "keystore:" option new conversion starts. All options are mandatory except "salt:", "iteration:" and "properties:"
Execute following command:
Output:
The result is conversion of all vaults with proper CLI commands.
20.9.5. Security Properties
Lets suppose security properties "a" and "c" defined in legacy security:
<subsystem xmlns="urn:jboss:domain:security:2.0">
...
<security-properties>
<property name="a" value="b" />
<property name="c" value="d" />
</security-properties>
</subsystem>
To define security properties in Elytron subsystem you need to set
attribute security-properties
of the subsystem:
./subsystem=elytron:write-attribute(name=security-properties, value={ \
a = "b", \
c = "d" \
})
You can also add or change one another property without modification of others using map operations. Following command will set property "e":
./subsystem=elytron:map-put(name=security-properties, key=e, value=f)
By the same way you can also remove one of properties - in example newly created property "e":
./subsystem=elytron:map-remove(name=security-properties, key=e)
Output XML configuration will be:
<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
<security-properties>
<security-property name="a" value="b"/>
<security-property name="c" value="d"/>
</security-properties>
...
</subsystem>
20.9.7. Simple SSL Migration
Simple SSL Migration
This section describe securing HTTP connections to the server using SSL
using Elytron.
It suppose you have already configured SSL using legacy
security-realm
, for example by
Admin Guide#Enable
SSL, and your configuration looks like:
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<keystore path="server.keystore" relative-to="jboss.server.config.dir" keystore-password="keystore_password" alias="server" key-password="key_password" />
</ssl>
</server-identities>
</security-realm>
To switch to Elytron you need to:
-
Create Elytron
key-store
- specifying where is the keystore file stored and password by which it is encrypted. Default type of keystore generated using keytool is PKCS12:/subsystem=elytron/key-store=LocalhostKeyStore:add(path=server.keystore,relative-to=jboss.server.config.dir,credential-reference={clear-text="keystore_password"},type=PKCS12)
-
Create Elytron
key-manager
- specifying keystore, alias (usingalias-filter
) and password of key:/subsystem=elytron/key-manager=LocalhostKeyManager:add(key-store=LocalhostKeyStore,alias-filter=server,credential-reference={clear-text="key_password"})
-
Create Elytron
server-ssl-context
- specifying only reference tokey-manager
defined above:/subsystem=elytron/server-ssl-context=LocalhostSslContext:add(key-manager=LocalhostKeyManager)
-
Switch
https-listener
from legacysecurity-realm
to newly created Elytronssl-context
:/subsystem=undertow/server=default-server/https-listener=https:undefine-attribute(name=security-realm) /subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=LocalhostSslContext)
-
And reload the server:
reload
Output XML configuration of Elytron subsystem should look like:
<subsystem xmlns="urn:wildfly:elytron:1.0" ...>
...
<tls>
<key-stores>
<key-store name="LocalhostKeyStore">
<credential-reference clear-text="keystore_password"/>
<implementation type="PKCS12"/>
<file path="server.keystore" relative-to="jboss.server.config.dir"/>
</key-store>
</key-stores>
<key-managers>
<key-manager name="LocalhostKeyManager" key-store="LocalhostKeyStore">
<credential-reference clear-text="key_password"/>
</key-manager>
</key-managers>
<server-ssl-contexts>
<server-ssl-context name="LocalhostSslContext" key-manager="LocalhostKeyManager"/>
</server-ssl-contexts>
</tls>
</subsystem>
Output https-listener
in Undertow subsystem should be:
<https-listener name="https" socket-binding="https" ssl-context="LocalhostSslContext" enable-http2="true"/>
Client-Cert SSL Authentication Migration
This suppose you have already configured Client-Cert SSL authentication
using truststore
in legacy security-realm
, for example by
Admin
Guide#Add Client-Cert to SSL, and your configuration looks like:
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<keystore path="server.keystore" relative-to="jboss.server.config.dir" keystore-password="keystore_password" alias="server" key-password="key_password" />
</ssl>
</server-identities>
<authentication>
<truststore path="server.truststore" relative-to="jboss.server.config.dir" keystore-password="truststore_password" />
<local default-user="$local"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
</security-realm>
Following configuration is sufficient to prevent users without valid
certificate and private key to access the server, but it does not
provide user identity to the application. That require to define
CLIENT_CERT HTTP mechanism / EXTERNAL SASL mechanism, which will be
described later.)
|
At first use steps above to migrate basic part of the configuration. Then continue by following:
-
Create
key-store
of truststore - like for keystore above:/subsystem=elytron/key-store=TrustStore:add(path=server.truststore,relative-to=jboss.server.config.dir,credential-reference={clear-text="truststore_password"},type=PKCS12)
-
Create
trust-manager
- specifyingkey-store
of trustore, created above:/subsystem=elytron/trust-manager=TrustManager:add(key-store=TrustStore)
-
Modify
server-ssl-context
to use newly created trustmanager:/subsystem=elytron/server-ssl-context=LocalhostSslContext:write-attribute(name=trust-manager,value=TrustManager)
-
Enable client authentication for
server-ssl-context
:/sub