Setting up a Jahia cluster

November 14, 2023

Here are the steps followed to install an Jahia cluster for a production environment. We will also detail the installation of two Apache2 front-ends.

General

Jahia will be installed on a cluster of three nodes:

  1. The first node, the processing, will be used for the contribution (addition of content, etc)
  2. The two other servers will be used for the consultation

Each node has :

  • CentOS
  • 40 GB of hard drive
  • 4 CPUs
  • 8 GB of memory

We will also need two servers for Apache2 and one server for the PostgreSQL database (version 9.X).

A F5 or a failover configuration (like Haproxy) above are recommended but won't be described in this document.

Matrix

The following parameters will be used between "<" an ">" in the next parts of the documentation.

Variable Value Description
DB_NAME jahia Name of the PostgreSQL database
DB_PORT 5432 Port used by PostgreSQL
DB_PASSWORD Change_it_it_s_important Password to access the database
DB_SRV_FQDN psql01.int.jahia.com Fully qualified domain name of the DB server
DB_USER_NAME jahia User to access the DV
DX_DATASTORE /srv/datastore/jahia Path of the Jahia datastore
DX_HOME /src/jahia Home of Jahia
DX_INSTALLER DigitalExperienceManager-EnterpriseDistribution-7.2.2.0-r57545.4233.jar Name of the Jahia installer
DX_TOOLS_PASSWORD Needs_to_be_complexed! Password to access the Jahia tools
DX_TOOLS_USER jahia User to access the Jahia tools
DX_ROOT_PASSWORD Needs_to_be_very_very_omplexed! Password for the user root
JAHIA_LINUX_USER jahia Linux user used to install and to run Jahia
JAHIA_SRV_01 jahia01 Hostname for the first Jahia server
JAHIA_SRV_02 jahia02 Hostname for the second Jahia server
JAHIA_SRV_03 jahia03 Hostname for the third Jahia server
JAHIA_SRV_FQDN_01 jahia01.int.jahia.com Fully qualified domain name for the first Jahia server
JAHIA_SRV_FQDN_02 jahia02.int.jahia.com Fully qualified domain name for the second Jahia server
JAHIA_SRV_FQDN_03 jahia03.int.jahia.com Fully qualified domain name for the third Jahia server
MAIL_ADDRESS it@jahia.com E-Mail address used to send/receive communications from Jahia
ORACLE_JDK_FILE_NAME jdk-8u191-linux-i586.rpm Package for the Oracke JDK 8
TOMCAT_HOME <DX_HOME>/tomcat Home of Tomcat
WEB_SRV_FQDN_01 web1.int.jahia.com Fully qualified domain name for the first web server
WEB_SRV_FQDN_02 web2.int.jahia.com Fully qualified domain name for the second web server
PUBLIC_CONTRIBUTE_DOMAIN exemple-contribute.jahia.com Public domain to access the contribution/edition
PUBLIC_DOMAIN example.jahia.com Public domain used to access Jahia

Prerequisites

Install

Oracle JDK 8

Jahia runs on a Java platform so it needs the Oracle JDK to be installed on the system.

  • Go to the Oracle download page
  • Look for the last JDK 8, download the Linux appropriate .rpm package then install it:
    sudo yum localinstall <ORACLE_JDK_FILE_NAME>
  • Once it's installed, check that the following command returns the correct version:
    java -version
  • If you expect the JVM to communicate with SSL endpoints/certicates that are not recognized by default, do not forget to add them to the JVM repository and to disable the automatic update of the JDK.

 

LibreOffice

LibreOffice is being using by Jahia for document conversion.

  • Install it:
    sudo yum install libreoffice

ImageMagick

ImageMagick is being used by Jahia for all operations related to the images (resizing, thumbnails, etc.)

  • Install it:
    sudo yum install ImageMagick

Development tools

Even if you should not need these tools, they might be useful in case of an outage.

  • Install maven/subversion/git:
    sudo yum install maven subversion git

LogRotate

