Detailed explanation of the Docker Compose setup
Keep in mind that:
This is not a hands-on tutorial and does not require any further action from your side.
Therefore, this article is an optional reading.
Challenges of local installation and development
In a nutshell, the article is dedicated to the two main challenges that are related to each other.
Running the HCMS CSK locally, and/or on a single machine, is coupled with various obstacles since the HCMS CSK has been designed as a cloud-based application. It is not a monolith application. The components of it are designed to run each on its own server, and a proper communication is necessary for them. On the same machine, this communication becomes a challenge as you need to imitate a "physical" separation between them but also the proper working connections.
Local development of the HCMS Client is tricky since existing browsers are very restrictive due to their security measures, especially Chrome. When you run the HCMS Client locally, the browser would be confronted with an insecure connection which it denies.
This article helps you to understand why the Docker Compose setup has been chosen for addressing these challenges.
Solutions and new challenges
Multiple containers
The setup is run in more than one container. If you look into your Docker Desktop while the application is running, you will see a parent container (called standalone
) and three children containers, hcms
, hcms-client
, and squid
. Docker Compose helps to make those containers work together smoothly, but we also had to take care of a few additional technical challenges. See next sections for detailed information.
docker-compose.yaml
also an overview of those containers, their orgins, and how they are connected.
Dealing with browser security
Let's go more deeply into the technical details. Two approaches are possible to make local development work:
by not compromising browser security and having both HCMS and HCMS Client on the same domain.
by compromising browser security and using the unsecure flag in cookies.
It would suffice when only one of the requirements - either flag or shared domain - is satisfied. They are not mutually exclusive though, and the provided setup includes them both, to ensure more robustness.
HCMS and HCMS Client on the same domain
In production, both parts of the application (hcms
and hcms-client
) will be available on the same domain, with the path-based routing implemented. The /hcms/
prefix is then used for the hcms
container, all other traffic goes to the hcms-client
container.
When doing local development, by default, they won't be on the same domain. As a solution, the HTTP "reverse" proxy is used, in particular, the haproxy. It is not run in a separate container, but inside the hcms-client
container instead. This means that the hcms-client
docker image is not used directly, but a new auxiliary one is built.
Cookies
In this Docker Compose setup, the SameSite=None
(an unsecure flag) is set in the HCMS configuration to bypass the browser security. To be precise, the flag is SameSite=None;Secure
.
Modern browsers won't allow you to set an unsecure flag SameSite=None
without specifying a secure context. Using cookies in a JavaScript from a different domain requires an additional Secure
flag. So, the complete flag is SameSite=None;Secure;
. This is needed for ther local frontend development, when, typically, the frontend cannot be run on the same domain as the rest of the application. Without SameSite=None;Secure;
, requests to both backends are always sent without any cookie and thus are all unauthorized.
Warning: this mode also disables other security checks and in general should be used only in combination with some firewall rules.
The flags are set by the following procedure:
When you run the the preparation script (
prepare-env.sh
), you need to provide the-d
option.The script will set the environmental variable
UNSECURE_DEVELOPMENT
to non-empty and pass it to theentrypoint.sh
.The non-empty
UNSECURE_DEVELOPMENT
variable enables rewriting the header with a regular expression in the haproxy.
However, when the unsecure flag is set, it is only possible to use https
and not http
. This has the following implications.
HTTPS protocol (TLS/SSL termination)
As a consequence of using HTTPS in a local environment, you need an automatically generated (self-signed) TLS certificate.
To enable you to use HTTPS, the setup uses the haproxy. Both the certificate and the private key are provided as a file in the mounted directory ssl
. The filename is hardcoded as hcmsclient.pem
. If available, the certificate+key can be provided as an optional parameter of the preparation script prepare-env.sh
, see also prepare Docker host.
Automatically generated (self-signed) TLS certificate
To provide you with such certifiacte, the setup relies upon the openssl that used as a part of the openjdk
image. The image is downloaded automatically as part of the docker run
execution.
The preparation script (prepare-env.sh
) checks the provided PEM file (if any) or the ./ssl/hcmsclient.pem
file (if it already exists). If one of the files is present and contains both the key and certificate, and the certificate is for the correct domain, then they are used. Otherwise, a new self-signed certificate is generated and saved as ./ssl/hcmsclient.pem
which is then used by the haproxy
.
Self-signed certificates do not automatically work in any browser and must be explicitly accepted sometimes. In certain cases, it can be bypassed as well, e.g., in Chrome by typing thisisunsafe
as described here.
Using a self-signed certificate in cross-component communication is enabled through the following configuration:
For the
hcms-client
container, a special environment variableNODE_TLS_REJECT_UNAUTHORIZED
is set to0
in theDockerfile
For the
hcms
container, the certificate is added to the list of trusted certificates by mounting thecacerts
file directly inside the JDK. Thiscacerts
file is patched by the preparation script (prepare-env.sh
). Note that the certificate is added even when it is actually provided from an external source (and, presumably, isn't self-signed at all). This might be useful when a non-standard CA is used.
access_token
cookie
JWT secret and The JWT secret and the corresponding cookie access_token
must be correctly configured. Also, user tracking must be enabled. The setup performs this configuration automatically by overriding the HCMS configuration stored on the server with a local configuration file. The JWT secret key can be provided, but that is an optional step and a new random one is generated if necessary. This is perfectly fine, because the only important requirement is that both parts of the application use the same key.
In a real environment, both JWT secret and the access_token
cookie must be configured via the commandline client or directly in the config.xml
. This is described in more detail in the cloud installation guide
Empty template configuration files are stored in the hcms-config.templates
directory. Configuration files with the correctly filled values are stored in the hcms-config
directory. The latter one is then mounted to the hcms
docker container. It is possible to make custom changes in that configuration, but please remember that the content of the hcms-config
directory is erased every time the preparation script runs!
If a dedicated DNS domain for the app is not (yet) available
It can happen that a dedicated, special DNS domain for the application is not available yet or cannot be easily obtained at all. As a solution, the setup uses a combination of the HTTP "forward" proxy and Docker Compose networking. When a browser uses a configured HTTP "forward" proxy, the DNS domain is not needed because the hostname resolution is done by the proxy itself. This proxy is running in a container where Docker Compose provides this hostname instead of DNS.
The host resolution uses a special property external_links
in the docker-compose.yml
. This property adds a custom hostname, without the need for a DNS server. The squid proxy is used for this purpose and exposed on port 3128. The image is built from scratch (based on the alpine
linux) and run as a separate container.
Note Most browsers actually don't use a forward proxy for http://localhost:*
requests. This is important for doing local frontend development.
Other advantages of this setup
As a bonus, this setup provides a very convenient way for creating a demo environment without advanced knowledge of cloud architecture and more importantly without such infrastructure at hand or necessary access rights for using it.