chap-security.xml revision 7272f624fb68a0d6818875a6d8584dd609f4f1c0
<?xml version="1.0" encoding="UTF-8"?>
<!--
! CCPL HEADER START
!
! This work is licensed under the Creative Commons
! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
! To view a copy of this license, visit
! or send a letter to Creative Commons, 444 Castro Street,
! Suite 900, Mountain View, California, 94041, USA.
!
! You can also obtain a copy of the license at
! See the License for the specific language governing permissions
! and limitations under the License.
!
! If applicable, add the following below this CCPL HEADER, with the fields
! enclosed by brackets "[]" replaced with your own identifying information:
! Portions Copyright [yyyy] [name of copyright owner]
!
! CCPL HEADER END
!
! Copyright 2011-2014 ForgeRock AS
!
-->
<chapter xml:id='chap-security'
xmlns='http://docbook.org/ns/docbook'
version='5.0' xml:lang='en'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://docbook.org/ns/docbook
xmlns:xlink='http://www.w3.org/1999/xlink'>
<title>Securing & Hardening OpenIDM</title>
<indexterm>
<primary>Best practices</primary>
</indexterm>
<indexterm>
<primary>Security</primary>
</indexterm>
<para>
OpenIDM provides a security management service, that manages keystore and
truststore files. The security service is accessible over the REST interface,
enabling you to read and import SSL certificates, and to generate certificate
signing requests.
</para>
<para>
This chapter describes the security management service and its REST interface.
</para>
<para>
In addition, the chapter outlines the specific security procedures that you
should follow before deploying OpenIDM in a production environment.
</para>
<note>
<para>
In a production environment, we recommend that you avoid the use of:
communications over insecure HTTP, self-signed certificates, and
certificates associated with insecure ciphers.
</para>
</note>
<section xml:id="security-management-service">
<title>Accessing the Security Management Service</title>
<para>
OpenIDM stores keystore and truststore files in a folder named
using the <command>keytool</command> command, or over the REST interface, at
information about using the <command>keytool</command> command, see
</para>
<para>
The following sections describe how to manage certificates and keys over REST.
</para>
<section xml:id="display-keystore-over-rest">
<title>Displaying the Contents of the Keystore</title>
<para>
OpenIDM generates a symmetric key and a private key the first time the
server is started. After startup, display the contents of the keystore over
REST, as follows:
</para>
<screen>
$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/security/keystore"</userinput>
<computeroutput>
{
"type" : "JCEKS",
"provider" : {
"Cipher.Blowfish SupportedKeyFormats" : "RAW",
...
},
"aliases" : [ "openidm-sym-default", "openidm-localhost" ]
}</computeroutput>
</screen>
<itemizedlist>
<para>
By default, OpenIDM includes the following aliases:
</para>
<listitem>
<para>
<literal>openidm-sym-default</literal> - the default symmetric key that is
used, for example, to encrypt the configuration.
</para>
</listitem>
<listitem>
<para>
<literal>openidm-localhost</literal> - the default alias that is used by
the Jetty web server to service SSL requests. This alias references a
private key and a self-signed certificate. You can use the self-signed
certificate for testing purposes. When you deploy OpenIDM in a production
environment, you should replace the self-signed certificate with a
certificate that has been signed by a certificate authority.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="csr-over-rest">
<title>Generating a Certificate Signing Request Over REST</title>
<para>
To request a signed certificate, generate a certificate signing request
(CSR) over REST, as described in this section. The details of the CSR are
specified in JSON format, for example:
</para>
<programlisting language="javascript">{
"DN" : "www.example.com",
"OU" : "HR",
"L" : "Cupertino",
"C" : "US"
} </programlisting>
<para>
For information about the complete contents of a CSR, see
</para>
<para>
To generate a CSR over the REST interface, include the private key alias in
the URL. The following example, uses the default alias
(<literal>openidm-localhost</literal>). If you have created your own private
key for this request, specify its alias instead of
<literal>openidm-localhost</literal>. Set <literal>"returnPrivateKey" : true</literal>
to return the private key along with the request.
</para>
<screen>
$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{"CN" : "www.example.com",
"OU" : "HR",
"L" : "Cupertino",
"C" : "US",
"returnPrivateKey" : true,
"alias" : "openidm-localhost"}' \
<computeroutput>
{
"_id": "openidm-localhost",
"csr": "-----BEGIN CERTIFICATE REQUEST-----\n
MIICmzCCAYMCAQAwWDEZMBcGA1UEAwwQd3d3MS5
leGFtcGxlLmNvbTELMAkGA1UE\nCwwCSFIxDTALBgNVBAoMBE5vbmUxEjAQBgNVBAcMCUN1cGVyd
GlubzELMAkGA1UE\nBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAjCjTt1b
aRCE1c0PuXiIlrO7wsF4dFTtTZKAhrpFdM+0hU4LeyCDxQQ5UDga3rmyVIvC8\nL1PvW+sZEcZ9r
T67XOV03cwUpjvG4W58FCUKd6UAI0szfIrFdvJp4q4LkkBNkk9J\nUf+MXsSVuHzZrqvqhX900Is
a19mXD6/P9Cql8KmwEzzbglGFf6uYAK33F71Kx409\nTeS85sjmBbyJwUVwhgQ0R35H3HC6jex4P
jx1rSfPmsi61JBx9kyGu6rnSv5FOQGy\nBQpgQFnJAgMBAAEwDQYJKoZIhvcNAQENBQADggEBAKc
F8HHkinqc6m\nBWhIcf5Omk6fSqG0aw7fqn20XWDkRm+I4vtm8P8CuWftUj5qv5kmyUtrcQ3+YPD
O\nL+cK4cfuCkjLQ3h4GIgBJP+gfWX8fTmCHyaHEFjLTMj1hZYEx+3f8awOVFoNmr3/\nB8LIJNH
F0x5XoudQrSg7lpVslXBHNeiM85+H08RMQh8Am2bp+Xstw=\n",
-----END CERTIFICATE REQUEST-----\n",
"publicKey": {
"format": "X.509",
"encoded": "-----BEGIN PUBLIC KEY-----\n
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr
ALtYU662bNbQZG7JZ3M\noOUmVP9cPP3+DhQ5H0V0qB+9YjE4XUtuwUGqaUmuT+mrXHwGpLAqvUm
94Ro+VUAF9Q6nk2j0PgTi+QZ0i93jbKAWWX57w6S5i7CpEptKyeP9iG\ncFnJddSICPHkbQJ73gu
lyZYkbcBblNUxIhODZV5bJ0oxn9qgYvzlxJupldYsYkBo\ncwIDAQAB\n
-----END PUBLIC KEY-----\n",
"algorithm": "RSA"
},
"privateKey": {
"format": "PKCS#8",
"encoded": "-----BEGIN RSA PRIVATE KEY-----\n
MIIEpAIBAAKCAQEArALtYU662bNbQZG7JZ3MoOU
VP9cPP3+DhQ5H0V0qB+9YjE4\nXUtuwUGqaUmuT+mrXHwGpLAqvUmNsVyXJj9sJhX6PCyXzO3RdK
Z0i93jbKAWWX57w6S5i7CpEptKyeP9iGcFnJddSICPHkbQJ73gulyZYkbcBb\nlNUxIhODZV5bJ0
Z6F4\nYyG7M0g7TE54dhqPvGNS9cO4r0Vom9373MDh+8QSfmLCC94Ro+VUAF9Q6nk2j0Pg\nTi+Q
oxn9qgYvzlxJupldYsYkBocwIDAQABAoIBAGmfpopRIPWbaBb8\nWNIBcuz9qSsaX1ZolP+qNWVZ
GugURgNBCO\ncmYqCDZLvpMy4S/qoRBu+6qdYGprb+tHshBYNywuDkrgszhwgr5yRm8VQ60T9tM/
\nceKM+TECgYEA2Az2DkpC9TjJHPJG7x4boRRVqV5YRgPf5MrU+7PxDMb+EauXXUXg\nsch9Eeon
30yINqSv6FwATLVlkzQpZLkkJ6GJqAxUmPjRslAuosiSJqKaWamDUDbz\nSu/7iANJWvRGayqZsa
GQqFwM0Xpfp/EiBGe757k0D02u8sAv94A75bsCgYEAy9FQ\nMwDU3CaDzgv0qgR1ojXkSW0dCbv0
X54Rm+qabT2fQ2lFJ/myWKsPgJ\n4gZ9bUvcemCcLLzsiAphueulQp49eOLnkzPlQKkCgYEAy7A0
Ypvsb6EO+BYDXXQzVvwx8CgYB9o67LcfTFLNzNFCOi9pLJBm2OMbvXt0wPCFch\nbCG34hdfMntU
RvDjvgPqYASSrZm+kvQW5cBAciMWDOe4y91ovAW+En3lFBoO+2Zg\nbcPr/8wUTblxfQxU660Fa4
GL0u2Wv5/f+94vlLb5nTpIfcFU7wllAXTjBwaf0Uet\nPy1P2QKBgQDPoyJqPi2TdN7ZQYcoXAM4
Gl5Yv9oO16RC917XH6SLvj0ePmdLgBXo\nrR6aAmOjLzFp9jiytWZqVR9DbAWd2YNpvQav4Gude3
6w==\n-----END RSA PRIVATE KEY-----\n",
"algorithm": "RSA"
} </computeroutput>
</screen>
<para>
This sample request returns the CSR, the private key associated with the
request, and the public key. The security management service stores the
private key in the repository.
When the signed certificate is returned by the certificate authority and you
import the certificate into the keystore, you do not need to supply the
private key. The security management service locates the private key in the
repository, adds the certificate chain, and loads it into the keystore.
</para>
<para>
If you will be importing the signed certificate into the keystore of an
OpenIDM instance that is not connected to the repository in which this
private key was stored, you must include the private key when you import the
signed certificate. Setting <literal>"returnPrivateKey" : true</literal> in
the CSR enables you to maintain a copy of the private key for this purpose.
</para>
<para>Send the output from
<screen>"csr": "-----BEGIN CERTIFICATE REQUEST-----
...
-----END CERTIFICATE REQUEST-----</screen>
to your certificate authority for signature.
</para>
</section>
<section xml:id="import-signed-cert-over-rest">
<title>To Import a Signed Certificate into the Keystore</title>
<para>
When a signed certificate is returned by the certificate authority, import
it into the keystore by running a RESTful <literal>PUT</literal> command on
the keystore alias. Include the CA root certificate in the command. If you
are not importing the certificate into the same keystore as the one from
which the CSR was generated, include the private key in the PUT request.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"alias": "openidm-localhost",
"fromCSR": true,
"certs": [
"-----BEGIN CERTIFICATE-----\n
MIIGcDCCBVigAwIBAgIDC23tMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\n
TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\n
YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\n
MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTMwODA3MTMyODAz\n
WhcNMTQwODA4MDY0NTM5WjB2MRkwFwYDVQQNExBwZ3BDaGU4cEJPZnptVE9KMQsw\n
CQYDVQQGEwJHQjEjMCEGA1UEAxMadGVzdC1jb25uZWN0LmZvcmdlcm9jay5jb20x\n
JzAlBgkqhkiG9w0BCQEWGHBvc3RtYXN0ZXJAZm9yZ2Vyb2NrLmNvbTCCASIwDQYJ\n
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJRWGbnMGs+uGKU6ZrlTaaFdPczLqZnv\n
opV6H1RkrRba2Lom6Hp2pgkqvOBfd1ZMOmLbjUHt0jhypnIzu7TVwtTH7Ywsrx9F\n
CV0YXKvHZgytcwZIc5WkQYaSWQA9lDWZzt5XjCErCATfiGEQ0k02QgpEfNTXxwQs\n
Au4wggLqMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUF\n
BwMBMB0GA1UdDgQWBBR2zHzb71ZOHSwDZk28L9It3PvOtzAfBgNVHSMEGDAWgBTr\n
Z2Vyb2NrLmNvbYINZm9yZ2Vyb2NrLmNvbTCCAVYGA1UdIASCAU0wggFJMAgGBmeB\n
DAECATCCATsGCysGAQQBgbU3AQIDMIIBKjAuBggrBgEFBQcCARYiaHR0cDovL3d3\n
dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjCB9wYIKwYBBQUHAgIwgeowJxYgU3Rh\n
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwAwIBARqBvlRoaXMgY2VydGlm\n
aWNhdGUgd2FzIGlzc3VlZCBhY2NvcmRpbmcgdG8gdGhlIENsYXNzIDEgVmFsaWRh\n
dGlvbiByZXF1aXJlbWVudHMgb2YgdGhlIFN0YXJ0Q29tIENBIHBvbGljeSwgcmVs\n
aWFuY2Ugb25seSBmb3IgdGhlIGludGVuZGVkIHB1cnBvc2UgaW4gY29tcGxpYW5j\n
ZSBvZiB0aGUgcmVseWluZyBwYXJ0eSBvYmxpZ2F0aW9ucy4wNQYDVR0fBC4wLDAq\n
oCigJoYkaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0MS1jcmwuY3JsMIGOBggr\n
b20vc3ViL2NsYXNzMS9zZXJ2ZXIvY2EwQgYIKwYBBQUHMAKGNmh0dHA6Ly9haWEu\n
c3RhcnRzc2wuY29tL2NlcnRzL3N1Yi5jbGFzczEuc2VydmVyLmNhLmNydDAjBgNV\n
HRIEHDAahhhodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS8wDQYJKoZIhvcNAQEFBQAD\n
DORtr5HOD1xGiixZWzA1V2pTmF/hJZbhmEgBUSIyPK5Z9pZPephMf+/KrovbQqKr\n
6SEjgs7dGwpo6fA2mfCH5cCrid0=\n
-----END CERTIFICATE-----",
"-----BEGIN CERTIFICATE-----\n
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\n
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\n
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\n
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\n
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\n
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\n
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\n
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\n
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\n
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\n
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\n
-----END CERTIFICATE-----"
],
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\n
zDot5q3vP9YjCihMZMkSa0zT2Zt+8S+mC0EVuYuTVhVpqrVNtkP1mlt+CYqmDffY\n
sGuD6SMrT6+SeAzX2uYFgY4+s8yaRWBcr0C5Z7yihilM6BK+IJ4is9kaW5VFr1Ph\n
PxYJwzHzouTdQDkLYlCjMN++NmIYfx7zrbEYV4VzXMxgNq7d3+d5dlVfE8xpAjSR\n
Lqlamib+doe1oWOQ2WiS6baBAH+Gw5rgqfwhJbCY/UlbCpuJ6kl7TLvTrFp8YpvB\n
n29oGfZ3vav8PqG+F987hSyWEIdGTMfIxwaUrdYe1fmbUCxv0suMcYTRbAs9g3cm\n
ViePuLbCdxIC5heUyqvDBbeOzgQWOu6SZjX+mAQpo0DPKt1KDP4DKv9EW92sIwW3\n
AnFg98sje0DZ+zfsnevGioQMJrG0JSnqTYADxHaauu7NWndkfMZisfNIKA0u+ajU\n
AbP8xFXIP5JU8O4tWmlbxAbMOYfrZHabFNZx4DH1OVOJqdJIVx0KER0GSZd50D6W\n
QBzCfEbwMlJ17OB0AgWzNrbaak3MCmW1mh7OecjQwge1ajy7ho+JtQ==\n
-----END RSA PRIVATE KEY-----"
}' \
</userinput>
<computeroutput>
{
"_id": "openidm-localhost"
} </computeroutput> </screen>
<para>
If the import is successful, the command returns the alias of the keystore
to which the signed certificate was added.
</para>
</section>
<section xml:id="certificate-over-rest">
<title>Generating a Self-Signed Certificate Over REST</title>
<para>
To generate a self-signed X.509 certificate, use the
<literal>generateCert</literal> action on the <literal>keystore</literal>
endpoint. This action must be performed as an authenticated administrative
user. The generated certificate is returned in the response to the request,
and stored in the OpenIDM keystore.
</para>
<para>
Specify the details of the certificate in the JSON payload. For example:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"algorithm" : "RSA",
"signatureAlgorithm" : "SHA512WithRSAEncryption",
"keySize" : 2048,
"domainName" : "www.example.com",
"validFrom" : "2014-08-13T07:59:44.497+02:00",
"validTo" : "2015-08-13T07:59:44.497+02:00",
"returnPrivateKey" : true,
"alias" : "new-alias"
}' \
</userinput>
<computeroutput>{
"publicKey": {
"algorithm": "RSA",
"encoded": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
...
\n-----END PUBLIC KEY-----\n",
"format": "X.509"
},
"cert": "-----BEGIN CERTIFICATE-----\nMIIDSDCCAjCgAwIBAgIGAUfOo3GvMA0GCSqGSIb3
...
\n-----END CERTIFICATE-----\n",
"type": "X.509",
"_id": "new-alias"
}</computeroutput>
</screen>
<itemizedlist>
<para>
The following certificate details can be specified:
</para>
<listitem>
<para>
<literal>"algorithm"</literal> (optional) - the public key algorithm, for
example, <literal>RSA</literal>. If no algorithm is specified, a default
of <literal>RSA</literal> is used.
</para>
</listitem>
<listitem>
<para>
<literal>"signatureAlgorithm"</literal> (optional) - the signature type,
for example, <literal>SHA512WithRSAEncryption</literal>. If no algorithm
is specified, a default of <literal>SHA512WithRSAEncryption</literal> is
used.
</para>
</listitem>
<listitem>
<para>
<literal>"keySize"</literal> (optional) - the size of the key (in bits)
used in the cryptographic algorithm, for example <literal>2048</literal>.
If no key size is specified, a default of <literal>2048</literal> is used.
</para>
</listitem>
<listitem>
<para>
<literal>"domainName"</literal> - the fully qualified domain name (FQDN)
</para>
</listitem>
<listitem>
<para>
<literal>"validFrom"</literal> and <literal>"validTo"</literal> (optional)
- the validity period of the certificate, in UTC time format, for example
<literal>2014-08-13T07:59:44.497+02:00</literal>. If no values are
specified, the certificate is valid for one year, from the current date.
</para>
</listitem>
<listitem>
<para>
<literal>"returnPrivateKey"</literal> (optional) - set this to
<literal>true</literal> to return the private key along with the request.
</para>
</listitem>
<listitem>
<para>
<literal>"alias"</literal> - the keystore alias or string that identifies
the certificate, for example <literal>openidm-localhost</literal>.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="security-endpoints">
<title>Security Management Service Endpoints</title>
<para>
The OpenIDM security management service includes the following endpoints:
<itemizedlist>
<listitem>
</listitem>
<listitem>
</listitem>
</itemizedlist>
</para>
<para>
You can use these endpoints to READ the contents on the keystore and
truststore.
</para>
<para>
In addition, you can use these endpoints to perform related actions such as
<literal>generateCert</literal> and <literal>generateCSR</literal>.
</para>
<para>
When you set up an <literal>_action</literal> request on either endpoint,
you need to include all properties, including <literal>alias</literal>,
in the request data.
</para>
<para>
The alias is not provided in the URL; it is available in the data object.
For example, you might include <literal>"alias" : "openidm-localhost"</literal>
within a <literal>--data '{ "alias" : "openidm-localhost" }' </literal>
option.
</para>
<para>
Certificates and private keys are associated with the following endpoints:
</para>
<para>
endpoints.
</para>
</section>
</section>
<section xml:id="security-precautions">
<title>Security Precautions for a Production Environment</title>
<para>
Out of the box, OpenIDM is set up for ease of development and deployment.
When you deploy OpenIDM in production, there are specific precautions you
should take to minimize security breaches. After following the guidance in
this section, make sure that you test your installation to verify that it
behaves as expected before putting it into production.
</para>
<section xml:id="security-ssl-https">
<title>Use SSL and HTTPS</title>
<indexterm>
<primary>Security</primary>
<secondary>SSL</secondary>
</indexterm>
<para>We recommend that you disable plain HTTP access, as
described in the section titled <link
xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="integrators-guide#security-jetty"><citetitle>Secure
Jetty</citetitle></link>.</para>
<para>
network. Mutual authentication with strong certificates, imported into the
trust and keystores of each application, provides a level of confidence for
trusting application access.
</para>
<para>Augment this protection with message level security where
appropriate.</para>
</section>
<section xml:id="rest-over-https">
<title>Restrict REST Access to the HTTPS Port</title>
<para>
When possible, use a certificate to secure REST access, over HTTPS. For
production, that certificate should be signed by a certificate authority.
</para>
<para>
OpenIDM generates a self-signed certificate when it first starts up. You can
use this certificate to test secure REST access.
</para>
<para>
While not recommended for production, you can test secure REST access using
the default self-signed certificate. To do so, you can create a self-signed
procedure:
</para>
<procedure>
<step>
<para>
Extract the certificate that is generated when OpenIDM starts up.
</para>
<para>
This command outputs the entire certificate to the terminal.
</para>
</step>
<step>
<para>
Using any text editor, create a file named
from <literal>-----BEGIN CERTIFICATE-----</literal> to
<literal>----END CERTIFICATE-----</literal> and paste it into the
the following:
</para>
<screen>
<computeroutput>-----BEGIN CERTIFICATE-----
MIIB8zCCAVygAwIBAgIETkvDjjANBgkqhkiG9w0BAQUFADA+MSgwJgYDVQQKEx9P
cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
c3QwHhcNMTEwODE3MTMzNTEwWhcNMjEwODE3MTMzNTEwWjA+MSgwJgYDVQQKEx9P
cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKwMkyvHS5yHAnI7+tXUIbfI
nQfhcTChpWNPTHc/cli/+Ta1InTpN8vRScPoBG0BjCaIKnVVl2zZ5ya74UKgwAVe
oJQ0xDZvIyeC9PlvGoqsdtH/Ihi+T+zzZ14oVxn74qWoxZcvkG6rWEOd42QzpVhg
wMBzX98slxkOZhG9IdRxAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEASo4qMI0axEKZ
UCq7qJZ75HnIIeVrZqmfTx8169paAKAaNF/KRhTE6ZII8+awst02L86shSSWqWz3
-----END CERTIFICATE-----</computeroutput>
</screen>
</step>
<step>
<para>
Test REST access on the HTTPS port, referencing the self-signed certificate
in the command. For example:
</para>
<screen><userinput>$ curl \
--header "X-OpenIDM-Username:openidm-admin" \
--header "X-OpenIDM-Password:openidm-admin" \
--cacert self-signed.crt \
--request GET \
<computeroutput>{
"result": [],
"resultCount": 0,
"pagedResultsCooke": null,
"remainingPagedResuts": -1
} </computeroutput></screen>
</step>
</procedure>
</section>
<section xml:id="security-encrypt-data">
<title>Encrypt Data Internally and Externally</title>
<indexterm>
<primary>Security</primary>
<secondary>Encryption</secondary>
</indexterm>
<indexterm>
<primary>Encryption</primary>
</indexterm>
data, OpenIDM also supports explicit encryption of data that goes on the
the final endpoint.</para>
<para>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.</para>
<para>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.</para>
</section>
<section xml:id="security-messages">
<title>Use Message Level Security</title>
<indexterm>
<primary>Security</primary>
<secondary>Authentication</secondary>
</indexterm>
<para>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.</para>
<section xml:id="security-messages-auth">
<title>Message Level Security with Logins</title>
<para>The following examples show successful authentications.</para>
<screen>
$ <userinput>curl \
--cacert self-signed.crt \
--user openidm-admin:openidm-admin \
<computeroutput>
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Set-Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ...;Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)
{"result":[],"resultCount":"0","pagedResultsCookie":null,"remainingPagedResults":-1}
</computeroutput>
$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
<computeroutput>
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Set-Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ...;Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)
{"result":[],"resultCount":"0","pagedResultsCookie":null,"remainingPagedResults":-1}
</computeroutput>
$ <userinput>curl \
--cacert self-signed.crt \
--header "Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ..." \
--header "X-Requested-With: OpenIDM Plugin" \
<computeroutput>
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)
</computeroutput>
</screen>
<para>Notice that the last example uses the cookie OpenIDM set in the
response to the previous request, and includes the
<literal>X-Requested-With</literal> 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
<literal>X-Requested-With</literal> header, OpenIDM returns HTTP 403
Forbidden.</para>
<note>
<para>
The careful readers among you may notice that the expiration date of the
JWT cookie, January 1, 1970, corresponds to the start of UNIX time. Since
that time is in the past, browsers will not store that cookie after the
browser is closed.
</para>
</note>
<para>You can also request one-time authentication without a session.</para>
<screen>
$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-NoSession: true" \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
<computeroutput>
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)
{"result":[],"resultCount":"0","pagedResultsCookie":null,"remainingPagedResults":-1}
</computeroutput>
</screen>
</section>
<section xml:id="security-messages-logout">
<title>Logout By Removing the JWT Cookie</title>
<para>
OpenIDM maintains sessions with a JWT session cookie, stored in a client
browser. To log out and destroy the session, you would access and remove that
cookie from the client browser.
</para>
<para>
The JWT session cookie is based on the <literal>JWT_SESSION</literal> module
xlink:href="integrators-guide#supported-auth-modules" xlink:show="new">
<citetitle>Supported Authentication Modules</citetitle></link>.
</para>
</section>
</section>
<section xml:id="security-replace-defaults">
<title>Replace Default Security Settings</title>
<indexterm>
<primary>Passwords</primary>
</indexterm>
<para>The default security settings are adequate for evaluation purposes. For
production, change the default encryption key, and then replace the default user
password.</para>
<procedure xml:id="security-change-encryption-keys">
<title>To Change Default Encryption Keys</title>
<indexterm>
<primary>Encryption</primary>
</indexterm>
<indexterm>
<primary>Security</primary>
<secondary>Encryption</secondary>
</indexterm>
<para>By default, OpenIDM uses a symmetric encryption key with alias
<literal>openidm-sym-default</literal>. Change this default key before
deploying OpenIDM in production.</para>
<para>
As noted in the section on the<link xlink:show="new"
xlink:href="integrators-guide#cli-keytool"
xlink:role="http://docbook.org/xlink/role/olink">
<citetitle>keytool</citetitle></link> command, the default keystore
password is <literal>changeit</literal>.
</para>
<step>
<para>Add the new key to the keystore.</para>
$ keytool \
-genseckey \
-alias new-sym-key \
-keyalg AES \
-keysize 128 \
-keystore security/keystore.jceks \
-storetype JCEKS</userinput>
<computeroutput>Enter keystore password:
Enter key password for <new-sym-key>
(RETURN if same as keystore password):
Re-enter new password:</computeroutput></screen>
<para>Additional options associated with the <command>keytool</command>
command in OpenIDM are shown in the following file:
</step>
<step>
<para>Change the alias used in
</step>
</procedure>
<procedure xml:id="security-replace-default-user-password">
<title>To Replace the Default User & Password</title>
<para>After changing the default encryption key, change at least the default
user password.</para>
<step>
<para>Use the <command>encrypt</command> command to obtain the encrypted
version of the new password.</para>
<screen>
<computeroutput>
...
-----BEGIN ENCRYPTED VALUE-----
{
"$crypto" : {
"value" : {
"iv" : "TCoC/YrmiRmINw6jCPB5LQ==",
"data" : "nCFvBIApIQ7C6k+UPzosaA==",
"key" : "openidm-sym-default"
},
"type" : "x-simple-encryption"
}
}
------END ENCRYPTED VALUE------
</computeroutput>
</screen>
</step>
<step>
<para>
Replace the user object in the
script before setting up your JDBC repository for OpenIDM.
</para>
<para>
Alternatively, replace the user in the internal user table.
</para>
</step>
</procedure>
</section>
<section xml:id="security-jetty">
<title>Secure Jetty</title>
<indexterm>
<primary>Ports</primary>
<secondary>Disabling</secondary>
</indexterm>
<para>
If you do not want to use regular HTTP on a production OpenIDM system, you
need to make two changes.
</para>
<para>
file. Comment out the line that enables regular HTTP.
</para>
<para>
The following excerpt includes the Java comment code that you would add
argument (8080 by default) is taken from the
</para>
<programlisting language="xml">
<Call name="addConnector>
<Arg>
<Set name="host"><Property name="jetty.host" /></Set>
name="getProperty"<Arg>openidm.port.http</Arg></Call></Set> -->
<Set name="maxIdleTime">300000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
<Set name="confidentialPort">
<Arg>openidm.port.https</Arg>
</Call>
</Set>
</New>
</Arg>
</Call></programlisting>
<para>
property to false, as shown in the following excerpt:
</para>
org.osgi.service.http.secure.enabled=true</programlisting>
</section>
<section xml:id="security-urls">
<title>Protect Sensitive REST Interface URLs</title>
<para>
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.
</para>
<!-- TODO: explain how to deny "it" in the authorization policy -->
<para>
In addition, you can deny direct HTTP access to system objects in production,
particularly access to <literal>action</literal>. As a rule of thumb, do not
expose anything that is not used in production. The main public interfaces
or are used for internal consumption.
</para>
<para>OpenIDM supports native query expressions on the repository, and it is
possible to enable these over HTTP, for example, the following query should
identify managed users in an OrientDB repository:</para>
<screen>
$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
"https://localhost:8443/openidm/managed/user?_queryExpression=select+*+from+managed_user"</userinput></screen>
<para>
By default, direct HTTP access to native queries is disallowed, and should
remain so in production systems.
</para>
<para>
For testing or development purposes, it can be helpful to enable native
queries on the repository over HTTP. To do so, edit the access
remove any instances of <literal>"disallowQueryExpression()"</literal>
such as the following:
</para>
<programlisting language="javascript">
{
"pattern" : "*",
"roles" : "openidm-admin",
"methods" : "*", // default to all methods allowed
"actions" : "*", // default to all actions allowed
// "customAuthz" : "disallowQueryExpression()",
"excludePatterns": "system/*"
},
...
{
"pattern" : "system/*",
"roles" : "openidm-admin",
"methods" : "create,read,update,delete,patch,query", // restrictions on 'action'
"actions" : ""
// "customAuthz" : "disallowQueryExpression()"
},
...
"customAuthz" : "ownDataOnly() &&
managedUserRestrictedToAllowedProperties('"+allowedPropertiesForManagedUser+"')",
// && disallowQueryExpression()"
</programlisting>
<para>See the chapter on <link xlink:href="integrators-guide#chap-auth"
Authentication, Authorization & RBAC</citetitle></link> for an example
showing how to protect sensitive URLs.</para>
</section>
<section xml:id="security-files">
<title>Protect Sensitive Files & Directories</title>
<para>Protect OpenIDM files from access by unauthorized users.</para>
<para>In particular, prevent other users from reading files in at least the
<para>
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
<filename>root</filename>, <filename>Administrator</filename>,
<filename>openidm</filename>, or something similar.
</para>
<procedure xml:id="security-files-unix">
<title>Protecting key files in Unix</title>
<step>
<para>
For the target directory, and the files therein, make sure user and
group ownership is limited to the user that is running the OpenIDM
service.
</para>
</step>
<step>
<para>
Disable access of any sort for <literal>other</literal>
users. One simple command for that purpose, from the
</para>
<screen># chmod -R o-rwx .</screen>
</step>
</procedure>
<procedure xml:id="security-files-windows">
<title>Protecting key files in Windows</title>
<step>
<para>
The OpenIDM process in Windows is normally run by the
<literal>Administrator</literal> user.
</para>
</step>
<step>
<para>
If you are concerned about the security of the administrative account,
you can <literal>Deny</literal> permissions on the noted directories to
existing users, or alternatively the <literal>Users</literal> group.
</para>
</step>
</procedure>
</section>
<section xml:id="security-bootstrap">
<title>Obfuscate Bootstrap Information</title>
<para>
including the keystore password, to start up. The keystore password is
<literal>changeit</literal> by default, and is stored in clear text in the
follow these steps.
</para>
<orderedlist>
<listitem>
<para>
Generate an obfuscated version of the password, by using the crypto bundle
provided with OpenIDM:
</para>
<screen>$ <userinput>$ java -jar /path/to/openidm/bundle/openidm-crypto-<?eval ${project.version}?>.jar
</userinput>
<computeroutput>
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
</computeroutput>
</screen>
</listitem>
<listitem>
<para>
Paste either the obfuscated password (<literal>OBF:xxxxxxx</literal>) or
the encrypted password (<literal>CRYPT:xxxxxxx</literal>) into the
regular keystore password and remove the comment tag, either from the line
that contains the obfuscated password or from the line that contains the
encrypted password:
</para>
<screen>
<computeroutput>
...
# Keystore password, adjust to match your keystore and protect this file
# openidm.keystore.password=changeit
openidm.truststore.password=changeit
# Optionally use the crypto bundle to obfuscate the password and set one of these:
openidm.keystore.password=OBF:1vn21ugu1saj1v9i1v941sar1ugw1vo0
# openidm.keystore.password=CRYPT:a8b5a01ba48a306f300b62a1541734c7
... </computeroutput>
</screen>
</listitem>
<listitem>
<para>
Restart OpenIDM.
</para>
</listitem>
</orderedlist>
</section>
<section xml:id="security-remove-dev-tools">
<title>Remove or Protect Development & Debug Tools</title>
<para>Before deploying OpenIDM in production, remove or protect development
and debug tools, including the OSGi console exposed under
integrated with authentication for OpenIDM.</para>
<para>To remove the OSGi console, remove the web console bundle,
<para>If you cannot remove the OSGi console, then protect it by overriding
the default <literal>admin:admin</literal> credentials. Create a file called
containing the user name and password to access the console in Java
properties file format.</para>
<programlisting language="ini">
username=<replaceable>user-name</replaceable>
password=<replaceable>password</replaceable></programlisting>
</section>
<section xml:id="security-protect-repo">
<title>Protect the OpenIDM Repository</title>
<!-- TODO: change if OrientDB is supported in production for 3.0 -->
<para>Use the JDBC or MSSQL repositories. OrientDB is not yet supported for
production use.</para>
<para>Use a strong password for the JDBC connection. Do not rely on default
passwords.</para>
<para>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.</para>
</section>
<section xml:id="security-adjust-log-levels">
<title>Adjust Log Levels</title>
<para>Leave log levels at <literal>INFO</literal> in production to ensure
that you capture enough information to help diagnose issues. See the chapter
xlink:href="integrators-guide#chap-logs"><citetitle>Configuring Server
Logs</citetitle> for more information.</link>
</para>
<para>At start up and shut down, <literal>INFO</literal> can produce many
messages. Yet, during stable operation, <literal>INFO</literal> generally
results in log messages only when coarse-grain operations such as
scheduled reconciliation start or stop.</para>
</section>
<section xml:id="security-run-as-service">
<title>Set Up Restart At System Boot</title>
<para>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 <link
xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="integrators-guide#chap-services"><citetitle>Starting and
Stopping OpenIDM</citetitle></link>.</para>
<para>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.</para>
</section>
</section>
</chapter>