By default, the logs are being rotated but there isn't any mechanism to remove logs older than a specific date. Linux offers a very useful tool to handle the logs.

  • LogRotate should already be installed by default but if it's not the case:
    sudo yum install logrotate

Swappiness

Swappiness is a value between 1 and 100 that controls the priority of the use of the RAM vs the SWAP. In Java applications, the garbage collector might end up using the swap which can slow a lot an application.

  • Check the swappiness
    sudo cat /proc/sys/vm/swappiness
  • If it's below or equal to 10, you don't have to do anything. Otherwise, apply the modifications at runtime:
    sudo sysctl vm.swappiness=10
    sudo swapoff -a
    sudo swapon -a
    
  • Make the modifications persistent by modifying the file /etc/sysctl.conf:
    vm.swappiness = 10

Entropy

Jahia is generating some UUIDs and for that, it needs some randomness and that needs a good entropy. In some virtualized environments, this entropy is not good at all and needs to be improved in order to have good performances.

Please follow this documentation: Jahia and entropy

Jahia Linux user

  • For security reasons, Jahia will need to be installed under a specific user:
    sudo adduser --quiet --disabled-password --disabled-login --home <DX_HOME> --gecos "" <JAHIA_LINUX_USER>

Database connection

  • The nodes of a Jahia cluster are communicating between each other thanks to two TCP ports (By default 7860 and 7870). Please check with your IT team that these flows are authorized.

Database connection

  • The latency against the database needs to be very small, < 0 ms, in order to get good performances:
    ping <DB_SRV_FQDN>
  • Check also that you are able to connect with the DB client and the credentials that will be used for Jahia. This user needs to be able to create the necessary tables and to insert/update/delete their content.
  • In case you're using a dedicated schema, please be sure that it's the default schema or you will have to customiez the JDBC url.

SMTP connection

  • Check that you're able to send mail with the SMTP server you want to use (SMTP relay, SMTP service, etc) from each Jahia server

Shared folder

The Jahia cluster will need a shared folder in order to store the datastore. In order to prevent a SPOF, we advise to check that you the related hardware and software have some redundancy. The easiest way is to give full rights for this user on this table.

  • Check that the user jahia has the right to create a file in the datastore
    sudo -u <JAHIA_LINUX_USER> touch <DX_DATASTORE>
  • Do not forget to remove this file

Internet connection

Check that you have access to Internet

Jahia cluster

