Security
Secure ForgeRock® Identity Management deployments.
Out-of-the-box, IDM is set up for ease of development and deployment. When you deploy IDM in production, there are specific precautions you should take to minimize security breaches. This guide describes the IDM security mechanisms and strategies you can use to reduce risk and mitigate threats to IDM security.
ForgeRock Identity Platform™ serves as the basis for our simple and comprehensive Identity and Access Management solution. We help our customers deepen their relationships with their customers, and improve the productivity and connectivity of their employees and partners. For more information about ForgeRock and about the platform, refer to https://www.forgerock.com.
The ForgeRock Common REST API works across the platform to provide common ways to access web resources and collections of resources.
Secret stores, certificates, and keys
Encryption makes it possible to protect sensitive data. IDM depends on encryption to negotiate secure network connections, and to keep sensitive data confidential. Encryption in turn depends on keys. IDM stores keys in secret stores, and supports the following secret store types:
-
File-based keystores
-
Property-based secret stores
-
Hardware Security Modules (HSM)
In production environments, avoid using self-signed certificates and certificates associated with insecure ciphers. |
Secret stores
Configure secret stores in your project’s conf/secrets.json
file, which has the following default configuration:
{
"stores": [
{
"name": "mainKeyStore",
"class": "org.forgerock.openidm.secrets.config.FileBasedStore",
"config": {
"file": "&{openidm.keystore.location|&{idm.install.dir}/security/keystore.jceks}",
"storetype": "&{openidm.keystore.type|JCEKS}",
"providerName": "&{openidm.keystore.provider|SunJCE}",
"storePassword": "&{openidm.keystore.password|changeit}",
"mappings": [
{
"secretId" : "idm.default",
"types": [ "ENCRYPT", "DECRYPT" ],
"aliases": [ "&{openidm.config.crypto.alias|openidm-sym-default}" ]
},
...
]
}
},
{
"name": "mainTrustStore",
"class": "org.forgerock.openidm.secrets.config.FileBasedStore",
"config": {
"file": "&{openidm.truststore.location|&{idm.install.dir}/security/truststore}",
"storetype": "&{openidm.truststore.type|JKS}",
"providerName": "&{openidm.truststore.provider|SUN}",
"storePassword": "&{openidm.truststore.password|changeit}",
"mappings": [
]
}
}
],
"populateDefaults": true
}
The mainKeyStore
and mainTrustStore
properties configure the default secret stores. IDM requires these properties in order to start up. Do not change the property names because they are also provided to third-party products that need a single keystore and a single truststore.
mainKeyStore
-
The main keystore references a Java Cryptography Extension Keystore (JCEKS) located at
/path/to/openidm/security/keystore.jceks
. mainTrustStore
-
The main truststore references a file-based truststore located at
/path/to/openidm/security/truststore
. populateDefaults
-
When IDM first starts up, it checks the secrets configuration. If
"populateDefaults": true
, IDM writes a number of encryption keys to the keystore, required to encrypt specific data.
You can manage these keystores and truststores using the keytool
command, included in your Java installation. For information about the keytool
command, refer to https://docs.oracle.com/en/java/javase/11/tools/keytool.html.
Each configured store has a name
and class
, and the following configuration properties:
file
-
For file-based secret stores, this property references the path to the store file, for example,
&{idm.install.dir}/security/keystore.jceks}
. Hardware security modules do not have afile
property. storetype
-
The type of secret store. IDM supports a number of store types, including JCEKS, JKS, PKCS #11, and PKCS #12.
providerName
-
Sets the name of the cryptographic service provider; for example,
SunPKCS11
orsoftHSM
. If no provider is specified, the JRE default is used. storePassword
-
The password to the secret store. For the default IDM keystore and trustore, the password is
changeit
. You should change this password in a production deployment, as described in Changing the Default Keystore Password. mappings
-
This object lets you map keys and certificates in the secret stores to specific encryption and decryption functionality in IDM. A secrets mapping object has the following structure:
{ "secretId" : "idm.config.encryption", "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ "&{openidm.config.crypto.alias|openidm-sym-default}" ] }
-
secretId
lets you map a secret to one or more aliases and gives an indication of the secret’s purpose. For example,idm.config.encryption
indicates the aliases that are used to encrypt and decrypt sensitive configuration properties. -
types
indicates what the keys are used for; for example, encryption and decryption of sensitive property values. -
aliases
are the key aliases in the secret store that are used for this purpose. You can add as many aliases as necessary. The first alias in the list determines which alias is the active one. Active secrets are used for signature generation and encryption.The aliases in the default keystore are described in Default keystore.
The default secret IDs and the aliases to which they are mapped are listed in Mapping SecretIDs to Key Aliases.
-
All these properties have a resolvable property value by default; for example
For more information, refer to Property Value Substitution. |
Mapping secretIDs to key aliases
secretId |
alias |
Description |
---|---|---|
|
|
Encryption keystore for legacy JSON objects that do not contain a |
|
|
Encrypts configuration information |
|
|
Encrypts managed user passwords |
|
|
Encrypts JWT session tokens |
|
|
Signs JWT session tokens using HMAC |
|
|
Encrypts JWT self-service tokens |
|
|
Signs JWT session tokens using RSA |
|
|
Encrypts confidential assignment attributes |
Default keystore
IDM generates a number of encryption keys in a JCEKS keystore the first time the server starts up. These keys map to the secrets defined in Mapping SecretIDs to Key Aliases. Note that the keystore, and the keys, are generated at startup and are not prepackaged. The keys are generated only if they do not already exist. You cannot specify custom aliases for these default keys.
To use a different keystore type, such as PKCS #12, create the keystore and generate the keys before you start IDM. This prevents IDM from generating the keys on startup. You can also convert the existing JCEKS keystore to a PKCS #12 keystore. If you use a different keystore type, you must edit the openidm.keystore.type
property (in the conf/secrets.json
file) to match the new type.
Use the keytool
command to list the default encryption keys, as follows:
keytool \ -list \ -keystore /path/to/openidm/security/keystore.jceks \ -storepass changeit \ -storetype JCEKS Keystore type: JCEKS Keystore provider: SunJCE Your keystore contains 5 entries openidm-sym-default, Nov 5, 2019, SecretKeyEntry, openidm-jwtsessionhmac-key, Nov 5, 2019, SecretKeyEntry, selfservice, Nov 5, 2019, PrivateKeyEntry, Certificate fingerprint (SHA-256): E9:0B:BA:FB:58:73:02:FC...:7B openidm-selfservice-key, Nov 5, 2019, SecretKeyEntry, openidm-localhost, Nov 5, 2019, PrivateKeyEntry, Certificate fingerprint (SHA-256): 21:50:6C:90:C7:A7:F7:32...:1B
If you are using IDM in a cluster, you must share these keys among all nodes in the cluster. The easiest way to do this is to generate a keystore with the appropriate keys and share the keystore in some way; for example, by using a filesystem that is shared between the nodes. |
Change the default keystore password
The default keystore password is changeit
. You should change this password in a production environment.
Repeat this procedure on each node if you run multiple nodes in a cluster to ensure that the new password is present on all nodes. |
-
Shut down the server if it is running.
-
Use the
keytool
command to change the keystore password. The following command changes the keystore password tonewPassword
:keytool \ -storepasswd \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype jceks \ -storepass changeit New keystore password: newPassword Re-enter new keystore password: newPassword
-
Change the passwords of the default encryption keys.
IDM uses a number of encryption keys, listed in Mapping SecretIDs to Key Aliases, whose passwords are also
changeit
by default. The passwords of each of these keys must match the password of the keystore.To get the list of keys in the keystore, run the following command:
keytool \ -list \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype jceks \ -storepass newPassword Keystore type: JCEKS Keystore provider: SunJCE Your keystore contains 5 entries openidm-sym-default, May 4, 2021, SecretKeyEntry, selfservice, May 4, 2021, PrivateKeyEntry, Certificate fingerprint (SHA-256): fingerprint openidm-jwtsessionhmac-key, May 4, 2021, SecretKeyEntry, openidm-localhost, May 4, 2021, PrivateKeyEntry, Certificate fingerprint (SHA-256): fingerprint openidm-selfservice-key, May 4, 2021, SecretKeyEntry,
Change the passwords of each default encryption key as follows:
keytool \ -keypasswd \ -alias openidm-localhost \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype jceks \ -storepass newPassword Enter key password for <openidm-localhost> changeit New key password for <openidm-localhost>: newPassword Re-enter new key password for <openidm-localhost>: newPassword
keytool \ -keypasswd \ -alias openidm-sym-default \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype jceks \ -storepass newPassword Enter key password for <openidm-sym-default> changeit New key password for <openidm-sym-default>: newPassword Re-enter new key password for <openidm-sym-default>: newPassword
keytool \ -keypasswd \ -alias openidm-selfservice-key \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype jceks \ -storepass newPassword Enter key password for <openidm-selfservice-key> changeit New key password for <openidm-selfservice-key>: newPassword Re-enter new key password for <openidm-selfservice-key>: newPassword
keytool \ -keypasswd \ -alias selfservice \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype jceks \ -storepass newPassword Enter key password for <selfservice> changeit New key password for <selfservice>: newPassword Re-enter new key password for <selfservice>: newPassword
keytool \ -keypasswd \ -alias openidm-jwtsessionhmac-key \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype jceks \ -storepass newPassword Enter key password for <openidm-jwtsessionhmac-key> changeit New key password for <openidm-jwtsessionhmac-key>: newPassword Re-enter new key password for <openidm-jwtsessionhmac-key>: newPassword
-
Configure a new expression resolver file to store only the keystore password:
-
Create a new directory in
/path/to/openidm/resolver/
that will contain only the properties file for keystore passwords. For example:mkdir /path/to/openidm/resolver/keystore
Substituted properties are not encrypted by default. You must therefore secure access to this directory, using the appropriate permissions. -
Set the
IDM_ENVCONFIG_DIRS
environment variable to include the new directory:export IDM_ENVCONFIG_DIRS=/path/to/openidm/resolver/,/path/to/openidm/resolver/keystore
-
Create a
.json
or.properties
file in that secure directory, that contains the new keystore password as a resolvable IDM property. For example, add one of the following files to that directory:
-
Encryption key management
Most regulatory requirements mandate that the keys used to decrypt sensitive data be rotated out and replaced with new keys on a regular basis. The main purpose of rotating encryption keys is to reduce the amount of data encrypted with that key, so that the potential impact of a security breach with a specific key is reduced. You can update encryption keys in several ways, including the following:
Manual key rotation
IDM evaluates keys in secrets.json
sequentially. For example, assume that you have added a new key named my-new-key
to the keystore. To use this new key to encrypt passwords, you would include my-new-key
as the first alias in the idm.password.encryption
secret:
{
"secretId" : "idm.password.encryption",
"types": [ "ENCRYPT", "DECRYPT" ],
"aliases": [ "my-new-key", "&{openidm.config.crypto.alias|openidm-sym-default}" ]
}
The properties that use this key (in this case, passwords) are re-encrypted with the new key the next time the managed object is updated. You do not need to restart the server.
If you rotate an encryption key, the active encryption key might not be the correct key to use for decryption of properties that have already been encrypted with a previous key. You must therefore keep all applicable keys in |
You can force key rotation on all managed objects by running the triggerSyncCheck
action on the entire managed object data set. The triggerSyncCheck
action examines the crypto blob of each object and updates the encrypted property with the correct key.
For example, the following command forces all managed user objects to use the new key:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --request POST \ "https://localhost:8443/openidm/managed/user/?_action=triggerSyncCheck" { "status": "OK", "countTriggered": 10 }
In a large managed object set, the triggerSyncCheck
action can take a long time to run on only a single node. You should therefore avoid using this action if your data set is large. An alternative to running triggerSyncCheck
over the entire data set is to iterate over the managed data set and call triggerSyncCheck
on each individual managed object. You can call this action manually or by using a script.
The following example shows the manual commands that must be run to launch the triggerSyncCheck
action on all managed users. The first command uses a query filter to return all managed user IDs. The second command iterates over the returned IDs calling triggerSyncCheck
on each ID:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ "https://localhost:8443/openidm/managed/user?_queryFilter=true&_fields=_id" { "result": [ { "_id": "9dce06d4-2fc1-4830-a92b-bd35c2f6bcbb", "_rev": "000000004988917b" }, { "_id": "55ef0a75-f261-47e9-a72b-f5c61c32d339", "_rev": "00000000dd89d671" }, { "_id": "998a6181-d694-466a-a373-759a05840555", "_rev": "000000006fea54ad" }, ... ] }
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --request POST \ "https://localhost:8443/openidm/managed/user/9dce06d4-2fc1-4830-a92b-bd35c2f6bcbb?_action=triggerSyncCheck"
In large data sets, the most efficient way to achieve key rotation is to use the scheduler service to launch these commands. The following section shows how to use the scheduler service for this purpose.
Scheduled key rotation
This example uses a script to generate multiple scheduled tasks. Each scheduled task iterates over a subset of the managed object set (defined by the pageSize
). The generated scheduled task then calls another script that launches the triggerSyncCheck
action on each managed object in that subset.
To schedule key rotation, set up a similar schedule:
-
Create a schedule configuration named
schedule-triggerSyncCheck.json
in your project’sconf
directory. That schedule should look as follows:{ "enabled" : true, "persisted" : true, "type" : "cron", "schedule" : "0 * * * * ? *", "concurrentExecution" : false, "invokeService" : "script", "invokeContext" : { "waitForCompletion" : false, "script": { "type": "text/javascript", "name": "sync/scheduleTriggerSyncCheck.js" }, "input": { "pageSize": 2, "managedObjectPath" : "managed/user", "quartzSchedule" : "0 * * * * ? *" } } }
You can change the following parameters of this schedule configuration to suit your deployment:
pageSize
-
The number of objects that each generated schedule will handle. This value should be high enough not to create too many schedules. The number of schedules that is generated is equal to the number of objects in the managed object store, divided by the page size.
For example, if there are 500 managed users and a page size of 100, five schedules will be generated (500/100).
managedObjectPath
-
The managed object set over which the scheduler iterates. For example,
managed/user
if you want to iterate over the managed user object set. quartzSchedule
-
The schedule at which these tasks should run. For example, to run the task every minute, this value would be
0 * * * * ? *
.
-
The schedule calls a
scheduleTriggerSyncCheck.js
script, located in a directory namedproject-dir/script/sync
. Create thesync
directory, and add the script:var managedObjectPath = object.managedObjectPath; var pageSize = object.pageSize; var quartzSchedule = object.quartzSchedule; var managedObjects = openidm.query(managedObjectPath, { "_queryFilter": "true", "_fields": "_id" }); var numberOfManagedObjects = managedObjects.result.length; for (var i = 0; i < numberOfManagedObjects; i += pageSize) { var scheduleId = java.util.UUID.randomUUID().toString(); var ids = managedObjects.result.slice(i, i + pageSize).map(function(obj) { return obj._id }); var schedule = newSchedule(scheduleId, ids); openidm.create("/scheduler", scheduleId, schedule); } function newSchedule(scheduleId, ids) { var schedule = { "enabled": true, "persisted": true, "type": "cron", "schedule": quartzSchedule, "concurrentExecution": false, "invokeService": "script", "invokeContext": { "waitForCompletion": true, "script": { "type": "text/javascript", "name": "sync/triggerSyncCheck.js" }, "input": { "ids": ids, "managedObjectPath": managedObjectPath, "scheduleId": scheduleId } } }; return schedule; }
-
Each generated scheduled task calls a script named
triggerSyncCheck.js
. Create the script in your project’sscript/sync
directory:var ids = object.ids; var scheduleId = object.scheduleId; var managedObjectPath = object.managedObjectPath; for (var i = 0; i & lt; ids.length; i++) { openidm.action(managedObjectPath + "/" + ids[i], "triggerSyncCheck", {}, {}); } openidm.delete("scheduler/" + scheduleId, null);
-
Test the key rotation:
-
Edit your project’s
conf/managed.json
file to return user passwords by default by setting"scope" : "public"
."password" : { ... "encryption" : { "purpose" : "idm.password.encryption" }, "scope" : "public", ... }
Because passwords are not returned by default, you will not be able to refer to the new encryption on the password unless you change the property’s
scope
. -
Perform a GET request to return any managed user entry in your data set:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --request GET \ "https://localhost:8443/openidm/managed/user/ccd92204-aee6-4159-879a-46eeb4362807" { "_id" : "ccd92204-aee6-4159-879a-46eeb4362807", "_rev" : "0000000009441230", "preferences" : { "updates" : false, "marketing" : false }, "mail" : "bjensen@example.com", "sn" : "Jensen", "givenName" : "Babs", "userName" : "bjensen", "password" : { "$crypto" : { "type" : "x-simple-encryption", "value" : { "cipher" : "AES/CBC/PKCS5Padding", "stableId" : "openidm-sym-default", "salt" : "CVrKDuzfzunXfTDbCwU1Rw==", "data" : "1I5tWT5aRH/12hf5DgofXA==", "keySize" : 16, "purpose" : "idm.password.encryption", "iv" : "LGE+jnC3ZtyvrE5pfuSvtA==", "mac" : "BEXQ1mftxA63dXhJO6dDZQ==" } } }, "accountStatus" : "active", "effectiveRoles" : [ ], "effectiveAssignments" : [ ] }
Notice that the user’s password is encrypted with the default encryption key (
openidm-sym-default
). -
Create a new encryption key in the IDM keystore:
keytool \ -genseckey \ -alias my-new-key \ -keyalg AES \ -keysize 128 \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype JCEKS
-
Shut down the server for keystore to be reloaded.
-
Change your project’s
conf/managed.json
file to change the encryption purpose for managed user passwords:"password" : { ... "encryption" : { "purpose" : "idm.password.encryption2" }, "scope" : "public", ... }
-
Add the corresponding
purpose
to thesecrets.json
file in themainKeyStore
code block:"idm.password.encryption2": { "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ { "alias": "my-new-key" } ] }
-
Restart the server and wait one minute for the scheduled task to start.
-
Perform a GET request again to return the entry of the managed user that you returned previously:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --request GET \ "https://localhost:8443/openidm/managed/user/ccd92204-aee6-4159-879a-46eeb4362807" { "_id" : "ccd92204-aee6-4159-879a-46eeb4362807", "_rev" : "0000000009441230", "preferences" : { "updates" : false, "marketing" : false }, "mail" : "bjensen@example.com", "sn" : "Jensen", "givenName" : "Babs", "userName" : "bjensen", "password" : { "$crypto" : { "type" : "x-simple-encryption", "value" : { "cipher" : "AES/CBC/PKCS5Padding", "stableId" : "my-new-key", "salt" : "CVrKDuzfzunXfTDbCwU1Rw==", "data" : "1I5tWT5aRH/12hf5DgofXA==", "keySize" : 16, "purpose" : "idm.password.encryption2", "iv" : "LGE+jnC3ZtyvrE5pfuSvtA==", "mac" : "BEXQ1mftxA63dXhJO6dDZQ==" } } }, "accountStatus" : "active", "effectiveRoles" : [ ], "effectiveAssignments" : [ ] }
The user password is now encrypted with
my-new-key
.
-
Change the active alias for managed object encryption
This example describes how to configure and then change the managed object encryption key with a scheduled task. You’ll create a new key, set up a managed user, add the key to secrets.json
, restart IDM, run a triggerSyncCheck
, and review the result.
-
Create a new key for the IDM keystore in the
security/keystore.jceks
file:keytool \ -genseckey \ -alias my-new-key \ -keyalg AES \ -keysize 128 \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype JCEKS
-
For the purpose of this example, in
managed.json
, set"scope" : "public"
to expose the applied password encryption key. -
Create a managed user:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --request PUT \ --data '{ "userName": "rsutter", "sn": "Sutter", "givenName": "Rick", "mail": "rick@example.com", "telephoneNumber": "6669876987", "description": "Another user", "country": "USA", "password": "Passw0rd" }' \ "https://localhost:8443/openidm/managed/user/ricksutter"
-
Add the newly created
my-new-key
alias to yourconf/secrets.json
file, in theidm.password.encryption
code block:"idm.password.encryption": { "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ "my-new-key", "&{openidm.config.crypto.alias|openidm-sym-default}" ] }
-
To apply the new key to your configuration, shut down and restart IDM.
-
Force IDM to update the key for your users with the
triggerSyncCheck
action:curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --request POST \ "https://localhost:8443/openidm/managed/user/?_action=triggerSyncCheck"
-
Review the result for the newly created user,
ricksutter
:curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --request GET \ "https://localhost:8443/openidm/managed/user/ricksutter"
-
In the output, you should see the new
my-new-key
encryption key applied to that user’s password:... "password": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "my-new-key", "salt": "bGyKG3PKmwHONOfxerr1Qg==", "data": "6vXZiJ3ZNN/UUnsrT7dTQw==", "keySize": 16, "purpose": "idm.password.encryption", "iv": "doAdtxfWfFbrPIIfubGi5g==", "mac": "OML6xd9qvDtD5AvMc1Tc3A==" } } }, ...
CA-signed certificates
You can use existing CA-signed certificates to secure connections and data by importing the certificates into the keystore, and referencing them your boot.properties
file. Use the keytool
command to import an existing certificate into the keystore.
Import CA-signed certificates
The following process imports a CA-signed certificate into the keystore, with the alias example-com. Replace this alias with the alias of your certificate:
-
Stop the server if it is running.
-
Back up your existing
openidm/security/keystore
andopenidm/security/truststore
files. -
Use the
keytool
command to import your existing certificate into the keystore, substituting your specific information:example-cert.p12
The name of your certificate file.
srcstorepass
The certificate password.
example-com
The existing certificate alias.
destination keystore password
The password for the keystore.
keytool \ -importkeystore \ -srckeystore example-cert.p12 \ -srcstoretype PKCS12 \ -srcstorepass changeit \ -srcalias example-com \ -destkeystore keystore.jceks \ -deststoretype JCEKS \ -destalias openidm-localhost Importing keystore example-cert.p12 to keystore.jceks… Enter destination keystore password: changeit
The keytool command creates a trusted certificate entry with the specified alias and associates it with the imported certificate. The certificate is imported into the keystore with the alias
openidm-localhost
. If you want to use a different alias, you must modify yourresolver/boot.properties
file to reference that alias, as shown in the following step.The certificate entry password must be the same as the IDM keystore password. If the source certificate entry password is different from the target keystore password, use the
-destkeypass
option with the same value as the-deststorepass
option to make the certificate password match the target keystore password. If you do not make these passwords the same, no error is generated when you import the certificate (or when you read the certificate entry in the destination keystore), but IDM will fail to start with the following exception:java.security.UnrecoverableKeyException: Given final block not properly padded.
-
If you specified an alias other than
openidm-localhost
for the new certificate, change the value ofopenidm.https.keystore.cert.alias
in yourresolver/boot.properties
file to that alias. For example, if your new certificate alias isexample-com
, change theboot.properties
file as follows:openidm.https.keystore.cert.alias=example-com
-
Restart the server.
Delete certificates
When using CA-signed certificates for encryption, it is a best practice to delete all unused default certificates from the keystore and truststore using the keytool
command, as shown in the following examples:
-
To delete the
openidm-localhost
certificate from the keystore:keytool \ -delete \ -alias openidm-localhost \ -keystore /path/to/openidm/security/keystore.jceks \ -storetype JCEKS \ -storepass changeit
-
To delete the
openidm-localhost
certificate from the truststore:keytool \ -delete \ -alias openidm-localhost \ -keystore /path/to/openidm/security/truststore \ -storepass changeit
You can use similar commands to delete custom certificates from the keystore and truststore, specifying the certificate alias in the request.
Delete root CA certificates
The Java and IDM truststore files include a number of root CA certificates. Although the probability of a compromised root CA certificate is low, it is a best practice to delete unused root CA certificates.
To review the list of root CA certificates in the IDM truststore:
keytool \ -list \ -keystore /path/to/openidm/security/truststore \ -storepass changeit
On UNIX/Linux systems, you can find additional lists of root CA certificates in files named |
Before making changes to Java environment keystore files, verify any Java-related cacerts
files are up-to-date and that you have a supported Java version installed.
You can delete root CA certificates with the keytool
command. For example, to remove the hypothetical examplecomca2
certificate from the truststore:
keytool \ -delete \ -keystore /path/to/openidm/security/truststore \ -storepass changeit \ -alias examplecomca2
On Windows systems, you can manage certificates with the Microsoft Management Console (MMC) snap-in tool. For more information, refer to Working With Certificates. |
Property-based secret stores
IDM servers can read keys and trusted certificates from properties that contain keys in Privacy-Enhanced Mail (PEM) format.
The following example configures a property-based secret store, and adds an RSA PEM secret whose purpose is to encrypt and decrypt managed user passwords:
-
Add a
PropertyBasedStore
secret store definition to yourconf/secrets.json
file:{ "name": "pemStore", "class": "org.forgerock.openidm.secrets.config.PropertyBasedStore", "config": { "format": "PEM", "algorithm": "RSA", "mappings": [ { "secretId": "idm.pem.purpose", "types": [ "ENCRYPT", "DECRYPT" ] } ] } }
-
Create an RSA PEM key:
openssl genrsa -out private-key.pem 3072
-
Display the private key. For example:
more private-key.pem -----BEGIN RSA PRIVATE KEY----- MIIG4w…lrDgao -----END RSA PRIVATE KEY-----
-
Use a text editor to convert your certificate to a single line, replacing line breaks with newline characters (
/n
). For example, on UNIX systems:awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' private-key.pem -----BEGIN RSA PRIVATE KEY-----\nMIIG4w…lrDgao\n-----END RSA PRIVATE KEY-----\n%
-
Copy the single-line private key and paste it into your
resolver/boot.properties
file, as a value of thesecretId
that you specified in Step 1. For example:idm.pem.purpose=-----BEGIN RSA PRIVATE KEY-----\nMIIG4w...lrDgao\n-----END RSA PRIVATE KEY-----\n%
-
Modify the encryption purpose for the managed user
password
in your managed object configuration to use thePropertyBaseStore
secret store that you created in Step 1:"password" : { "title" : "Password", "description" : "Password", "type" : "string", "viewable" : false, "searchable" : false, "userEditable" : true, "encryption" : { "purpose" : "idm.pem.purpose", "cipher" : "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" } ... }
IDM now encrypts and decrypts passwords with the RSA PEM key.
Hardware security module (HSM)
This topic demonstrates how to use a PKCS #11 device, such as a hardware security module (HSM), to store the keys used to secure communications. IDM supports retrieval of secrets from HSMs either locally or over the network.
On Windows systems using the 64-bit JDK, the Sun PKCS #11 provider is available only from JDK version 1.8b49. If you want to use a PKCS #11 device on Windows, use the 32-bit version of the JDK, or upgrade your 64-bit JDK to version 1.8b49 or higher. |
HSM configuration
This section assumes that you have access to an HSM device (or a software emulation of an HSM device, such as SoftHSM) and that the HSM provider has been configured and initialized.
The command-line examples in this section use SoftHSM for testing purposes. Before you start, set the correct environment variable for the SoftHSM configuration, for example:
export SOFTHSM2_CONF=/path/to/softhsm/2.0.0/etc/softhsm2.conf
Also initialize slot 0
on the provider, with a command similar to the following:
softhsm2-util --init-token --slot 0 --label "My token 1"
This token initialization requests two PINs—an SO PIN and a user PIN. You can use the SO PIN to reinitialize the token. The user PIN is provided to IDM so that it can interact with the token. Remember the values of these PINs because you will use them later in this section.
The PKCS #11 standard uses a configuration file to interact with the HSM device. The following example shows a basic configuration file for SoftHSM:
name = softHSM
library = /path/to/softhsm/2.0.0/lib/softhsm/libsofthsm2.so
slot = 1
attributes(generate, *, *) = {
CKA_TOKEN = true
}
attributes(generate, CKO_CERTIFICATE, *) = {
CKA_PRIVATE = false
}
attributes(generate, CKO_PUBLIC_KEY, *) = {
CKA_PRIVATE = false
}
attributes(*, CKO_SECRET_KEY, *) = {
CKA_PRIVATE = false
CKA_EXTRACTABLE = true
}
Your HSM configuration file must include at least the following settings:
name
-
A suffix to identify the HSM provider. This example uses the
softHSM
provider. library
-
The path to the PKCS #11 library.
slot
-
The slot number to use, specified as a string. Make sure that the slot you specify here has been initialized on the HSM device.
The attributes
specify additional PKCS #11 attributes that are set by the HSM. For a complete list of these attributes, refer to the PKCS #11 Reference.
If you are using the JWT Session Module, you must set
The HSM provider must allow secret keys to be extractable because the authentication service serializes the JWT Session Module key and passes it to the authentication framework as a base 64-encoded string. |
HSM default encryption keys
When IDM first starts up, it generates a number of encryption keys required to encrypt specific data. If you are using an HSM provider, you must generate these keys manually. The secret keys must use an HMAC algorithm.
This procedure assumes that your HSM configuration file is located at |
-
The
openidm-sym-default
key is the default symmetric key required to encrypt the configuration. The following command generates that key in the HSM provider. The-providerArg
must point to the HSM configuration file described in HSM Configuration.keytool \ -genseckey \ -alias openidm-sym-default \ -keyalg HmacSHA256 \ -keysize 256 \ -keystore NONE \ -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /path/to/hsm/hsm.conf Enter keystore password:
Enter the password of your HSM device. If you are using SoftHSM, enter your user PIN as the keystore password.
-
The
openidm-selfservice-key
is used by the Self-Service UI to encrypt managed user passwords and other sensitive data. Generate theopenidm-selfservice-key
key:keytool \ -genseckey \ -alias openidm-selfservice-key \ -keyalg HmacSHA256 \ -keysize 256 \ -keystore NONE \ -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /path/to/hsm/hsm.conf Enter keystore password: user PIN
Enter the password of your HSM device. If you are using SoftHSM, enter your user PIN as the keystore password.
-
The
openidm-jwtsessionhmac-key
is used by the JWT session module to encrypt JWT session cookies. Generate the JWT session module key:keytool \ -genseckey \ -alias openidm-jwtsessionhmac-key \ -keyalg HmacSHA256 \ -keysize 256 \ -keystore NONE \ -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /path/to/hsm/hsm.conf Enter keystore password: user PIN
-
The
openidm-localhost
certificate is used to support SSL/TLS. Generate the certificate:keytool \ -genkey \ -alias openidm-localhost \ -keyalg RSA \ -keysize 2048 \ -keystore NONE \ -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /path/to/hsm/hsm.conf Enter keystore password: user PIN 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]: OpenIDM Self-Signed Certificate 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=OpenIDM Self-Signed Certificate, L=Unknown, ST=Unknown, C=Unknown correct? [no]: yes
-
The
selfservice
certificate secures requests from the End User UI. Generate the certificate:keytool \ -genkey \ -alias selfservice \ -keyalg RSA \ -keysize 2048 \ -keystore NONE \ -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /path/to/hsm/hsm.conf Enter keystore password: user PIN 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]: OpenIDM Self Service Certificate 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,O=OpenIDM Self Service Certificate,OU=None,L=None,ST=None,C=None? [no]: yes
-
If you are not using the HSM provider for the truststore, you must add the certificates generated in the previous two steps to the default IDM truststore.
If you are using the HSM provider for the truststore, you can skip this step.
To add the
openidm-localhost
certificate to the IDM truststore, export the certificate from the HSM provider, then import it into the truststore:keytool \ -export \ -alias openidm-localhost \ -file exportedCert \ -keystore NONE \ -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /path/to/hsm/hsm.conf Enter keystore password: user PIN Certificate stored in file exportedCert keytool \ -import \ -alias openidm-localhost \ -file exportedCert \ -keystore /path/to/openidm/security/truststore Enter keystore password: changeit Owner: CN=localhost, OU=Unknown, O=OpenIDM Self-Signed Certificate, L=… Issuer: CN=localhost, OU=Unknown, O=OpenIDM Self-Signed Certificate, L=… Serial number: 5d2554bd Valid from: Fri Aug 19 13:11:54 SAST 2016 until: Thu Nov 17 13:11:54 SAST 2016 Certificate fingerprints: MD5: F1:9B:72:7F:7B:79:58:29:75:85:82:EC:79:D8:F9:8D SHA1: F0:E6:51:75:AA:CB:14:3D:C5:E2:EB:E5:7C:87:C9:15:43:19:AF:36 SHA256: 27:A5:B7:0E:94:9A:32:48:0C:22:0F:BB:7E:3C:22:2A:64:B5:45:24:14:70:... Signature algorithm name: SHA256withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 7B 5A 26 53 61 44 C2 5A 76 E4 38 A8 52 6F F2 89 .Z&SaD.Zv.8.Ro.. 0010: 20 04 52 EE .R. ] ] Trust this certificate? [no]: yes Certificate was added to keystore
The default truststore password is changeit.
Configure IDM to support an HSM provider
To enable IDM to use an HSM provider, make the following configuration changes:
-
In your secret store configuration (
conf/secrets.json
), change themainKeyStore
andmainTrustStore
to reference the HSM. For example:{ "stores": [ { "name": "mainKeyStore", "class": "org.forgerock.openidm.secrets.config.HsmBasedStore", "config": { "storetype": "&{openidm.keystore.type|PKCS11}", "providerName": "&{openidm.keystore.provider|SunPKCS11-softHSM}", "storePassword": "&{openidm.keystore.password|changeit}", "mappings": [ { "secretId" : "idm.default", "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ "&{openidm.config.crypto.alias|openidm-sym-default}" ] }, { "secretId" : "idm.config.encryption", "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ "&{openidm.config.crypto.alias|openidm-sym-default}" ] }, { "secretId" : "idm.password.encryption", "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ "&{openidm.config.crypto.alias|openidm-sym-default}" ] }, { "secretId" : "idm.jwt.session.module.encryption", "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ "&{openidm.https.keystore.cert.alias|openidm-localhost}" ] }, { "secretId" : "idm.jwt.session.module.signing", "types": [ "SIGN", "VERIFY" ], "aliases": [ "&{openidm.config.crypto.jwtsession.hmackey.alias|openidm-jwtsessionhmac-key}" ] }, { "secretId" : "idm.selfservice.signing", "types": [ "SIGN", "VERIFY" ], "aliases": [ "selfservice" ] }, { "secretId" : "idm.selfservice.encryption", "types": [ "ENCRYPT", "DECRYPT" ], "aliases": [ "&{openidm.config.crypto.selfservice.sharedkey.alias|openidm-selfservice-key}" ] } ] } }, { "name": "mainTrustStore", "class": "org.forgerock.openidm.secrets.config.HsmBasedStore", "config": { "storetype": "&{openidm.keystore.type|PKCS11}", "providerName": "&{openidm.keystore.provider|SunPKCS11-softHSM}", "storePassword": "&{openidm.keystore.password|changeit}", "mappings": [ ] } } ], "populateDefaults": false }
The "populateDefaults": false
turns off the default key generation. This setting is required for an HSM key provider. -
In the IDM Java security file (
conf/java.security
), Specify the location of your PKCS #11 configuration file. For example:security.provider.14=SunPKCS11 /path/to/pkc11/config/pkcs11.conf
Templates for the
pkcs11.conf
file are included in your PKCS package.You should now be able to start IDM with the keys in the HSM provider.
FIPS 140-2 compliance
To achieve FIPS 140-2 compliance, configure the Bouncy Castle FIPS libraries with IDM. This enables the use of the Bouncy Castle FIPS keystore and security provider in FIPS-approved mode.
Bouncy Castle FIPS is useful when dealing with government data, where meeting the FIPS 140-2 security requirement is necessary for regulatory compliance.
Bouncy Castle FIPS is less performant than other keystores. The destroyable keys cannot be cached and must be read from the keystore with every use. |
To configure IDM to use Bouncy Castle FIPS:
Download the Bouncy Castle libraries
The IDM CLI does not work when using Bouncy Castle FIPS. |
To use Bouncy Castle FIPS with IDM, download the libraries:
-
Download the following libraries from Bouncy Castle to the server/machine where IDM is deployed:
File Description bc-fips-latestVersionNumber.jar
(1)Contains the Bouncy Castle FIPS security provider implementation.
bcpkix-fips-latestVersionNumber.jar
(2)Provides FIPS support for cert generation.
bctls-fips-latestVersionNumber.jar
(3)Provides TLS support using FIPS compliance.
(1) The tested version is
bc-fips-1.0.2.3.jar
.(2) The tested version is
bcpkix-fips-1.0.7.jar
.(3) The tested version is
bctls-fips-1.0.14.jar
. -
Copy the downloaded files to
/path/to/openidm/bundle
. -
Restart IDM.
Enable the Bouncy Castle FIPS provider in the JVM
To enable the Bouncy Castle FIPS provider in your JVM, do one of the following:
Add Bouncy Castle providers to the existing JVM
If the existing JVM supports Bouncy Castle, then you can add the security providers to the JVM.
Add the Bouncy Castle security providers to $JAVA_HOME/conf/security/java.security
:
security.provider.13=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider C:HYBRID;ENABLE{All};
security.provider.14=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
The security.provider.13 and security.provider.14 keys are JVM specific and must be the next 2 values in the security providers list.
|
Add Bouncy Castle providers to IDM conf/java.security
If the existing JVM supports Bouncy Castle, then you can add the security providers to the /path/to/openidm/conf/java.security
.
This file provides additions to the $JAVA_HOME/conf/security/java.security
file.
Add the Bouncy Castle security providers to /path/to/openidm/conf/java.security
:
security.provider.13=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider C:HYBRID;ENABLE{All};
security.provider.14=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
The security.provider.13 and security.provider.14 keys are JVM specific and must be the next 2 values in the security providers list.
|
Build a distribution of the JVM that supports Bouncy Castle
If the existing JVM doesn’t support Bouncy Castle, you must create a new JVM distribution.
-
Build a new distribution:
$JAVA_HOME/bin/jlink \ --no-header-files \ --no-man-pages \ --compress=2 \ --module-path /Users/bjensen/Downloads \ --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.management.rmi,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.se,java.security.jgss,java.security.sasl,java.smartcardio,java.sql,java.sql.rowset,java.transaction.xa,java.xml,java.xml.crypto,jdk.accessibility,jdk.aot,jdk.attach,jdk.charsets,jdk.compiler,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.dynalink,jdk.editpad,jdk.hotspot.agent,jdk.httpserver,jdk.internal.ed,jdk.internal.jvmstat,jdk.internal.le,jdk.internal.opt,jdk.internal.vm.ci,jdk.internal.vm.compiler,jdk.internal.vm.compiler.management,jdk.jartool,jdk.javadoc,jdk.jcmd,jdk.jconsole,jdk.jdeps,jdk.jdi,jdk.jdwp.agent,jdk.jfr,jdk.jlink,jdk.jshell,jdk.jsobject,jdk.jstatd,jdk.localedata,jdk.management,jdk.management.agent,jdk.management.jfr,jdk.naming.dns,jdk.naming.ldap,jdk.naming.rmi,jdk.net,jdk.pack,jdk.rmic,jdk.scripting.nashorn,jdk.scripting.nashorn.shell,jdk.sctp,jdk.security.auth,jdk.security.jgss,jdk.unsupported,jdk.unsupported.desktop,jdk.xml.dom,jdk.zipfs,org.bouncycastle.fips.core,org.bouncycastle.fips.tls \ --output /location/to/bouncy/castle/jvm --ignore-signing-information
A customized JVM is created at the output location /location/to/bouncy/castle/jvm you specified.
-
Add the Bouncy Castle security providers to
/location/to/bouncy/castle/jvm/conf/security/java.security
:security.provider.13=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider C:HYBRID;ENABLE{All}; security.provider.14=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
The security.provider.13
andsecurity.provider.14
keys are JVM specific and must be the next 2 values in the security providers list.
Create the IDM Bouncy Castle keystore and cryptographic keys
Before you create the cryptographic keys, you must Enable the Bouncy Castle FIPS provider in the JVM. |
To create the necessary IDM cryptographic keys:
-
Create the Bouncy Castle keystore. This can be done in conjunction with creating the first cryptographic key:
keytool \ -genseckey \ -alias openidm-sym-default \ -keyalg aes \ -keysize 256 \ -keystore /location/to/keystore.bcfks \ -storepass changeit -storetype BCFKS \ -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
This creates the
openidm-sym-default
key in a keystore called /location/to/keystore.bcfks while also creating that keystore if it does not exist.You must use the JVM specific keytool that the Bouncy Castle security provider uses.
For example, if you enable the security providers in the system default JVM, you must use the system default keytool command. If you create a custom JVM, you must use the keytool command for where that JVM is located.
The keytool command is in the
bin
directory of the JVM Java home.Failure to use the
keytool
command you configure for Bouncy Castle results in the following error:keytool error: java.lang.Exception: Provider "org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider" not found
-
Create the remaining keys:
Create theopenidm-selfservice-key
keytool \ -genseckey \ -alias openidm-selfservice-key \ -keyalg aes \ -keysize 256 \ -keystore /location/to/keystore.bcfks \ -storepass changeit \ -storetype BCFKS \ -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
Create theopenidm-jwtsessionhmac-key
keytool \ -genseckey \ -alias openidm-jwtsessionhmac-key \ -keyalg aes \ -keysize 256 \ -keystore /location/to/keystore.bcfks \ -storepass changeit \ -storetype BCFKS \ -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
Create theopenidm-localhost
keykeytool \ -genkey \ -alias openidm-localhost \ -keyalg RSA \ -keysize 2048 \ -keystore /location/to/keystore.bcfks \ -storepass changeit \ -storetype BCFKS \ -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
Create theselfservice
keykeytool \ -genkey \ -alias selfservice \ -keyalg RSA \ -keysize 2048 \ -keystore /location/to/keystore.bcfks \ -storepass changeit \ -storetype BCFKS \ -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
Provide the JVM to IDM
If you create a custom JVM location, you must to provide that JVM to IDM in the /path/to/openidm/startup.sh
file.
By default, IDM uses the system Java and falls back to using the JAVA_HOME
if the system Java is not defined:
startup.sh
fileif which java &>/dev/null; then
JAVA=java
elif [ -n "$JAVA_HOME" ] && [ -x "$JAVA_HOME/bin/java" ]; then
JAVA="$JAVA_HOME/bin/java"
else
echo JAVA_HOME not available, Java is needed to run IDM
echo Please install Java and set JAVA_HOME accordingly
exit 1
fi
To configure IDM to use the JAVA_HOME
you set, change the startup.sh
file to the following:
startup.sh
fileif [ -n "$JAVA_HOME" ] && [ -x "$JAVA_HOME/bin/java" ]; then
JAVA="$JAVA_HOME/bin/java"
else
echo JAVA_HOME not available, Java is needed to run IDM
echo Please install Java and set JAVA_HOME accordingly
exit 1
fi
Configure the Bouncy Castle keystore in secrets.json
After you add the Bouncy Castle security providers
and create the keystore and keys,
you must replace the default IDM keystore with the new Bouncy Castle keystore in /path/to/openidm/conf/secrets.json
:
{
"name" : "mainKeyStore",
"class" : "org.forgerock.openidm.secrets.config.FileBasedStore",
"config" : {
"file" : "&{idm.install.dir}/security/keystore.bcfks",
"storetype" : "BCFKS",
"providerName" : "BCFIPS",
"storePassword" : "changeit",
"mappings" : [
{
"secretId" : "idm.default",
"types" : [
"ENCRYPT",
"DECRYPT"
],
"aliases" : [
"&{openidm.config.crypto.alias|openidm-sym-default}"
]
},
{
"secretId" : "idm.config.encryption",
"types" : [
"ENCRYPT",
"DECRYPT"
],
"aliases" : [
"&{openidm.config.crypto.alias|openidm-sym-default}"
]
},
{
"secretId" : "idm.password.encryption",
"types" : [
"ENCRYPT",
"DECRYPT"
],
"aliases" : [
"&{openidm.config.crypto.alias|openidm-sym-default}"
]
},
{
"secretId" : "idm.assignment.attribute.encryption",
"types" : [
"ENCRYPT",
"DECRYPT"
],
"aliases" : [
"&{openidm.config.crypto.alias|openidm-sym-default}"
]
}
]
}
},
IDM is now configured to start using the Bouncy Castle keystore.
Disable Bouncy Castle FIPS-approved mode
By default, IDM turns on Bouncy Castle in FIPS-approved mode. This makes Bouncy Castle FIPS 140-2 compliant.
IDM sets the configuration in /path/to/openidm/startup.sh
and /path/to/openidm/bin/docker-entrypoint.sh
using the following property:
org.bouncycastle.fips.approved_only=true
To disable FIPS-approved mode, change org.bouncycastle.fips.approved_only
to false
.
In |
These settings must take place early in the IDM start process per Bouncy Castle’s documentation. |
Passwords
IDM provides password management features that help you enforce password policies, limit the number of passwords users must remember, and allow users to reset and change their passwords.
Password policy
A password policy is a set of rules defining what sequence of characters constitutes an acceptable password. Acceptable passwords generally are too complex for users or automated programs to generate or guess.
Password policies set requirements for password length, character sets that passwords must contain, dictionary words and other values that passwords must not contain. Password policies also require that users not reuse old passwords, and that users change their passwords on a regular basis.
IDM enforces password policy rules as part of the general policy service. The default password policy applies the following rules to passwords as they are created and updated:
-
A password property is required for any user object.
-
The value of a password cannot be empty.
-
The password must include at least one capital letter.
-
The password must include at least one number.
-
The minimum length of a password is 8 characters.
-
The password cannot contain the user name, given name, or family name.
You can change these validation requirements, or include additional requirements, by configuring the policy for passwords.
Passwords are validated in several situations:
- Password change and password reset
-
Password change refers to users changing their own passwords. Password reset refers to an administrator setting a user or account password on behalf of a user.
By default, IDM validates password values as they are provisioned.
- Password recovery
-
Password recovery involves recovering a password or setting a new password when the password has been forgotten.
- Password history
-
You can add validation to prevent reuse of previous password values. For more information, refer to Creating a Password History Policy.
- Password expiration
-
You can use workflows to ensure that users are able to change expiring passwords or to reset expired passwords.
Password history policy
The sample described in Store multiple passwords for managed users shows how to set up a password history policy in a scenario where users have multiple different passwords across resources. You can use the scripts provided in that sample to set up a simple password history policy that prevents managed users from setting the same password that they used previously. The default scripts do not evaluate the current password.
To create a password history policy based on the scripts in the multiple passwords sample, make the following changes to your project:
-
Copy the
pwpolicy.js
script from the multiple passwords sample to your project’sscript
directory:cp /path/to/openidm/samples/multiple-passwords/script/pwpolicy.js /path/to/openidm/my-project-dir/script/
The
pwpolicy.js
script contains anis-new
policy definition that compares a new field value with the list of historical values for that field.The
is-new
policy takes ahistoryLength
parameter that specifies the number of historical values on which the policy should be enforced. This number must not exceed thehistorySize
that you set inconf/managed.json
to be passed to theonCreate
andonUpdate
scripts. -
Copy the
onCreate-user-custom.js
andonUpdate-user-custom.js
scripts to your project’sscript
directory:cp samples/multiple-passwords/script/onCreate-user-custom.js /my-project-dir/script/ cp samples/multiple-passwords/script/onUpdate-user-custom.js /my-project-dir/script/
These scripts validate the password history policy when a managed user is created or updated.
-
Update your policy configuration (
conf/policy.json
) to reference the new policy definition by adding the policy script to theadditionalFiles
array:{ "type" : "text/javascript", "file" : "policy.js", "additionalFiles": [ "script/pwpolicy.js" ], ... }
-
Update your project’s
conf/managed.json
file as follows:-
Add a
fieldHistory
property to the managed user object:"fieldHistory" : { "title" : "Field History", "type" : "object", "viewable" : false, "searchable" : false, "userEditable" : false, "scope" : "private" }
The value of this field is a map of field names to a list of historical values for that field. These lists of values are used by the
is-new
policy to determine if a new value has already been used. -
Update the managed user object to call the scripts when a user is created or updated:
"name" : "user", "onCreate" : { "type" : "text/javascript", "file" : "script/onCreate-user-custom.js", "historyFields" : [ "password" ], "historySize" : 4 }, "onUpdate" : { "type" : "text/javascript", "file" : "script/onUpdate-user-custom.js", "historyFields" : [ "password" ], "historySize" : 4 }, ...
If you have any other script logic that is executed on these events, you must update the scripts to include that logic, or add the password history logic to your current scripts. -
Add the
is-new
policy to the list of policies enforced on thepassword
property of a managed user. Specify the number of historical values that the policy should check inhistoryLength
property:"password" : { ... "policies" : [ { "policyId" : "at-least-X-capitals", "params" : { "numCaps" : 1 } }, ... { "policyId" : "is-new", "params" : { "historyLength" : 4 } }, ... ] }
-
You should now be able to test the password history policy by creating a new managed user, and having that user update their password. If the user specifies the same password used within the previous four passwords, the update request is denied with a policy error.
Multiple passwords per linked resource
You can store multiple passwords in a single managed user entry to enable synchronization of different passwords on different external resources.
To store multiple passwords, extend the managed user schema to include additional properties for each target resource. You can set separate policies on each of these new properties, to ensure that the stored passwords adhere to the password policies of the specific external resources.
To use this custom managed object property and its policies to update passwords on an external resource, you must make the corresponding configuration and script changes in your deployment. For a detailed sample that implements multiple passwords, refer to Store multiple passwords for managed users. That sample can also help you set up password history policies.
Random passwords
In certain situations, you might want to generate a random password when users are created.
You can customize your user creation logic to include a randomly generated password that complies with the default password policy. This functionality is included in the default crypto script, bin/defaults/script/crypto.js
, but is not invoked by default. For an example of how this functionality might be used, refer to the openidm/bin/defaults/script/onCreateUser.js
script. The following section of that file (commented out by default) means that users created through the admin UI, or directly over the REST interface, will have a randomly generated password added to their entry:
if (!object.password) {
// generate random password that aligns with policy requirements
object.password = require("crypto").generateRandomString([
{ "rule": "UPPERCASE", "minimum": 1 },
{ "rule": "LOWERCASE", "minimum": 1 },
{ "rule": "INTEGERS", "minimum": 1 },
{ "rule": "SPECIAL", "minimum": 1 }
], 16);
}
The changes made to scripts take effect after the time set in the recompile.minimumInterval , described in Script configuration.
|
The generated password can be encrypted or hashed, in accordance with the managed user schema, defined in conf/managed.json
. For more information, refer to Encoding Attribute Values. Synchronizing hashed passwords is not supported.
You can use this random string generation in a number of situations. Any script handler that is implemented in JavaScript can call the generateRandomString
function.
password
property
To use a property other than the default password
property to store passwords, you must change the following files:
policy.json
-
If you want to enforce password validation rules on a different property, change the
password
property in this file. managed.json
-
Modify the
password
object in this file, which also includes password complexity policies. sync.json
-
If you change the
password
property, make sure that you limit the change to the appropriate system, designated assource
ortarget
. selfservice-reset.json
-
If you are setting up self-service password reset, as described in Password reset, change the value of
identityPasswordField
frompassword
to the desired new property. - Every UI file that includes
password
as a property name -
Whenever there’s a way for a user to enter a password, the associated
HTML
page will include a password entry. For example, theLoginTemplate.html
file includes thepassword
property. A full list of default files with thepassword
property include:-
_passwordFields.html
-
_resetPassword.html
-
ConfirmPasswordDialogTemplate.html
-
EditPasswordPageView.html
-
LoginTemplate.html
-
MandatoryPasswordChangeDialogTemplate.html
-
resetStage-initial.html
-
UserPasswordTab.html
This list does not include any created custom UI files. -
Email rate limiting
No rate limiting is applied to password reset emails, or any emails sent by the IDM server. This means that an attacker can potentially spam a known user account with an infinite number of emails, filling that user’s inbox. In the case of password reset, the spam attack can obscure an actual password reset attempt.
In a production environment, you must configure email rate limiting through the network infrastructure in which IDM runs. Configure the network infrastructure to detect and prevent frequent repeated requests to publicly accessible web pages, such as the password reset page. You can also handle rate limiting within your email server.
Network connections
This topic explains how to secure incoming connections and ports. As a general precaution in production environments, avoid communication over insecure HTTP.
Use TLS/SSL
Use TLS/SSL to access IDM, ideally with mutual authentication so that only trusted systems can invoke each other. TLS/SSL protects data on the network. Mutual authentication with strong certificates, imported into the truststore and keystore of each application, provides a level of confidence for trusting application access.
Restrict REST access to the HTTPS port
In a production environment, you should restrict REST access to a secure port:
-
Edit your project’s
conf/jetty.xml
file:Comment out or delete the
<Call name="addConnector">
code block that includes theopenidm.port.http
property.Do not delete the <Call name="addConnector">
code blocks that contain theopenidm.port.https
andopenidm.port.mutualauth
properties. -
Edit
resolver/boot.properties
:-
Set the
openidm.port.https
port number. -
Set the
openidm.port.mutualauth
port number. -
Add the property
openidm.https.enabled=true
.
-
Use a certificate to secure REST access over HTTPS. You can use self-signed certificates in a test environment. In production, all certificates should be signed by a certificate authority. The examples in this guide assume a CA-signed certificate named ca-cert.pem
.
Protect sensitive REST interface URLs
Anything attached to the router is accessible with the default policy, including the repository. If you do not need such access, deny it in the authorization policy to reduce the attack surface.
In addition, you can deny direct HTTP access to system objects in production, particularly access to action
. As a rule of thumb, do not expose anything that is not used in production.
For an example that shows how to protect sensitive URLs, refer to Configure Access Control in access.json.
Enable HTTP Strict-Transport-Security
HTTP Strict-Transport-Security (HSTS) is a web security policy that forces browsers to make secure HTTPS connections to specified web applications. HSTS can protect websites against passive eavesdropper and active man-in-the-middle attacks.
IDM provides an HSTS configuration, but it is disabled by default. To enable HSTS, locate the following excerpt in your conf/jetty.xml
file:
<New id="tlsHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
...
<Call name="addCustomizer">
<Arg>
<New class="org.eclipse.jetty.server.SecureRequestCustomizer">
<!-- Enable SNI Host Check when true -->
<Arg name="sniHostCheck" type="boolean">true</Arg>
<!-- Enable Strict-Transport-Security header and define max-age when >= 0 seconds -->
<Arg name="stsMaxAgeSeconds" type="long">-1</Arg>
<!-- If enabled, add includeSubDomains to Strict-Transport-Security header when true -->
<Arg name="stsIncludeSubdomains" type="boolean">false</Arg>
</New>
</Arg>
</Call>
...
Set the following arguments:
stsMaxAgeSeconds
-
This parameter sets the length of time, in seconds, that the browser should remember that a site can only be accessed using HTTPS.
For example, the following setting applies the HSTS policy and remains in effect for an hour:
<Arg name="stsMaxAgeSeconds" type="long">3600</Arg>
stsIncludeSubdomains
-
If this parameter is
true
, the HSTS policy is applied to the domain of the issuing host as well as its subdomains:<Arg name="stsIncludeSubdomains" type="boolean">true</Arg>
For more information about HSTS, click here.
Restrict the HTTP payload size
Restricting the size of HTTP payloads can protect the server against large payload HTTP DDoS attacks. IDM includes a servlet filter that limits the size of an incoming HTTP request payload, and returns a 413 Request Entity Too Large
response when the maximum payload size is exceeded.
By default, the maximum payload size is 5MB. You can configure the maximum size in your project’s conf/servletfilter-payload.json
file. That file has the following structure by default:
{
"classPathURLs" : [ ],
"systemProperties" : { },
"requestAttributes" : { },
"scriptExtensions" : { },
"initParams" : {
"maxRequestSizeInMegabytes" : 5
},
"urlPatterns" : [
"/*"
],
"filterClass" : "org.forgerock.openidm.jetty.LargePayloadServletFilter"
}
Change the value of the maxRequestSizeInMegabytes
property to set a different maximum HTTP payload size.
The remaining properties in this file are described in Register Additional Servlet Filters.
Deploy securely behind a load balancer
IDM prevents URL-hijacking, with the following code block in the conf/jetty.xml
file:
<Call name="addCustomizer">
<Arg>
<New class="org.eclipse.jetty.server.SecureRequestCustomizer">
<!-- Enable SNI Host Check when true -->
<Arg name="sniHostCheck" type="boolean">true</Arg>
<!-- Enable Strict-Transport-Security header and define max-age when >= 0 seconds -->
<Arg name="stsMaxAgeSeconds" type="long">-1</Arg>
<!-- If enabled, add includeSubDomains to Strict-Transport-Security header when true -->
<Arg name="stsIncludeSubdomains" type="boolean">false</Arg>
</New>
</Arg>
</Call>
If you are deploying IDM behind a system such as a load balancer, firewall, or a reverse proxy, you must uncomment the next section in jetty.xml
, so that Jetty honors X-Forwarded-Host
headers:
<Call name="addCustomizer">
<Arg>
<New class="org.eclipse.jetty.server.ForwardedRequestCustomizer">
<Set name="forcedHost">
<Call class="org.forgerock.openidm.jetty.Param" name="getProperty">
<Arg>openidm.host</Arg>
</Call>:<Call class="org.forgerock.openidm.jetty.Param" name="getProperty">
<Arg>openidm.port.https</Arg>
</Call>
</Set>
</New>
</Arg>
</Call>
Connect to IDM through a proxy server
-
Add the following JVM parameters to the value of
OPENIDM_OPTS
in your startup script (startup.sh
orstartup.bat
):- -Dhttps.proxyHost
-
Hostname or IP address of the proxy server; for example,
proxy.example.com
or192.168.0.1
. - -Dhttps.proxyPort
-
Port number used by IDM; for example,
8443
or9443
.
Example
# Only set OPENIDM_OPTS if not already set [ -z "$OPENIDM_OPTS" ] && OPENIDM_OPTS="-Xmx1024m -Xms1024m -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8443"
-
Enable the
ForwardedRequestCustomizer
class so that Jetty honorsX-Forwarded-
headers.To enable the class, uncomment the following excerpt in your
conf/jetty.xml
file:<Call name="addCustomizer"> <Arg> <New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"> <Set name="forcedHost"> <Call class="org.forgerock.openidm.jetty.Param" name="getProperty"> <Arg>openidm.host</Arg> </Call>: <Call class="org.forgerock.openidm.jetty.Param" name="getProperty"> <Arg>openidm.port.https</Arg> </Call> </Set> </New> </Arg> </Call>
For more information on this class, refer to the Jetty documentation.
Protect against Cross-Site Request Forgery
IDM provides a filter to protect against Cross-Site Request Forgery (CSRF). The filter is disabled by default. To enable it, set the following property in resolver/boot.properties
:
openidm.csrfFilter.enabled=true
The filter requires the client to send a CSRF cookie (X-CSRF-Token
) on every request. By default, this cookie is the JWT session cookie (session-jwt
), obtained on authentication. If your client uses a different cookie for authentication, set the name of that cookie in the following property in resolver/boot.properties
:
openidm.csrfFilter.authCookieName=session-jwt
If there are HTTP request paths that the CSRF filter should always allow, set these paths as comma-separated values of the openidm.csrfFilter.pathWhitelistCSV
property in resolver/boot.properties
. For example:
openidm.csrfFilter.pathWhitelistCSV=/openidm/example,/openidm/my-project
IDM data
Beyond relying on end-to-end availability of TLS/SSL to protect data, IDM also supports explicit encryption of data that goes on the network. This can be important if the TLS/SSL termination happens prior to the final endpoint.
IDM also supports encryption of data stored in the repository, using the symmetric keys specified in conf/secrets.json
. This protects against some attacks on the data store. Explicit table mapping is supported for encrypted string values.
IDM automatically encrypts sensitive data (such as passwords) in configuration files, and replaces cleartext values when the system first reads the configuration file. Take care with configuration files that contain clear text values that IDM has not yet read and encrypted.
Encode attribute values
There are two ways to encode attribute values for managed objects—reversible encryption and salted hashing algorithms. Attribute values that might be encoded include passwords, authentication questions, credit card numbers, and social security numbers. If passwords are already encoded on the external resource, they are generally excluded from the synchronization process. For more information, refer to Passwords.
You configure attribute value encoding, per schema property, in the managed object configuration. The following sections show how to use reversible encryption and salted hash algorithms to encode attribute values.
Reversible encryption
The following managed object configuration encrypts and decrypts the password
attribute using the default symmetric key:
{
"objects" : [
{
"name" : "user",
...
"schema" : {
...
"properties" : {
...
"password" : {
"title" : "Password",
...
"encryption" : {
"purpose" : "idm.password.encryption"
},
"scope" : "private",
}
...
}
]
}
The settings for reversible encryption depend on the encryption capabilities of the underlying JVM. refer to the explanations in javax.crypto.Cipher. You can accept the default settings, or specify the cipher
and the keySize
, for example:
...
"encryption" : {
"purpose": "idm.password.encryption",
"cipher": "AES/GCM/NoPadding",
"keySize": 128
},
The syntax for the cipher
is algorithm/mode/padding, for example, "cipher" : "AES/CBC/PKCS5Padding"
:
-
The cipher algorithm defines how the plaintext is encrypted and decrypted.
The default algorithm is the Advanced Encryption Standard (AES).
-
The cipher mode defines how a block cipher algorithm transforms data larger than a single block.
The default cipher mode is cipher block chaining (CBC).
-
The cipher padding defines how to pad the plaintext to reach the appropriate size for the algorithm.
The default cipher padding is PKCS#5 padding.
-
The cipher key size determines the encryption strength, where longer key lengths strengthen encryption at the cost of lower performance.
The default
keySize
is16
.
If you change the default cipher, you must specify the algorithm, mode, and padding. If the algorithm does not require a mode, use |
To encrypt attribute values from the command-line, refer to encrypt.
Salted hash algorithms
To encode attribute values with salted hash algorithms, add the secureHash
property to the attribute definition and define the hashing configuration. The configuration depends on the algorithm that you choose.
If you do not specify an algorithm, SHA-256
is used by default. MD5 and SHA-1 are supported for legacy reasons, but should not be used in production environments.
Algorithm | Config Property and Description | ||
---|---|---|---|
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
The following list displays supported hash algorithms and example configurations:
SHA-256
-
"secureHash" : { "algorithm" : "SHA-256", "saltLength" : 16 }
SHA-384
-
"secureHash" : { "algorithm" : "SHA-384", "saltLength" : 16 }
SHA-512
-
"secureHash" : { "algorithm" : "SHA-512", "saltLength" : 16 }
Bcrypt
-
"secureHash" : { "algorithm" : "BCRYPT", "cost" : 16 }
Scrypt
-
"secureHash" : { "algorithm" : "SCRYPT", "hashLength" : 16, "saltLength" : 16, "n" : 32768, "r" : 8, "p" : 1 }
- Password-Based Key Derivation Function 2 (
PBKDF2
) -
"secureHash" : { "algorithm" : "PBKDF2", "hashLength" : 16, "saltLength" : 16, "iterations" : 10, "hmac" : "SHA-256" }
Some one-way hash functions are designed to be computationally expensive. Functions such as PBKDF2, Bcrypt, and Scrypt are designed to be relatively slow even on modern hardware. This makes them generally less susceptible to brute force attacks. However, computationally expensive functions can dramatically increase response times. If you use these functions, be aware of the performance impact and perform extensive testing before deploying your service in production. Do not use functions like PBKDF2 and Bcrypt for any accounts that are used for frequent, short-lived connections. Hashing is a one-way operation, such that the original value cannot be recovered. Therefore, if you hash the value of any property, you cannot synchronize that property value to an external resource. For managed object properties with hashed values, you must either exclude those properties from the mapping or set a random default value if the external resource requires the property. |
The following excerpt of a managed object configuration shows that values of the password
attribute are hashed using the SHA-256
algorithm:
{
"objects" : [
{
"name" : "user",
...
"schema" : {
...
"properties" : {
...
"password" : {
"title" : "Password",
...
"secureHash" : {
"algorithm" : "SHA-256"
},
"scope" : "private",
}
...
}
]
}
To hash attribute values from the command-line, refer to secureHash.
Configure hashing using the admin UI
You can set a property hash algorithm using the admin UI. However, only some algorithms and none of the enhanced configuration options are supported.
Show Me
-
Select Configure > Managed Objects, and select an object type (for example, User).
-
On the Properties tab, select a property to hash.
-
On the Property Name page, click the Privacy & Encryption tab, and select Hashed.
-
From the adjacent drop-down menu, select an algorithm.
-
Click Save.
Encrypted objects
Encrypted objects and properties, such as passwords, include a $crypto
object, that has the following structure:
"password": {
"$crypto": {
"type": "x-simple-encryption",
"value": {
"cipher": "AES/CBC/PKCS5Padding",
"stableId": "openidm-sym-default",
"salt": "Gwi+AGrn+VBOTmyq+TTuuw==",
"data": "+9i7XAXpWZBXYTVEOBkM+w==",
"keySize": 16,
"purpose": "idm.password.encryption",
"iv": "4xtI88eFu5tgfm8ooq+yqQ==",
"mac": "N1zsYo71M/b/G6iLOhNohA=="
}
}
}
Most of the properties in the encrypted object value
are self-explanatory and indicate how the property was encrypted. Specific IDM properties include the following:
-
The
stableId
indicates the key alias that was used to encrypt the property value. -
The
purpose
refers to the secret ID used to encrypt the property value. For more information about secret IDs, refer to Secret stores.
Encrypt and decrypt properties over REST
The openidm.encrypt
and openidm.decrypt
functions of the Resource API enable you to encrypt and decrypt property values. To use these functions over the REST interface, run the ?_action=eval
action on the script
endpoint.
The following example uses the openidm.encrypt
function to encrypt a password value:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Content-Type: application/json" \ --cacert ca-cert.pem \ --request POST \ --data '{ "type": "text/javascript", "globals": { "val": { "myKey": "myPassword" } }, "source":"openidm.encrypt(val,null,\"idm.password.encryption\");" }' \ "https://localhost:8443/openidm/script?_action=eval" { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "qAS/eG7zdnFyK5H8lXvqTA==", "data": "zewf6hR1yjp34EFJqUGpdnzzFCPJs2IaX4V97jdQlSI=", "keySize": 16, "purpose": "idm.password.encryption", "iv": "A4pIiY6kG6t0uLyLmJAoWQ==", "mac": "sFDJqg0Mmp0Ftl+1q1Bjzw==" } } }
The following example uses the openidm.decrypt
function to decrypt the password value:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Content-Type: application/json" \ --cacert ca-cert.pem \ --request POST \ --data '{ "type": "text/javascript", "globals": { "val": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "qAS/eG7zdnFyK5H8lXvqTA==", "data": "zewf6hR1yjp34EFJqUGpdnzzFCPJs2IaX4V97jdQlSI=", "keySize": 16, "purpose": "idm.password.encryption", "iv": "A4pIiY6kG6t0uLyLmJAoWQ==", "mac": "sFDJqg0Mmp0Ftl+1q1Bjzw==" } } } }, "source":"openidm.decrypt(val);" }' \ "https://localhost:8443/openidm/script?_action=eval" { "myKey": "myPassword" }
For more information, refer to openidm.encrypt
and openidm.decrypt
.
Secure the repository
Configuration data and, in most deployments, user data, are stored in the IDM repository. In production deployments, you must secure access to the repository, and encrypt sensitive stored data.
For JDBC repositories, use a strong password for the connection to the repository and change at least the password of the database user (openidm
by default). When you change the database username and/or password, update your database connection configuration file (conf/datasource.jdbc-default.json
).
For a DS repository, change the bindDN
and bindPassword
for the directory server user in the ldapConnectionFactories
property in the repo.ds.json
file.
In both cases, the password is encrypted on server startup, using the key specified in the idm.password.encryption
secret ID in conf/secrets.json
.
Sensitive files and directories
Protect IDM files from access by unauthorized users. In particular, prevent other users from reading files in at least the openidm/resolver/
and openidm/security/
directories.
The objective is to limit access to the user that is running the service. Depending on the operating system and configuration, that user might be root
, Administrator
, openidm
, or something similar.
Protect sensitive files in Unix
-
Make sure that user and group ownership of the installation and project directories is limited to the user running the IDM service.
-
Disable access of any sort for
other
users. One simple command for that purpose, from the/path/to/openidm
directory, is:chmod -R o-rwx .
Protect sensitive files in Windows
The IDM process in Windows is typically run by the Local System
service account.
If you are concerned about the security of this account, you can set up a service account that only has permissions for IDM-related directories, then remove User access to the directories noted above. You should also configure the service account to deny local and remote login. For more information, refer to the User Rights Assignment article in Microsoft’s documentation.
Remove or protect development and debug tools
Before you deploy IDM in production, remove or protect development and debug tools, including the Felix web console that is exposed under /system/console
. Authentication for this console is not integrated with authentication for IDM.
Remove the Felix web console
-
Remove the web console bundle and all related plugin bundles:
rm /path/to/openidm/bundle/org.apache.felix.webconsole*.jar rm /path/to/openidm/bundle/openidm-felix-webconsole-7.3.1.jar
-
Remove the
felix.webconsole.json
configuration file from your project’sconf/
directory:rm /path/to/project-dir/conf/felix.webconsole.json
Protect the Felix web console
To protect access to the Felix web console, change the credentials in your project’s conf/felix.webconsole.json
file. These properties can be set using property substitution. This file contains the username and password to access the console, by default:
{
"username" : "&{openidm.felix.webconsole.username|admin}",
"password" : "&{openidm.felix.webconsole.password|admin}"
}
Adjust log levels
In production, set log levels to INFO
to ensure that you capture enough information to help diagnose issues, but do not expose unnecessary information. For more information, refer to Server logs.
At startup and shutdown, INFO
can produce many messages. During stable operation, INFO
generally results in log messages only when coarse-grain operations such as scheduled reconciliation start or stop.
The default IDM log formatter encodes all control characters (such as newline characters) using URL-encoding, to protect against log forgery. For more information, refer to Server logs. |
Secure the API Explorer
The REST API Explorer serves up interactive REST API documentation. The API Explorer can help you identify endpoints, and run REST calls against those endpoints. To protect production servers from unauthorized API descriptor requests, IDM requires authentication, by default. The property authEnabled
protects static web resources from public view.
ui.context-api.json
file{
"enabled" : true,
"authEnabled" : true,
"urlContextRoot" : "/api",
"defaultDir" : "&{idm.install.dir}/ui/api/default",
"extensionDir" : "&{idm.install.dir}/ui/api/extension"
}
To disable the API Explorer, set the following property in your resolver/boot.properties
file:
openidm.apidescriptor.enabled=false
Hide unused REST endpoints
The two main use cases for IDM are data synchronization and user self-service.
If you are using IDM only to synchronize data sources, do not expose the server externally. In this case, IDM initiates all connections.
If you are using IDM only for user self-service, ensure that the server is behind a firewall or proxy, such as ForgeRock Identity Gateway. At a minimum, hide the /admin
endpoint in the web interface via the proxy. Use the conf/access.json file as a guide for proxy or firewall rules.
If you are using IDM for data synchronization and user self-service, it is preferable to run two IDM servers or clusters, each with its own security model. Because the two use cases have very different load characteristics and security implications, running them on separate servers can help to prevent synchronization activity from impacting the performance on end-user systems.
Disable automatic configuration updates
By default, IDM monitors files in the conf
directory periodically for any changes to the configuration. This functionality is configured in the following properties in your conf/system.properties
file:
openidm.fileinstall.poll
-
Sets the interval at which files are polled for changes.
2000
milliseconds by default. openidm.fileinstall.enabled
-
Specifies whether files should be monitored.
true
by default. In a production system, you should disable automatic polling for updates to prevent untested configuration changes from disrupting your identity service. Setting this property tofalse
also disables the file-based configuration view, which means that IDM reads its configuration only from the repository.If automatic polling is enabled, IDM immediately uses changes to scripts called from a JSON configuration file.
openidm.config.bootstrap.enabled
-
Allows IDM to start up and load configuration when there aren’t currently any configs stored in the repository.
true
by default. openidm.fileinstall.filter
-
Specify which file types should be monitored (if
openidm.fileinstall.enabled=true
). File types are specified in a pipe-separated list, for example:openidm.fileinstall.filter=.*\\.cfg|.*\\.json
Secure IDM server files with a read-only installation
One method of locking down the server is to install IDM on a read-only file system. To accomplish this, complete all procedures in this topic.
This topic assumes that you have prepared the read-only volume appropriate for your Linux/UNIX installation environment and that you have set up a regular Linux user named idm
and a dedicated volume for the /idm
directory.
Prep
-
Configure the dedicated volume device,
/dev/volume
in the/etc/fstab
file, as follows:/dev/volume/idm ext4 ro,defaults 1,2
When you run the
mount -a
command, the/dev/volume
volume device is mounted on the/idm
directory. -
You can switch between read-write and read-only mode for the
/idm
volume with the following commands:sudo mount -o remount,rw /idm sudo mount -o remount,ro /idm
-
Confirm the result with the
mount
command, which should show that the/idm
volume is mounted in read-only mode:/dev/volumeon /idm type ext4 (ro)
-
Set up the
/idm
volume in read-write mode:sudo mount -o remount,rw /idm
-
With the following commands, you can unpack the IDM binary in the
/idm
directory, and give useridm
ownership of all files in that directory:sudo unzip /idm/IDM-7.3.1.zip sudo chown -R idm.idm /idm
Redirect audit and logging data
After you have installed IDM on a read-only file system, redirect audit and logging data to writable volumes. This procedure assumes a user idm
with Linux administrative (superuser) privileges.
-
Create an external directory where IDM can send logging, auditing, and internal repository information:
sudo mkdir -p /var/log/openidm/audit sudo mkdir /var/log/openidm/logs sudo mkdir -p /var/cache/openidm/felix-cache sudo mkdir /var/run/openidm
Alternatively, route audit data to a remote data store. For an example of how to send audit data to a MySQL repository, refer to Direct audit information to MySQL.
-
Give the
idm
user ownership of the newly created directories:sudo chown -R idm.idm /var/log/openidm sudo chown -R idm.idm /var/cache/openidm sudo chown -R idm.idm /var/run/openidm
-
Modify the following configuration files:
- conf/audit.json
-
Make sure the
handlerForQueries
is the JSON audit event handler and change thelogDirectory
property to the/var/log/openidm/audit
subdirectory:"eventHandlers" : [ { "class" : "org.forgerock.audit.handlers.json.JsonAuditEventHandler", "config" : { "name" : "json", "logDirectory" : "/var/log/openidm/audit", ... }, ... } ]
- conf/logging.properties
-
Change the
java.util.logging.FileHandler.pattern
property as follows:java.util.logging.FileHandler.pattern = /var/log/openidm/logs/openidm%u.log
- conf/config.properties
-
Activate and redirect the
org.osgi.framework.storage
property as follows:# If this value is not absolute, then the felix.cache.rootdir controls # how the absolute location is calculated. (See buildNext property) org.osgi.framework.storage=&{felix.cache.rootdir|&{user.dir}}/felix-cache # The following property is used to convert a relative bundle cache # location into an absolute one by specifying the root to prepend to # the relative cache path. The default for this property is the # current working directory. felix.cache.rootdir=/var/cache/openidm
Your setup may require additional redirection for the following:
|
Finishing touches
-
Adjust the value of the
OPENIDM_PID_FILE
in thestartup.sh
andshutdown.sh
scripts. To do so for a default bash shell, set the value ofOPENIDM_PID_FILE
for useridm
by adding the following line to/home/idm/.bashrc
:export OPENIDM_PID_FILE=/var/run/openidm/openidm.pid
For other shells, adjust your changes accordingly. When you log in again as user
idm
, yourOPENIDM_PID_FILE
variable should redirect the process identifier file,openidm.pid
to the/var/run/openidm
directory, ready for access by theshutdown.sh
script. -
While the volume is still mounted in read-write mode, start IDM normally:
path/to/openidm/startup.sh -p project-dir
The first startup of IDM either processes the signed certificate that you added, or generates a self-signed certificate, and encrypts any passwords in the various configuration files.
-
Stop IDM.
-
You can now mount the
/idm
directory in read-only mode. The configuration in/etc/fstab
ensures that Linux mounts the/idm
directory in read-only mode on next system boot.sudo mount -o remount,ro /idm
-
Reboot the system.
-
You can now start IDM, configured on a secure read-only volume.
path/to/openidm/startup.sh -p project-dir