After following the guidance in this chapter, make sure that you test your installation to verify that it behaves as expected before putting it into production.
Out of the box, OpenIDM is set up for ease of development and deployment. When deploying OpenIDM in production, take the following precautions.
Disable plain HTTP access, included for development convenience, as described in the section titled Secure Jetty.
Use TLS/SSL to access OpenIDM, ideally with mutual authentication so that only trusted systems can invoke each other. TLS/SSL protects data on the wire. Mutual authentication with certificates imported into the applications' trust and key stores provides some confidence for trusting application access.
Augment this protection with message level security where appropriate.
Use certificates to secure REST access, over HTTPS. The following procedure shows how to generate a self-signed certificate to secure REST calls, over HTTPS. Note that in production systems, it is recommended that you use a key that has been signed by a certificate authority.
Extract the certificate that is installed with OpenIDM.
$ openssl s_client -showcerts -connect localhost:8443 </dev/null
This command outputs the entire certificate to the terminal.
Using any text editor, create a file named server.crt.
Copy the portion of the certificate from BEGIN CERTIFICATE
to END CERTIFICATE and paste it into the
server.crt file. Your server.crt
file should now contain something like the following:
$ more server.crt
-----BEGIN CERTIFICATE-----
MIIB8zCCAVygAwIBAgIETkvDjjANBgkqhkiG9w0BAQUFADA+MSgwJgYDVQQKEx9P
cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
c3QwHhcNMTEwODE3MTMzNTEwWhcNMjEwODE3MTMzNTEwWjA+MSgwJgYDVQQKEx9P
cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKwMkyvHS5yHAnI7+tXUIbfI
nQfhcTChpWNPTHc/cli/+Ta1InTpN8vRScPoBG0BjCaIKnVVl2zZ5ya74UKgwAVe
oJQ0xDZvIyeC9PlvGoqsdtH/Ihi+T+zzZ14oVxn74qWoxZcvkG6rWEOd42QzpVhg
wMBzX98slxkOZhG9IdRxAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEASo4qMI0axEKZ
m0jU4yJejLBHydWoZVZ8fKcHVlD/rTirtVgWsVgvdr3yUr0Idk1rH1nEF47Tzn+V
UCq7qJZ75HnIIeVrZqmfTx8169paAKAaNF/KRhTE6ZII8+awst02L86shSSWqWz3
s5xPB2YTaZHWWdzrPVv90gL8JL/N7/Q=
-----END CERTIFICATE-----
Generate a private, self-signed key as follows:
Generate an encrypted 1024-bit RSA key, and save it
to a file named localhost.key. Enter
a pass phrase for the key as requested.
$ openssl genrsa -des3 -out localhost.key 1024 Generating RSA private key, 1024 bit long modulus .........++++++ .........................++++++ e is 65537 (0x10001) Enter pass phrase for localhost.key: Verifying - Enter pass phrase for localhost.key:
Generate a certificate request using the key you
created in the previous step, and save it to a file named
localhost.csr. Enter any required
information to create the DN for the request.
$ openssl req -new -key localhost.key -out localhost.csr
This step creates a file, localhost.csr,
that contains the details of the certificate request.
Sign the certificate with the key you created in the
previous step, and generate a certificate that is valid
for one year in a file named localhost.crt.
The x509 subcommand enables you to
retrieve the information that is stored in the SSL certificate.
Output will depend on the details that you entered in the
certificate request.
$ openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt
Signature ok
subject=/C=FR/ST=Il-DE-FRANCE/L=Paris/O=example.com
Getting Private key
Enter pass phrase for localhost.key:
The contents of localhost.crt should
now be something like this:
$ more localhost.crt
-----BEGIN CERTIFICATE-----
MIIB/zCCAWgCCQD6VdiF6rX2czANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJa
QTELMAkGA1UECBMCV0MxEjAQBgNVBAcTCUNhcGUgVG93bjEUMBIGA1UEChMLZXhh
bXBsZS5jb20wHhcNMTMwMTI1MTIzNzIyWhcNMTQwMTI1MTIzNzIyWjBEMQswCQYD
VQQGEwJaQTELMAkGA1UECBMCV0MxEjAQBgNVBAcTCUNhcGUgVG93bjEUMBIGA1UE
ChMLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAONLO82s
wKA0tWkbR66DajwQKNO9QlYwZvcK4X7MFOcwex+8j2vvG5HCB0BW2Gm72mFTWei8
gVgQDP1oe/yTWDZRaiJ8rGWdvpgH1Cmxcd3N1AhhRya1I2j5wxrc9ZsyyTYCg2fd
pFfULrUXSd9QlB2qQZz7kb4ksT/mSwPiGqvFAgMBAAEwDQYJKoZIhvcNAQEFBQAD
gYEA3WrP8NKjXwQzE0vabYmdUhPHt3PF8EMMwVJ+h8G9Dwmtll0P/kLybXdHF1P/
SvN8ofdaEKi4DrLvBifkJvHdTm9DgZJo+bROM6LM9kac6CxNvwj9m/4g6mhnjxEV
63WQPzvAeriO51JC0ysMVe5vf+lO0t+J8W6SfPTKwoXDQhY=
-----END CERTIFICATE-----
Combine the contents of server.crt and
localhost.crt to create a Privacy Enhanced
Mail Certificate (.pem) file named
CA.pem.
$ cat server.crt localhost.crt > CA.pem
The contents of CA.pem should be
something like the following (a concatenation of
server.crt and localhost.crt).
$ more CA.pem
-----BEGIN CERTIFICATE-----
MIIB8zCCAVygAwIBAgIETkvDjjANBgkqhkiG9w0BAQUFADA+MSgwJgYDVQQKEx9P
cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
c3QwHhcNMTEwODE3MTMzNTEwWhcNMjEwODE3MTMzNTEwWjA+MSgwJgYDVQQKEx9P
cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKwMkyvHS5yHAnI7+tXUIbfI
nQfhcTChpWNPTHc/cli/+Ta1InTpN8vRScPoBG0BjCaIKnVVl2zZ5ya74UKgwAVe
oJQ0xDZvIyeC9PlvGoqsdtH/Ihi+T+zzZ14oVxn74qWoxZcvkG6rWEOd42QzpVhg
wMBzX98slxkOZhG9IdRxAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEASo4qMI0axEKZ
m0jU4yJejLBHydWoZVZ8fKcHVlD/rTirtVgWsVgvdr3yUr0Idk1rH1nEF47Tzn+V
UCq7qJZ75HnIIeVrZqmfTx8169paAKAaNF/KRhTE6ZII8+awst02L86shSSWqWz3
s5xPB2YTaZHWWdzrPVv90gL8JL/N7/Q=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB/zCCAWgCCQD6VdiF6rX2czANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJa
QTELMAkGA1UECBMCV0MxEjAQBgNVBAcTCUNhcGUgVG93bjEUMBIGA1UEChMLZXhh
bXBsZS5jb20wHhcNMTMwMTI1MTIzNzIyWhcNMTQwMTI1MTIzNzIyWjBEMQswCQYD
VQQGEwJaQTELMAkGA1UECBMCV0MxEjAQBgNVBAcTCUNhcGUgVG93bjEUMBIGA1UE
ChMLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAONLO82s
wKA0tWkbR66DajwQKNO9QlYwZvcK4X7MFOcwex+8j2vvG5HCB0BW2Gm72mFTWei8
gVgQDP1oe/yTWDZRaiJ8rGWdvpgH1Cmxcd3N1AhhRya1I2j5wxrc9ZsyyTYCg2fd
pFfULrUXSd9QlB2qQZz7kb4ksT/mSwPiGqvFAgMBAAEwDQYJKoZIhvcNAQEFBQAD
gYEA3WrP8NKjXwQzE0vabYmdUhPHt3PF8EMMwVJ+h8G9Dwmtll0P/kLybXdHF1P/
SvN8ofdaEKi4DrLvBifkJvHdTm9DgZJo+bROM6LM9kac6CxNvwj9m/4g6mhnjxEV
63WQPzvAeriO51JC0ysMVe5vf+lO0t+J8W6SfPTKwoXDQhY=
-----END CERTIFICATE-----
Test REST access on the HTTPS port, using the certificate that you created in the previous step. For example:
$ curl
--header "X-OpenIDM-Username:openidm-admin"
--header "X-OpenIDM-Password:openidm-admin"
--cacert CA.pem
--request GET
"https://localhost:8443/openidm/managed/user/?_queryId=query-all-ids"
{
"conversion-time-ms": 0,
"result": [
{
"_rev": "0",
"_id": "8afd44a7-13be-449e-9c47-7a310e675c00"
}
],
"query-time-ms": 1
}
If you receive the response
curl: (52) Empty reply from server, check
that you have, in fact, used https and not
http in the URL.
Beyond relying on end-to-end availability of TLS/SSL to protect data, OpenIDM also supports explicit encryption of data that goes on the wire. This can be important if the TLS/SSL termination happens prior to the final end point.
OpenIDM also supports encryption of data stored in the repository, using a symmetric key. This protects against some attacks on the data store. Explicit table mapping is supported for encrypted string values.
OpenIDM automatically encrypts sensitive data in configuration files, such as passwords. OpenIDM replaces clear text values when the system first reads the configuration file. Take care with configuration files having clear text values that OpenIDM has not yet read and updated.
OpenIDM supports message level security, forcing authentication before granting access. Authentication works by means of a filter-based mechanism that lets you use either an HTTP Basic like mechanism or OpenIDM-specific headers, setting a cookie in the response that you can use for subsequent authentication. If you attempt to access OpenIDM URLs without the appropriate headers or session cookie, OpenIDM returns HTTP 401 Unauthorized, or HTTP 403 Forbidden, depending on the situation. If you use a session cookie, you must include an additional header that indicates the origin of the request.
The following examples show successful authentications.
$ curl
--dump-header /dev/stdout
--user openidm-admin:openidm-admin
"http://localhost:8080/openidm/managed/user/?_queryId=query-all-ids"
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=2l0zobpuk6st1b2m7gvhg5zas;Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json; charset=UTF-8
Date: Wed, 18 Jan 2012 10:36:19 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.0.9
Transfer-Encoding: chunked
{"query-time-ms":1,"result":[{"_id":"ajensen"},{"_id":"bjensen"}]}
$ curl
--dump-header /dev/stdout
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
"http://localhost:8080/openidm/managed/user/?_queryId=query-all-ids"
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=ixnekr105coj11ji67xcluux8;Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json; charset=UTF-8
Date: Wed, 18 Jan 2012 10:36:40 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.0.9
Transfer-Encoding: chunked
{"query-time-ms":0,"result":[{"_id":"ajensen"},{"_id":"bjensen"}]}
$ curl
--dump-header /dev/stdout
--header "Cookie: JSESSIONID=ixnekr105coj11ji67xcluux8"
--header "X-Requested-With: OpenIDM Plugin"
"http://localhost:8080/openidm/managed/user/?_queryId=query-all-ids"
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Wed, 18 Jan 2012 10:37:20 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.0.9
Transfer-Encoding: chunked
{"query-time-ms":1,"result":[{"_id":"ajensen"},{"_id":"bjensen"}]}Notice that the last example uses the cookie OpenIDM set in the
response to the previous request, and includes the
X-Requested-With header to indicate the origin of the
request. The value of the header can be any string, but should be informative
for logging purposes. If you do not include the
X-Requested-With header, OpenIDM returns HTTP 403
Forbidden.
You can also request one-time authentication without a session.
$ curl
--dump-header /dev/stdout
--header "X-OpenIDM-NoSession: true"
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
"http://localhost:8080/openidm/managed/user/?_queryId=query-all-ids"
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Wed, 18 Jan 2012 10:52:27 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.0.9
Transfer-Encoding: chunked
{"query-time-ms":1,"result":[{"_id":"ajensen"},{"_id":"bjensen"}]}To log out and destroy the session, send the specific OpenIDM header.
$ curl --dump-header /dev/stdout --header "Cookie: JSESSIONID=ixnekr105coj11ji67xcluux8" --header "X-Requested-With: OpenIDM Plugin" --header "X-OpenIDM-Logout: true" "http://localhost:8080/openidm/" HTTP/1.1 204 No Content
OpenIDM creates the openidm-admin user with password
openidm-admin by default. This internal user is stored in
OpenIDM's repository.
mysql> select objectid,roles from internaluser; +---------------+----------------------------------+ | objectid | roles | +---------------+----------------------------------+ | anonymous | openidm-reg | | openidm-admin | openidm-admin,openidm-authorized | +---------------+----------------------------------+ 2 rows in set (0.00 sec)
OpenIDM uses the internal table for authentication, and also to set
the roles for RBAC authorization of an authenticated user. The router
service, described in the Router
Service Reference appendix, enables you to apply filters
as shown in openidm/conf/router.json and the associated
script, openidm/script/router-authz.js. See the chapter
on Managing Authentication,
Authorization & RBAC for details.
The default security settings are adequate for evaluation purposes. For production, change the default encryption key, and then replace the default user password.
By default, OpenIDM uses an symmetric encryption key with alias
openidm-sym-default. Change this default key before
deploying OpenIDM in production.
Add the new key to the key store.
$ cd /path/to/openidm/ $ keytool -genseckey -alias new-sym-key -keyalg AES -keysize 128 -keystore security/keystore.jceks -storetype JCEKS Enter keystore password: Enter key password for <new-sym-key> (RETURN if same as keystore password): Re-enter new password: $
Also see
openidm/samples/security/keystore_readme.txt.
Change the alias used in
openidm/conf/boot/boot.properties.
After changing the default encryption key, change at least the default user password.
Use the encrypt command to obtain the encrypted version of the new password.
$ cd /path/to/openidm/
$ cli.sh encrypt newpwd
...
-----BEGIN ENCRYPTED VALUE-----
{
"$crypto" : {
"value" : {
"iv" : "TCoC/YrmiRmINw6jCPB5LQ==",
"data" : "nCFvBIApIQ7C6k+UPzosaA==",
"cipher" : "AES/CBC/PKCS5Padding",
"key" : "openidm-sym-default"
},
"type" : "x-simple-encryption"
}
}
------END ENCRYPTED VALUE------Replace the user object in the
openidm/db/scripts/mysql/openidm.sql script before
setting up MySQL as a repository for OpenIDM.
Alternatively, replace the user in the internal user table.
Before running OpenIDM in production, edit the
openidm/conf/jetty.xml configuration to avoid
clear text HTTP. Opt instead for HTTPS, either with or without mutual
authentication. To disable plain HTTP access, comment out the section in
openidm/conf/jetty.xml that enables HTTP on port
8080.
<!--
<Item>
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port">8080</Set>
<Set name="maxIdleTime">300000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
<Set name="lowResourcesConnections">20000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set>
</New>
</Item>
-->Although the repository is accessible directly by default, since anything attached to the router is accessible with the default policy, avoid direct HTTP access in production. If you do not need such access, deny it in the authorization policy to reduce the attack surface.
Similarly 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. The main public interfaces
over HTTP are /openidm/managed/ and /openidm/config/.
Other URIs are triggered indirectly, or are for internal consumption.
OpenIDM supports native query expressions on the JDBC repository and it is possible to enable these over HTTP, for example:
$curl --header "X-OpenIDM-Username: openidm-admin" --header "X-OpenIDM-Password: openidm-admin" "http://localhost:8080/openidm/managed/user?_queryExpression=select+*+from+managedobjects"
By default, direct HTTP access to native queries is disallowed, and
should remain so in production systems. To enable native queries on the JDBC
repository over HTTP, specifically for testing or development purposes,
remove the custom authorization call from the router authorization script
(openidm/script/router-authz.js).
"customAuthz" : "disallowQueryExpression()"
Remember to remove the comma at the end of the preceding line as well.
See the chapter on Managing Authentication, Authorization & RBAC for an example showing how to protect sensitive URLs.
Protect OpenIDM files from access by unauthorized users.
In particular, prevent other users from reading files in at least the
openidm/conf/boot/ and
openidm/security/ directories.
OpenIDM uses the information in
conf/boot/boot.properties, including the key store
password, to start up. You can set an obfuscated version in the file, or
prompt for the password at start up time.
To use an obfuscated password, follow these steps:
Generate an obfuscated version of the password, by using the crypto bundle provided with OpenIDM:
$ java -jar /path/to/openidm/bundle/openidm-crypto-2.1.0-SNAPSHOT.jar This utility helps obfuscate passwords to prevent casual observation. It is not securely encrypted and needs further measures to prevent disclosure. Please enter the password: OBF:1vn21ugu1saj1v9i1v941sar1ugw1vo0 CRYPT:a8b5a01ba48a306f300b62a1541734c7
Paste the obfuscated password into the
conf/boot/boot.properties file. Comment out
the regular keystore password and remove the comment tag from the
line that contains the obfuscated password:
$ more conf/boot/boot.properties
...
# Keystore password, adjust to match your keystore and protect this file
# openidm.keystore.password=changeit
openidm.truststore.password=changeit
# optionally use the cli encrypt to obfuscate the password and set
openidm.keystore.password=OBF:1vn21ugu1saj1v9i1v941sar1ugw1vo0
#openidm.keystore.password=CRYPT:
...Restart OpenIDM.
$ ./startup.sh
Before deploying OpenIDM in production, remove or protect development
and debug tools, including the OSGi console exposed under
/system/console. Authentication for this console is not
integrated with authentication for OpenIDM.
To remove the OSGi console, remove the web console bundle,
org.apache.felix.webconsole-.version.jar
If you cannot remove the OSGi console, then protect it by overriding
the default admin:admin credentials. Create a file called
openidm/conf/org.apache.felix.webconsole.internal.servlet.OsgiManager.cfg
containing the user name and password to access the console in Java
properties file format.
username=user-name password=password
Use the JDBC repository. OrientDB is not yet supported for production use.
Use a strong password for the JDBC connection. Do not rely on default passwords.
Use a case sensitive database, particularly if you work with systems with different identifiers that match except for case. Otherwise correlation queries can pick up identifiers that should not be considered the same.
Leave log levels at INFO in production to ensure
that you capture enough information to help diagnose issues. See the chapter
on Configuring Server
Logs for more information.
At start up and shut down, INFO can produce many
messages. Yet, during stable operation, INFO generally
results in log messages only when coarse-grain operations such as
scheduled reconciliation start or stop.
You can run OpenIDM in the background as a service (daemon), and add startup and shutdown scripts to manage the service at system boot and shutdown. For more information, see Starting and Stopping OpenIDM.
See your operating system documentation for details on adding a service such as OpenIDM to be started at boot and shut down at system shutdown.