Install

  • First we are going to install the processing node
  • Become the user jahia:
    sudo -i -u <JAHIA_LINUX_USER>
  • Retrieve the last minor version of Jahia you're using from the page Jahia . Why this version? Because it might contains a lot of bug fixes.
  • To install it:
    java -jar <DX_INSTALLER> -console
  • Then follow these actions:
    1. Accept the license
    2. Enter the path of Jahia: <DX_HOME>
    3. Choose the Custom install (advanced)
    4. By default, keep the installation of Tomcat
    5. By default, use a standalone DBMS (DataBase Management System)
    6. Choose PostgreSQL 9.X (item n°3)
    7. By default, keep the Database driver: org.postgresql.Driver
    8. Enter the Database URL related to your environment: jdbc:postgresql://<DB_SRV_FQDN>:<DB_PORT>/<DB_NAME>
    9. Enter the Database username: <DB_USER_NAME>
    10. Enter the Database password: <DB_PASSWORD>
    11. The datastore will be a shared folder so do not store binary data in the database
    12. By default, create the required database tables
    13. By default, keep the context name empty
    14. Specify the Jahia tool manager username: <DX_TOOLS_USER>
    15. Specify the Jahia tool manager password (it has to be complex for security reasons): <DX_TOOLS_PASSWORD>
    16. By default, keep the SSH console port set to 8101
    17. By default, keep the HTTP connector port set to 8080
    18. By default, keep the HTTPS redirect port set to 8443
    19. By default, keep the AJP connector port set to 8009
    20. By defaut, keep the Server shutdown port set to 8005
    21. Set the jvmRoute to the hostname, it will ease the maintenance/debugging later: <JAHIA_SRV_01>
    22. Set the Maximum heap size in MB to 6144
    23. By default, keep the Maximum PermGen size in MB to 384
    24. There is no need at this level to add any additional option to the JVM
    25. Set the operating mode to Production
    26. If not needed, do not configure an LDAP provider
    27. Activate the cluster mode by selecting the option Provide cluster configuration for this server
    28. As this is the first server of the cluster, it has to be a background job processing server. A cluster can only have one processing server otherwise it can lead to a full corruption of the data.
    29. If your server has multiple network interfaces, enter the IP that will be used to communicate with the other servers
    30. By default, keep the Port number set to 7870
    31. By default, keep the Hazelcast Port set to 7860
    32. For the Current node server ID, set it to the hostname, it will the maintenance/debugging later
    33. CSet the path to the datastore: <DX_DATASTORE>
    34. Enter the password for the root user (it needs to be complex): <DX_ROOT_PASSWORD>
    35. Confirm it
    36. The First name, Last name and E-mail are optional, put what you want
    37. Choose the Preferred language: 1 for English, 3 for French
    38. Set the Mail server to 127.0.0.1
    39. Set the Mail administrator to the team that will handle the servers: <MAIL_ADDRESS>
    40. Set the Mail from - sender to the team that will handle Jahia: <MAIL_ADDRESS>
    41. Validate the settings to be sure that Jahia will be correctly installed
    42. If LibreOffice is installed, its path should be automatically set. If it's not the case, it's should be /usr/lib64/libreoffice in CentOS
    43. The binaries of ImageMagik should be installed by default in /usr/bin
    44. Choose the default language: 41 for English, 60 for French
    45. Keep the development license provided by default
    46. No need to add a path to a folder with additional modules to be deployed
    47. By default, keep the Runtime data path: <DX_HOME>/digital-factory-data
    48. By default, keep the Configuration path: <DX_HOME>/digital-factory-config
    49. Installation should be done and here is what you should have in the console:
      [ Starting to unpack ]
      [ Processing package: Jahia + Jahia Core Content Platform (1/2) ]
      [ Processing package: Add Apache Tomcat - generate a complete installation folder (2/2) ]
      [ Unpacking finished ]
      Install was successful
      application installed on /srv/jahia
      [ Console installation done ]

Logs rotation

  • Modify the file <DX_HOME>/digital-factory-config-jahia/jahia.log4j.xml in order to replace the term DaileRollingFileAppender with FileAppender:
sed -i 's/DailyRollingFileAppender/FileAppender/' log4j.xml
  • Modify the file <TOMCAT_HOME>/conf/server.xml in order to have the following content for the ValveAccessLog:
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="access-log" suffix=".txt" rotatable="false"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
  • Below the following modification, add also this content for the RemoteIpValve:
        <Valve className="org.apache.catalina.valves.RemoteIpValve"
               remoteIpHeader="x-forwarded-for"
               proxiesHeader="x-forwarded-by"
               protocolHeader="x-forwarded-proto" 
               protocolHeaderHttpsValue="https"/>
  • Modify the file <TOMCAT_HOME>/conf/logging.properties in order to have the following content:
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

handlers = java.util.logging.ConsoleHandler

.handlers = java.util.logging.ConsoleHandler

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

1catalina.org.apache.juli.AsyncFileHandler.level = FINE
1catalina.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina.
1catalina.org.apache.juli.AsyncFileHandler.maxDays = 90

2localhost.org.apache.juli.AsyncFileHandler.level = FINE
2localhost.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost.
2localhost.org.apache.juli.AsyncFileHandler.maxDays = 90

3manager.org.apache.juli.AsyncFileHandler.level = FINE
3manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
3manager.org.apache.juli.AsyncFileHandler.prefix = manager.
3manager.org.apache.juli.AsyncFileHandler.maxDays = 90

