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.
Jahia will be installed on a cluster of three nodes:
Each node has :
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.
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 |
Jahia runs on a Java platform so it needs the Oracle JDK to be installed on the system.
sudo yum localinstall <ORACLE_JDK_FILE_NAME>
java -version
LibreOffice is being using by Jahia for document conversion.
sudo yum install libreoffice
ImageMagick is being used by Jahia for all operations related to the images (resizing, thumbnails, etc.)
sudo yum install ImageMagick
Even if you should not need these tools, they might be useful in case of an outage.
sudo yum install maven subversion git
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.
sudo yum install logrotate
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.
sudo cat /proc/sys/vm/swappiness
sudo sysctl vm.swappiness=10
sudo swapoff -a
sudo swapon -a
/etc/sysctl.conf
:
vm.swappiness = 10
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
sudo adduser --quiet --disabled-password --disabled-login --home <DX_HOME> --gecos "" <JAHIA_LINUX_USER>
ping <DB_SRV_FQDN>
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.
sudo -u <JAHIA_LINUX_USER> touch <DX_DATASTORE>
Check that you have access to Internet
sudo -i -u <JAHIA_LINUX_USER>
java -jar <DX_INSTALLER> -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 ]
sed -i 's/DailyRollingFileAppender/FileAppender/' log4j.xml
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="access-log" suffix=".txt" rotatable="false"
pattern="%h %l %u %t "%r" %s %b" />
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-forwarded-for"
proxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto"
protocolHeaderHttpsValue="https"/>
# 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
<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
}
[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
systemctl enable jahia
wget https://store.jahia.com/cms/mavenproxy/private-app-store/org/jahia/modules/security-filter/1.0.2/security-filter-1.0.2.jar
mv security-filter-1.0.2.jar <DX_HOME>/digital-factory-data/modules/
On each node, add the following line to the file <DX_HOME>/digital-factory-config/jahia/jahia.properties:
modules.purgeAtStartup = false
On each node, add the following line to the file <DX_HOME>/digital-factory-config/jahia/jahia.properties:
repositoryDirectoryListingDisabled = true
On each node, add the following line to the file <TOMCAT_HOME>/bin/setenv.sh:
CATALINA_OPTS="$CATALINA_OPTS -Dkaraf.handle.sigterm=false"
systemctl start jahia
journalctl -f -u jahia
--------------------------------------------------------------------------------------------------
P R O D U C T I O N M O D E A C T I V E
--------------------------------------------------------------------------------------------------
Modules:
Started: 38
--------------------------------------------------------------------------------------------------
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.
<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:
# 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>
<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>
SSLEngine on
SSLCertificateKeyFile /etc/apache2/ssl/privkey.pem
SSLCertificateFile /etc/apache2/ssl/fullchain.pem
RequestHeader set X-Forwarded-Proto "https"
a2ensite <PUBLIC_DOMAIN>
a2enmod <MODULE_NAME>
apachectl configtest
a2enmod <MODULE_NAME>
systemctl reload httpd