chap-security.xml revision 006579fc6d904d79ff1065cc8aa5c244a00f41ab
<?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-2012 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 http://docbook.org/xml/5.0/xsd/docbook.xsd'
xmlns:xlink='http://www.w3.org/1999/xlink'
xmlns:xinclude='http://www.w3.org/2001/XInclude'>
<title>Securing & Hardening OpenIDM</title>
<indexterm>
<primary>Best practices</primary>
</indexterm>
<indexterm>
<primary>Security</primary>
</indexterm>
<para>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.</para>
<para>Out of the box, OpenIDM is set up for ease of development and
deployment. When deploying OpenIDM in production, take the following
precautions.</para>
<section xml:id="security-ssl-https">
<title>Use SSL and HTTPS</title>
<indexterm>
<primary>Security</primary>
<secondary>SSL</secondary>
</indexterm>
<para>Disable plain HTTP access, included for development convenience, 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>
the wire. Mutual authentication with certificates imported into the
applications' trust and key stores provides some confidence for trusting
application access.</para>
<para>Augment this protection with message level security where
appropriate.</para>
</section>
<section xml:id="security-encrypt-data">
<title>Encrypt Data Internally & 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 end point.</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.</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.</para>
<para>The following examples show successful authentications.</para>
<screen>$ curl
--user openidm-admin:openidm-admin
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
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
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
--header "Cookie: JSESSIONID=ixnekr105coj11ji67xcluux8"
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"}]}</screen>
<para>Notice that the last example uses the cookie OpenIDM set in the
response to the penultimate request. You can also request one-time
authentication without a session.</para>
<screen>$ curl
--header "X-OpenIDM-NoSession: true"
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
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"}]}</screen>
<para>To log out and destroy the session, send the specific OpenIDM
header.</para>
<screen>$ curl
--header "Cookie: JSESSIONID=ixnekr105coj11ji67xcluux8"
--header "X-OpenIDM-Logout: true"
<para>OpenIDM creates the <literal>openidm-admin</literal> user with password
<literal>openidm-admin</literal> by default. This internal user is stored in
OpenIDM's repository.</para>
<screen>mysql> select objectid,roles from internaluser;
+---------------+----------------------------------+
| objectid | roles |
+---------------+----------------------------------+
| anonymous | openidm-reg |
| openidm-admin | openidm-admin,openidm-authorized |
+---------------+----------------------------------+
2 rows in set (0.00 sec)</screen>
<indexterm>
<primary>Authentication</primary>
<secondary>Internal users</secondary>
</indexterm>
<para>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 <link
xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="integrators-guide#appendix-router"><citetitle>Router
Service Reference</citetitle></link> appendix, enables you to apply filters
xlink:href="integrators-guide#chap-auth"><citetitle>Managing Authentication,
Authorization & RBAC</citetitle></link> for details.</para>
</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 an symmetric encryption key with alias
<literal>openidm-sym-default</literal>. Change this default key before
deploying OpenIDM in production.</para>
<step>
<para>Add the new key to the key store.</para>
$ 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:
$ </screen>
<para>Also see
</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>
$ cli.sh encrypt newpwd
...
-----BEGIN ENCRYPTED VALUE-----
{
"$crypto" : {
"value" : {
"iv" : "TCoC/YrmiRmINw6jCPB5LQ==",
"data" : "nCFvBIApIQ7C6k+UPzosaA==",
"key" : "openidm-sym-default"
},
"type" : "x-simple-encryption"
}
}
------END ENCRYPTED VALUE------</screen>
</step>
<step>
<para>Replace the user object in the
setting up MySQL as a 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>Before running OpenIDM in production, edit the
clear text HTTP. Opt instead for HTTPS, either with or without mutual
authentication. To disable plain HTTP access, comment out the section in
8080.</para>
<programlisting language="xml">
<!--
<Item>
<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>
--></programlisting>
</section>
<section xml:id="security-urls">
<title>Protect Sensitive REST Interface URLs</title>
<para>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.</para>
<para>Similarly 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
Other URIs are triggered indirectly, or are for internal consumption.</para>
<para>OpenIDM supports native query expressions on the JDBC repository and it
is possible to enable these over HTTP, for example:</para>
<screen width="91">$curl
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
<para>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
<screen>"customAuthz" : "disallowQueryExpression()"</screen>
<para>Remember to remove the comma at the end of the preceding line as
well.</para>
<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
</section>
<section xml:id="security-bootstrap">
<title>Obfuscate Bootstrap Information</title>
<para>OpenIDM uses the information in
password to start up. You can set an obfuscated version in the file, or
prompt for the password at start up time.</para>
<para>To generate obfuscated versions of a password, use the
<command>encrypt</command> command-line utility. For more
information, see the <link xlink:href="integrators-guide#cli-encrypt"
</citetitle></link> documentation.</para>
</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>
<para>Use the JDBC repository. 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 details see the section titled <link
xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="integrators-guide#startup-shutdown"><citetitle>Startup &
Shutdown</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>
</chapter>