4host-manager.org.apache.juli.AsyncFileHandler.level = FINE
4host-manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
4host-manager.org.apache.juli.AsyncFileHandler.prefix = host-manager.
4host-manager.org.apache.juli.AsyncFileHandler.maxDays = 90

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter


############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler

# For example, set the org.apache.catalina.util.LifecycleBase logger to log
# each component that extends LifecycleBase changing state:
#org.apache.catalina.util.LifecycleBase.level = FINE

# To see debug messages in TldLocationsCache, uncomment the following line:
#org.apache.jasper.compiler.TldLocationsCache.level = FINE

# To see debug messages for HTTP/2 handling, uncomment the following line:
#org.apache.coyote.http2.level = FINE

# To see debug messages for WebSocket handling, uncomment the following line:
#org.apache.tomcat.websocket.level = FINE
  • Create the file /etc/logrotate.d/jahia_logrotate with the following content:
<TOMCAT_HOME>/logs/access-log.txt {
        missingok
        su jahia jahia
        compress
        copytruncate
        rotate 14
        daily
        maxsize 500M
        dateext
        dateformat _%Y-%m-%d
}

<TOMCAT_HOME>/logs/catalina.out {
        missingok
        su jahia jahia
        compress
        copytruncate
        rotate 14
        daily
        maxsize 500M
        dateext
        dateformat _%Y-%m-%d
}

<TOMCAT_HOME>/logs/jahia_access.log {
        missingok
        su jahia jahia
        compress
        copytruncate
        rotate 14
        daily
        maxsize 500M
        dateext
        dateformat _%Y-%m-%d
}

<TOMCAT_HOME>/logs/jahia.log {
        missingok
        su jahia jahia
        compress
        copytruncate
        rotate 14
        daily
        maxsize 500M
        dateext
        dateformat _%Y-%m-%d
}

<TOMCAT_HOME>/tomcat/logs/jahia_profiler.log {
        missingok
        su jahia jahia
        compress
        copytruncate
        rotate 14
        daily
        maxsize 500M
        dateext
        dateformat _%Y-%m-%d
}
  • Check that the previous file has the correct permission 644

Jahia as a service

  • Create the file /etc/systemd/system/jahia.service with the following content:
[Unit]
Description=Jahia
After=network.target

[Service]
Type=simple

Environment=CATALINA_PID=<TOMCAT_HOME>/temp/tomcat.pid
Environment=CATALINA_HOME=<TOMCAT_HOME>
Environment=CATALINA_BASE=<TOMCAT_HOME>
Environment="CATALINA_OPTS=-server"
Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/urandom"

ExecStart=<TOMCAT_HOME>/bin/catalina.sh run
ExecStop=/bin/kill -15 $MAINPID
WorkingDirectory=<DX_HOME>

User=jahia
Group=jahia
RestartSec=10
Restart=on-failure
SuccessExitStatus=143
TimeoutSec=300

[Install]
WantedBy=multi-user.target
  • Enable the service with the following command:
systemctl enable jahia

Securing

  • Retrieve the last version of the module security-filter on our store.
    • Be aware that if you have done your developments and test without it, it is strongly advised to read and follow this documentation.
    wget https://store.jahia.com/cms/mavenproxy/private-app-store/org/jahia/modules/security-filter/1.0.2/security-filter-1.0.2.jar
  • Transfer the module to <DX_HOME>/digital-factory-data/modules:
    mv security-filter-1.0.2.jar <DX_HOME>/digital-factory-data/modules/

Bugs workaround

QA-10559

On each node, add the following line to the file <DX_HOME>/digital-factory-config/jahia/jahia.properties:

modules.purgeAtStartup = false

QA-10727

On each node, add the following line to the file <DX_HOME>/digital-factory-config/jahia/jahia.properties:

repositoryDirectoryListingDisabled = true

QA-10454

On each node, add the following line to the file <TOMCAT_HOME>/bin/setenv.sh:

