Deploying Java Remote Connector Server in a Docker Container
Author: |
Konstantin Lapine |
Created at: |
Jun 2023 |
Updated at: |
Aug 2023 |
The Forgerock Java Remote Connector Server (RCS) can be deployed within a Docker container.
##RCS Parent Image
Currently, RCS images could be pulled from the gcr.io/forgerock-io/rcs image repository in the public ForgeRock Container Registry (you will need a Google account to see it in a browser).
A public RCS image provides the default installation of the ForgeRock Open Identity Connector Framework (ICF).
##Customizing the Parent RCS Image
If you need to extend the parent image functionality, you can customize
it by copying additional files to the ICF installation folder in the
final image, which is located at /opt/openicf. The additional files
could include complete connector packages, scripts, drivers, etc.
The easiest and the most readable way to capture your RCS image customizations is to save them in a custom docker image using a custom Dockerfile.
For example:
# 1. Accept build arguments in the FROM instruction. # @example ARG FROM_TAG=1.5.20.15 FROM gcr.io/forgerock-io/rcs:$FROM_TAG # 2. Merge your custom files with the RCS installation. # @example COPY openicf/conf conf COPY openicf/connectors connectors COPY openicf/lib lib COPY openicf/scripts scripts # 3. Use root user for operations that escalate permissions. # @example # USER root # 4. Add untrusted public TLS certificate(s) to RCS' `truststore` to allow for communications with unrecognized hosts. # Run `docker build` with the `--progress plain` option to check the outcome of the RUN instruction. # @example # COPY openicf/security/BadSSL-Untrusted-Root-Certificate-Authority.cer /opt/openicf/security # RUN keytool -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -trustcacerts -import -file /opt/openicf/security/BadSSL-Untrusted-Root-Certificate-Authority.cer -alias badssl-com-untrusted-root-ca -noprompt # 5. Switch back to a non-root user. # @example # USER 11111
-
During the docker build phase, you can use the –build-arg flag, where you can specify the parent image tag against which you want to build your RCS image. Doing so will allow to choose a particular version of the parent image available at the gcr.io/forgerock-io/rcs repository.
For example:
docker build --build-arg FROM_TAG=1.5.20.15 -t rcs .
-
You can copy your local files into the
/opt/openicflocation in the parent RCS image to extend and/or modify its functionality. You can replicate theopenicfinstallation folder structure locally and copy everything at once, or use separateCOPYinstructions for individual folders and files, doing which could help with caching respective layers in the final image.NOTE that the RCS parent image Dockerfile specifies a working directory:
[ . . . ] WORKDIR /opt/openicf [ . . . ]
This means that in the
COPYinstruction destination, you need to use an absolute path or a path relative to theWORKDIRlocation. *openicf/conf+ Provide your custom version of the
openicf/conf/ConnectorServer.propertiesfile, in which you can specify all or some of the RCS configuration properties as described in the Configure a remote connector server RCS doc.-
openicf/connectorsAdd your custom/downloaded connectors packaged in JAR files into this sub-folder.
-
openicf/lib-
Add any additional JARs used by your connectors into this sub-folder.
For example, you can supply database driver(s) that your connectors use.
-
Provide your customized version of the
openicf/lib/framework/logback.xmlfile to overwrite the default one; and thus, adjust the detail of the logs produced by the RCS, as described in the Connector development > Connector troubleshooting doc and in the How do I enable debug logging and log rotation for the Java Remote Connector Server (RCS)? Knowledge Base article.The resulting log files could be found in a running container under the
/opt/openicf/logsfolder, will be printed out in the standard output by the container in the (default) attached mode, or could be explicitly requested from the container with the docker logs command.
-
-
openicf/scriptsAdd Groovy scripts used by your scripted connectors to this folder.
-
-
By default, a Docker container runs as a root user.
It is generally recommended to run a Docker container as a non-root user, which is addressed in the RCS parent image Dockerfile:
[ . . . ] USER 11111 [ . . . ]
For some instructions, however, you might need elevated permissions. For example, for adding an untrusted certificate to the Java truststore, you will need the root user privileges.
-
You can copy any other content that your connectors might use to any other accessible location within the connector server image.
In the provided example, a public certificate is added to the Java
truststoreto allow for RCS communications with an otherwise untrusted host; for example, a third-party API. The destination of theCOPYinstruction in this case could be any accessible location within the image, because the change is not applied by the ICF, but with a command provided in theRUNinstruction.Currently, RCS does not have its own
truststore, and by default is using its Java Virtual Machine (JVM)truststore.The Identity Cloud certificates are signed by a trusted CA and do NOT need to be added to the
truststore. -
After any specific instructions that require elevated permissions have been performed, you should switch back to the non-root user using the USER instruction.
During the development phase, you might choose to continue as a root user. For example, root user privileges are required for enabling file sync when using a Docker image in the Skaffold dev mode.
In this case, you can specify the user in the docker run command with the –user , -u flag.
##Pushing RCS Image to a Docker Repository
To share your RCS image and make it available for orchestration tools, you can push it to a Docker repository.
If your Docker repository is served from a private Docker registry, make sure you have configured Docker to have appropriate privileges to push to this repository. Your Docker registry provider can provide specific instructions on setting up authentication for Docker.
For example, Set up authentication for Docker describes how to use gcloud credential helper for configuring Docker with the Google Cloud CLI session credentials, which you can use while developing your RCS images and pushing them to a Google Cloud Artifact Registry.
You will need to tag your image before you push it to a repository. You can also tag your image with the reference to your repository in the docker build command using the -t, --tag flag.
For example:
$ docker build --build-arg FROM_TAG=1.5.20.15 -t gcr.io/<organization>/<username>/rcs . $ docker push gcr.io/<organization>/<username>/rcs
##Registering RCS
You need to configure your RCS in client mode in order to be able to connect it to a managed environment such as ForgeRock Identity Cloud (Identity Cloud).
In the client mode, you must provide the
connectorserver.connectorServerName property in your RCS
configuration, which must match the name under which your connector
server is registered in IDM.
In Identity Cloud, you can register a connector server using the
ForgeRock Identity
Platform (Platform) admin UI, as described in the
Sync
identities with an external resource > Register a remote server doc. In
the UI, the Name input for a connector server accepts a
^[a-z0-9_-]+$ value, which can then be used in the
connectorServerName property in your connector server configuration.
Eventually, the
Namevalue is saved as thenameproperty of aremoteConnectorClientsentry in theprovisioner.openicf.connectorinfoprovider.jsonfile in IDM.
##Configuring RCS
To configure your RCS in the client mode, you will need to define the following RCS configuration properties:
-
connectorserver.url -
connectorserver.connectorServerName
In order to connect to an Identity Cloud tenant, you will also need to specify the following:
-
connectorserver.tokenEndpoint -
connectorserver.clientId -
connectorserver.clientSecret -
connectorserver.scope
To enable more controlled debug output, you should also specify the RCS logger class:
-
connectorserver.loggerClass
The static connector server properties could be provided via
instructions included in the openicf/conf/ConnectorServer.properties
file.
As an example, a ConnectorServer.properties file could contain the
following instructions for setting the properties that are not likely to
change and carry no sensitive data; hence, could be embedded into the
image:
openicf/conf/ConnectorServer.properties
# Set connectorserver properties.
# @see {@link https://backstage.forgerock.com/docs/openicf/latest/connector-reference/configure-server.html#rcs-properties}
# Set static properties.
connectorserver.scope=fr:idm:*
connectorserver.loggerClass=org.forgerock.openicf.common.logging.slf4j.SLF4JLog
To provide dynamic values in the connector server configuration, which
could be the preferred approach for secrets and environment specifics,
you can define JVM’s system properties via the OPENICF_OPTS
environment variable, which needs to be exported into the JVM’s
environment before the connector server starts. This information should
be provided at the time when the container runs, so that it is not
embedded into the image.
##Running RCS
You can create an RCS container and run it with the docker run command.
In a standalone Docker container, environment variables can be set with one of the -e, --env, --env-file flags.
For example:
.env file
OPENICF_OPTS=-Dconnectorserver.url=wss://openam-dx-kl04.forgeblocks.com/openicf/0 -Dconnectorserver.tokenEndpoint=https://openam-dx-kl04.forgeblocks.com/am/oauth2/realms/root/realms/alpha/access_token -Dconnectorserver.connectorServerName=rcs-docker-1 -Dconnectorserver.clientId=RCSClient -Dconnectorserver.clientSecret=YA...H?
$ docker run --env-file .env rcs
A Docker environment file is limited to one-line variable definitions and might not be easy to read. An alternative approach could be defining the environment variable on the host machine and then using the
-e, --envflag.For example:
.envOPENICF_OPTS="-Dconnectorserver.url=wss://openam-dx-kl04.forgeblocks.com/openicf/0 \ -Dconnectorserver.tokenEndpoint=https://openam-dx-kl04.forgeblocks.com/am/oauth2/realms/root/realms/alpha/access_token \ -Dconnectorserver.connectorServerName=rcs-docker-1 \ -Dconnectorserver.clientId=RCSClient \ -Dconnectorserver.clientSecret=YA...H?"$ set -a; source .env; set +a; $ docker run -e OPENICF_OPTS rcs
This way, the sensitive and dynamic properties will not have to be embedded into the image.
In turn, every value used in the OPENICF_OPTS variable could be
substituted dynamically to address different environments and/or
different connector server names registered in IDM. For example, you
might be interested in running multiple containers referring to
different connector server names registered in a
server
cluster.
##Developing Scripted Connectors with Docker
The parent RCS image does not ship with any (Groovy) scripts. This means that while developing scripted connectors, you can use docker run (-v, --volume) flag as a convenient way of dynamically providing content of your scripts to the running RCS container.
For example:
docker run --rm --env-file .env -v ./openicf/scripts:/opt/openicf/scripts rcs
Now, any updates to the scripts made in the ./openicf/scripts folder
on the host machine will become immediately available to the RCS running
in the container.
While developing your scripts, you can also benefit from attaching a debugger to your RCS process, as described in the Attaching Debugger to RCS within Docker Container chapter of a developing scripted connectors article on the ForgeRock Community site.