CATALINA_OPTS="$CATALINA_OPTS -Dkaraf.handle.sigterm=false"

Start and check Jahia

  • Start Jahia thanks to its service:
    systemctl start jahia
  • Display the stdout logs:
    journalctl -f -u jahia
  • Wait for Jahia to be completely started, you should see that in the logs:
--------------------------------------------------------------------------------------------------
  P R O D U C T I O N   M O D E   A C T I V E
--------------------------------------------------------------------------------------------------
  Modules:
      Started: 38
--------------------------------------------------------------------------------------------------
  • If you don't have all the default modules started, it might be the consequence of a problem. If that's the case, please contact our support at https://support.jahia.com if you have a subscription.
  • Now it's time to check that we have access to the administration and to the tools:
    • Go to http://<JAHIA_SRV_01>:8080/administration and login
    • Go to http://<JAHIA_SRV_01>:8080/tools and login
      • Open the cluster view and check that everything seems to be correct: you should see in the tables the processing server with the correct IP and ports. If you have a doubt, please contact our support at https://support.jahia.com if you have a subscription.

Installation of the two other nodes

  • Stop Jahia on the processing node and make a backup thanks to an archive. The archive is used in order to preverse the attributes of the files (permissions and timestamps).
  • Transfer the archive to the two other servers
  • On each server:
    • Check that the prerequisites are fine
    • As the user jahia, extract the archive in the same folder than the processing node
    • Modify the file <DX_HOME>/digital-factory-config/jahia/jahia.node.properties
      • Property cluster.tcp.bindAddress has to be set to the IP of the server if you have multiple network interfaces
      • Property processingServer has to be uncommented and set to false
      • Property cluster.node.serverId has to be set to the hostname of the server
    • Look for the file discovery.config in a subfolder of <DX_HOME>/digital-factory-data/bundles-deployed and removed it
  • Put into place the log rotation as before
  • Put into place the service as before
  • Apply the modifications to work around the know bugs as previously
  • Start Jahia thanks to its service on each node then check the same things than before

Apache2 front-ends

The front-end will proxy the Jahia nodes thanks to the HTTP protocol. Depending at what levels the SSL is being handled (Failover and/or Apache2 and/or Tomcat), the next steps might need some changes.

  • Here is an example of the configuraiton for an Apache2 virtual host, that shoud be in /etc/httpd/sites-available/<PUBLIC_DOMAIN>.conf:
    <VirtualHost *:80>
            ServerName <PUBLIC_DOMAIN>
            DocumentRoot /var/www/vhosts/<PUBLIC_DOMAIN>/html/
    
            <Directory /var/www/vhosts/<PUBLIC_DOMAIN>/html>
                    Options -Indexes
            </Directory>
    
            CustomLog /var/log/apache2/access-<PUBLIC_DOMAIN>.log combined
            ErrorLog /var/log/apache2/error-<PUBLIC_DOMAIN>.log
    
            RewriteEngine On
            RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
            RewriteRule .* - [F]
    
            RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
    </VirtualHost>
    
    <VirtualHost *:80>
            ServerName <PUBLIC_CONTRIBUTE_DOMAIN>
            DocumentRoot /var/www/vhosts/<PUBLIC_CONTRIBUTE_DOMAIN>/html/
    
            <Directory /var/www/vhosts/<PUBLIC_CONTRIBUTE_DOMAIN>/html>
                    Options -Indexes
            </Directory>
    
            CustomLog /var/log/apache2/access-<PUBLIC_CONTRIBUTE_DOMAIN>.log combined
            ErrorLog /var/log/apache2/error-<PUBLIC_CONTRIBUTE_DOMAIN>.log
    
            RewriteEngine On
            RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
            RewriteRule .* - [F]
    
            RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
    </VirtualHost>
    
    <VirtualHost *:443>
            ServerName <PUBLIC_DOMAIN>
            DocumentRoot /var/www/vhosts/<PUBLIC_DOMAIN>/html/
    
            <Directory /var/www/vhosts/<PUBLIC_DOMAIN>/html>
                    Options -Indexes
            </Directory>
    
            CustomLog /var/log/apache2/access-<PUBLIC_DOMAIN>.log combined
            ErrorLog /var/log/apache2/error-<PUBLIC_DOMAIN>.log
    
            ProxyPreserveHost On
            ProxyRequests Off
            ProxyPass /robots.txt !
    
            ProxyPass /errors/ !
            ProxyErrorOverride On
            ErrorDocument 503 /errors/503.html
            ErrorDocument 404 /errors/404.html
            ErrorDocument 401 /cms/login
    
            ProxyPass / balancer://jahia_cluster/
            ProxyPassReverse / balancer://jahia_cluster/
            <Proxy balancer://jahia_cluster>
                    BalancerMember http://<JAHIA_SRV_FQDN_02>:8080 route=<JAHIA_SRV_02> connectiontimeout=20 timeout=300 ttl=120
                    BalancerMember http://<JAHIA_SRV_FQDN_03>:8080 route=<JAHIA_SRV_03> connectiontimeout=20 timeout=300 ttl=120                
                    ProxySet lbmethod=bytraffic stickysession=JSESSIONID|jsessionid
            </Proxy>
    
            RemoteIPHeader X-Forwarded-For
            Include ssl-common.conf
    
            # BACKLOG-5641
            Header set X-XSS-Protection "1; mode=block"
            Header set X-Content-Type-Options nosniff
            # BACKLOG-5641
            Header set X-Frame-Options: "sameorigin"
            # BACKLOG-7050
            SessionMaxAge 63072000
    
            Include protection.conf
            Include dx-security.conf
    
            # Redirect to edit mode when /cms/edit is present in the URI
            RewriteEngine On
            RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
            RewriteRule .* - [F]
            RewriteRule ^(/cms/edit/.*)$ https://<PUBLIC_CONTRIBUTE_DOMAIN>%{REQUEST_URI} [R=301,L]
    </VirtualHost>
    
    <VirtualHost *:443>
            ServerName <PUBLIC_CONTRIBUTE_DOMAIN>
            DocumentRoot /var/www/vhosts/<PUBLIC_CONTRIBUTE_DOMAIN>/html/
    
            <Directory /var/www/vhosts/<PUBLIC_CONTRIBUTE_DOMAIN>/html>
                    Options -Indexes
            </Directory>
    
            CustomLog /var/log/apache2/access-<PUBLIC_CONTRIBUTE_DOMAIN>.log combined
            ErrorLog /var/log/apache2/error-<PUBLIC_CONTRIBUTE_DOMAIN>.log
    
            ProxyPreserveHost On
            ProxyRequests Off
            ProxyPass /robots.txt !
    
            ProxyPass /errors/ !
            ProxyErrorOverride On
            ErrorDocument 503 /errors/503.html
            ErrorDocument 404 /errors/404.html
            ErrorDocument 401 /cms/login
    
            ProxyPass / balancer://jahia_cluster/
            ProxyPassReverse / balancer://jahia_cluster/
            <Proxy balancer://jahia_cluster>
                    BalancerMember http://<JAHIA_SRV_FQDN_01>:8080 route=<JAHIA_SRV_01> connectiontimeout=20 timeout=300 ttl=120
                    ProxySet lbmethod=bytraffic stickysession=JSESSIONID|jsessionid
            </Proxy>
    
            RemoteIPHeader X-Forwarded-For
            RequestHeader set X-Forwarded-Proto "https"
    
            Include ssl-common.conf
            Header set X-Robots-Tag "noindex, nofollow"
    
            # BACKLOG-5641
            Header set X-XSS-Protection "1; mode=block"
            Header set X-Content-Type-Options nosniff
            # BACKLOG-5641
            Header set X-Frame-Options: "sameorigin"
            # BACKLOG-7050
            SessionMaxAge 63072000
    
            Include protection.conf
            Include dx-security.conf
    </VirtualHost>
    
    
    

And here are some files included in this virtual host:

  • dx-security.conf: restrict the access to sensitive URLs (administration, contributing, edition, tools, etc) to some trusted IPs
    # secure admin/contrib URLs from outside our network
    Define allowed_ip "LIST_OF_SECURED_IPS"
    <LocationMatch "^/(start|cms\/admin|welcome\/adminmode|cms\/edit|cms\/contribute|cms\/studio|tools|modules\/tools|repository|server)">
        Require ip ${allowed_ip}
    </LocationMatch>
    
  • protection.conf: some standards URLs that hackers are trying to use and consequently the logs can be polluted
    <IfModule mod_rewrite.c>
     RewriteEngine On
            RewriteCond %{REQUEST_URI} ^/adminer_nq.php$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/adminer.php$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/adm.php$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/.*\.asp$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/.*\.aspx$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/[^/]+\.bak$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/cgi-bin/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/clientaccesspolicy.xml$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/crossdomain.xml$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/[^/]+\.dat$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/\.env$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/\.ftpconfig$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/\.git/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/\.git$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/Joomla/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/MySQLDumper/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/php.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/ping-fstrz$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/plus/.*\.php$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/pma/.*\.php$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/[^/]+\.rar$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/readme.html$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/SQLiteManager/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/SQLiteManager/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/SQLite/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/[^/]+\.sql$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/sql.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/sugarcrm/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/SugarCRM/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/[^/]+\.tar\.gz$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/[^/]+\.tar$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/wordpress/.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/wp-login.php$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/wp-.*$ [NC,OR]
            RewriteCond %{REQUEST_URI} ^/[^/]+\.zip$ [NC]
     RewriteRule ^.*$ - [F,L]
    </IfModule>
    
    
  • ssl-common.conf: only few directives in order to activate the SSL.
    SSLEngine on
    SSLCertificateKeyFile /etc/apache2/ssl/privkey.pem
    SSLCertificateFile /etc/apache2/ssl/fullchain.pem
    RequestHeader set X-Forwarded-Proto "https"
    
  • Once your configuration is ready, you have to activate the site with this command:
    a2ensite <PUBLIC_DOMAIN>
  • Depending on your configuration, you will need to activate the Apache2 modules.
    • Here is a list according to our example:
      • headers_module
      • lbmethod_bytraffic_module
      • mpm_prefork_module
      • proxy_module
      • proxy_balancer_module
      • proxy_http_module
      • remoteip_module
      • reqtimeout_module
      • rewrite_module
      • ssl_module
      • status_module
    • Activate the related module thanks to this command:
      a2enmod <MODULE_NAME>
  • Do not forget to run this command before reloading/restarting the Apache2 service in order to test it:
    apachectl configtest
    • If you're getting an error like Invalid command ‘XXXXX’, perhaps misspelled or defined by a module, it means that an HTTP module needs to be activated.  To correct that, you have to:
      • Go to the Apache2 documentation and look for module who handles this command/directive, we will call it <MODULE_NAME>
      • Activate the related module thanks to this command:
        a2enmod <MODULE_NAME>
      • Test again your configuration
  • If it's ok, reload the service:
    systemctl reload httpd
  • Once your Apache2 is setup, you can check a few things:
    • Connect to Jahia and check in the logs that the client IP is correct. It should be the one of the client and not the one from the web server or any network element at an upper level. To get the expected IP of the client, you can to the website What is my public IP
    • In the administration mode of Jahia, create a sample website with the server name set to the same public domain used in the virtual host and publish the website. Try to connect to it and you should have an URL like https://<PUBLIC_DOMAIN>.home.html and not something like https://<PUBLIC_DOMAIN>/cms/render/live/en/sites/<SITE_KEY>/home.html
    • Check the quality of your SSL thanks to this website: https://www.ssllabs.com/ssltest/
    • If it's not A+, please check this website in order to improve the score: https://cipherli.st/