1.3. Adjustments (optional)

1.3.1. Enable SSL/HTTPS

Enabling SSL is highly recommended on live systems, as it protects sensitive information, like passwords, to be sent plain over the Internet, making it readable by eavesdroppers. If the Tomcat server is directly used from the Internet, to enable SSL / HTTPS you first have to enable (un-comment) the https connector in the file <tomcat_home>/conf/server.xml

<Connector port="443" maxHttpHeaderSize="8192"
    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
    enableLookups="false" disableUploadTimeout="true"
    acceptCount="100" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS" />

Generate a key with the keytool from Java:

$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA -keystore /path/to/my/keystore

After executing this command, you will first be prompted for the keystore password. Passwords are *case sensitive*. You will also need to specify the custom password in the server.xml configuration file, as described later. Next, you will be prompted for general information about this Certificate, such as company, contact name, and so on. This information will be displayed to users who attempt to access a secure page in your application, so make sure that the information provided here matches what they will expect. Finally, you will be prompted for the key password, which is the password specifically for this Certificate (as opposed to any other Certificates stored in the same keystore file). You MUST use the same password here as was used for the keystore password itself. (Currently, the keytool prompt will tell you that pressing the ENTER key does this for you automatically). If everything was successful, you now have a keystore file with a Certificate that can be used by your server.

1.3.2. Adjust Tomcat/Java memory

The default memory heap size of Tomcat is very low. You can augment this in the following way:

Windows

In the bin directory of Tomcat create (if it doesn't exist) a file called setenv.bat, edit this file and add the following line:

set JAVA_OPTS=-Xms128m -Xmx512m -XX:MaxPermSize=128M

Linux

In the bin directory of Tomcat create (if it doesn't exist) a file called setenv.sh, edit this file and add the following line:

JAVA_OPTS="-Xms128m -Xmx512m -XX:MaxPermSize=128M"

1.3.3. Clustering

Clustering is useful both for scaling (serving more requests) and for high availability (if a server crashes, the application continues to run). The main reason for configuring a cluster in Tomcat is to replicate HTTP sessions. Cyclos, however, doesn't use Tomcat sessions, but handles them internally. This way, there is no special Tomcat configuration to support a Cyclos cluster.

The Cyclos application, however, needs some small configurations to enable clustering. Cyclos uses Hazelcast to synchronize aspects (such as caches) between cluster servers. To enable clustering, find in cyclos.properties the line containing cyclos.clusterHandler, and set it to hazelcast.

Some extra configuration can be performed in the WEB-INF/classes/hazelcast.xml file. Basically, if the local network runs more than a single Cyclos instance, the group needs to be configured. Configure all files belonging to the same group with the same group name and password. It is also possible to change the default multicast to TCP/IP communication. Just comment the <multicast> tag and uncomment the <tcp-ip> tag, setting up the hosts / ports which will be part of the cluster. For a TCP/IP cluster, Hazelcast needs the host name / port of at least one node already in a cluster (it is not necessary to set all other nodes on each node).

To setup high-availability at database (Postgresql) level, please, refer to this document.

1.3.4. Use Apache as frontend for Tomcat

You can use apache as a front-end / load balancer for the tomcat. This is very usefull when you have several domains configured on the server. There are several documentations and examples available on the internet, in our example we will use the mod_jk library for apache.

sudo apt-get install apache2 libapache2-mod-jk

The configuration is done on the file /etc/libapache2-mod-jk/workers.properties. By default this is configured to use the AJP port 8009, this is the default ajp port for tomcat, if you are using a different port you need to configure here.

On tomcat we need to enable the ajp connector. Edit the file tomcat/conf/server.xml and uncomment the AJP connector:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Now on apache we need to configure the virtualhost to use the ajp connector. On the virtualhost of your domain add the following lines:

        <IfModule mod_jk.c>
            JkMount /* ajp13_worker
            JkMount / ajp13_worker      
        </IfModule>            
        

This example uses the cyclos as ROOT application on tomcat. If you want to use something like http://www.yourdomain.com/cyclos we need to deploy cyclos on the webapps/cyclos directory and configure apache like this:

        <IfModule mod_jk.c>
            JkMount /cyclos/* ajp13_worker
            JkMount /cyclos ajp13_worker    
        </IfModule>            
        

Now restart both apache and tomcat and check if it works.

1.3.5. Enable SSL on apache

Enabling SSL is highly recommended on live systems, as it protects sensitive information, like passwords, to be sent plain over the Internet, making it readable by eavesdroppers. If you are using apache as a front-end for the tomcat first you need to enable the ssl module.

sudo a2enmod ssl

After enable the module we need to configure the virtualhost to use the ssl. On the virtualhost of your domain add the following lines:

NameVirtualhost www.yourdomain.org:443
<VirtualHost www.yourdomain.org:443>
ServerAdmin youremail@yourdomain.org
ServerName www.yourdomain.org
DocumentRoot /var/www/

<Directory />
        Options FollowSymLinks
        AllowOverride All
</Directory>
<Directory /var/www/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
</Directory>

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
        AllowOverride None
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
        Order allow,deny
        Allow from all
</Directory>

ErrorLog /var/log/apache2/domain_error.log
LogLevel warn
CustomLog /var/log/apache2/domain_access.log combined

<IfModule mod_ssl.c>
       SSLEngine on
       SSLProtocol ALL -SSLv2 -SSLv3
       SSLHonorCipherOrder On
       SSLCipherSuite ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH
       ServerSignature Off
       BrowserMatch ".*MSIE.*" \
        nokeepalive ssl-unclean-shutdown \
        downgrade-1.0 force-response-1.0
       SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
       SSLCertificateFile /etc/ssl/certs/yourcertificate.crt
       SSLCertificateKeyFile /etc/ssl/private/yourkey.key        
</IfModule>
</VirtualHost>            
        

Now we need to generate the certificate, in this example we will use a self-signed certificate, normally used to test your new SSL implementation

Generate a Private Key

The utility "openssl" is used to generate the key and CSR. This utility comes with the OpenSSL package and is usually installed under /usr/local/ssl/bin. If the utility was installed elsewhere, these instructions will need to be adjusted accordingly.

Type the following command at the prompt:

        openssl genrsa -des3 -out yourkey.key 2048

Generate a CSR (Certificate Signing Request)

Once the private key is generated a Certificate Signing Request can be generated. The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate. The second option is to self-sign the CSR, which will be demonstrated in the next section.

During the generation of the CSR, you will be prompted for several pieces of information. These are the X.509 attributes of the certificate. One of the prompts will be for "Common Name (e.g., YOUR name)". It is important that this field be filled in with the fully qualified domain name of the server to be protected by SSL. If the website to be protected will be https://public.akadia.com, then enter public.akadia.com at this prompt. The command to generate the CSR is as follows:

            openssl req -new -key yourkey.key -out yourcertificate.csr
        
            Country Name (2 letter code) [GB]:CH
            State or Province Name (full name) [Berkshire]:Bern
            Locality Name (eg, city) [Newbury]:Oberdiessbach
            Organization Name (eg, company) [My Company Ltd]:Akadia AG
            Organizational Unit Name (eg, section) []:Information Technology
            Common Name (eg, your name or your server hostname) []:public.akadia.com
            Email Address []:martin dot zahn at akadia dot ch
            Please enter the following extra attributes
            to be sent with your certificate request
            A challenge password []:
            An optional company name []:
       

Remove Passphrase from Key

One unfortunate side-effect of the pass-phrased private key is that Apache will ask for the pass-phrase each time the web server is started. Obviously this is not necessarily convenient as someone will not always be around to type in the pass-phrase, such as after a reboot or crash. mod_ssl includes the ability to use an external program in place of the built-in pass-phrase dialog, however, this is not necessarily the most secure option either. It is possible to remove the Triple-DES encryption from the key, thereby no longer needing to type in a pass-phrase. If the private key is no longer encrypted, it is critical that this file only be readable by the root user! If your system is ever compromised and a third party obtains your unencrypted private key, the corresponding certificate will need to be revoked. With that being said, use the following command to remove the pass-phrase from the key:

    cp yourkey.key yourkey.key.org
    openssl rsa -in yourkey.key.org -out yourkey.key
    

The newly created yourkey.key file has no more passphrase in it.

Generating a Self-Signed Certificate

At this point you will need to generate a self-signed certificate because you either don't plan on having your certificate signed by a CA, or you wish to test your new SSL implementation while the CA is signing your certificate. This temporary certificate will generate an error in the client browser to the effect that the signing certificate authority is unknown and not trusted.

To generate a temporary certificate which is good for 365 days, issue the following command:

    openssl x509 -req -days 365 -in yourcertificate.csr -signkey yourkey.key \
    -out yourcertificate.crt
    

Installing the Private Key and Certificate

When Apache with mod_ssl is installed, it creates several directories in the Apache config directory. The location of this directory will differ depending on how Apache was compiled.

    cp yourcertificate.crt /etc/ssl/certs/yourcertificate.crt
    cp yourkey.key /etc/ssl/private/yourkey.key
    

Now restart apache and check if it works.

1.3.6. Configuring Cyclos to work behind a proxy / load balancer

The easiest configuration for a load balancer is Apache connecting to Tomcat using the AJP protocol. In this case, the original request is forwarded to Tomcat as is, keeping the original client IP address and URL. However, in most other cases, the load balancer works as a proxy, sending a new HTTP to Tomcat and forwarding the response to the client. Examples of such proxies include Apache with mod_proxy, Nginx, haproxy and Amazon's Elastic Load Balancing. In either way, generally the proxy will have the server certificate and will terminate the SSL connection with the client. The second request, to Tomcat, will generally use a plain HTTP connection.

That means the client IP address received by Cyclos, as well as the request URL are different than the original request performed by the client. As Cyclos uses the client IP for logging and blocking in case of abuse, this would lead Cyclos to block the proxy, preventing any further request. However, the proxy will add some extra request headers, with information about the original request. Cyclos then needs to be configured to read both the IP address and which was the connection protocol used by the original request (HTTP or HTTPS) from those headers, instead of directly from the incoming HTTP request. As such, the following settings in cyclos.properties are needed:

  • cyclos.header.remoteAddress: Specifies the name of the header which contains the original client's IP address. The name of this header is usually X-Forwarded-For.
  • cyclos.header.protocol: Specifies the protocol name (http or https) used on the original request. The name of this header is usually X-Forwarded-Proto.

The following cases are handled by Cyclos to match a specific network / configuration from the request URL:

  • A request to Tomcat using the root URL specified in a parent configuration (normally the network default). For example, if the network default configuration's root URL is http://cyclos-net.com, any requests to http://cyclos-net.com/* will match that configuration;
  • A request to Tomcat using the root URL specified in a parent configuration plus a specific path of an inherited configuration. Following the previous example, if the child configuration has a custom path of config, any requests to http://cyclos-net.com/config/* will match that configuration;
  • If no custom URL is matched, requests having first subpath (after the web application context path) equals the network internal name. For example, if Cyclos is deployed in a Tomcat under the context path cyclos and it has a network called main, any requests to http://localhost:8080/cyclos/main/* will match this network;
  • Same as previous, but with a specific configuration path. Following the previous example, if that network has a configuration with path config, requests to http://localhost:8080/cyclos/main/config/* will match this configuration;
  • Also, the name global is reserved as a network internal name. For example, requests to http://localhost:8080/cyclos/global/* will be considered in global mode;
  • Still if no custom URL is matched, if no network internal name is given and there is a default network, the network internal name can be omitted. For example, requests to http://localhost:8080/cyclos/* will be considered in the default network.

When Tomcat is behind a proxy, probably it will never receive requests using the original public URL. Hence, only matching by network (and optionally, configuration paths) will be used. Still, networks should correctly set the root url in their configurations because they are used to generate full URLs in the server side, for example, when sending links in e-mails or resolving image URLs in rich texts.

Following are common cases that could be configured for a proxy, all assuming Cyclos is deployed to a Tomcat accessible by the proxy via http://tomcat:8080/cyclos:

  • The proxy handles the (sub)domain of a specific network. For example, it handles requests to https://main.my-cyclos.com and forwards them to http://tomcat:8080/cyclos/main. The network in Cyclos must have the internal name main in this case;
  • The proxy handles a specific (sub)domain for the global mode. For example, it handles requests to https://global.my-cyclos.com and forwards them to http://tomcat:8080/cyclos/global;
  • The proxy handles a multi-network entry point on a (sub)domain of a specific network. For example, it handles requests to https://www.my-cyclos.com and forwards them to http://tomcat:8080/cyclos. So, requests to https://www.my-cyclos.com/main will match the main network, while requests to https://www.my-cyclos.com/alt will match the alt network.

1.3.6.1. Reserved names that cannot be used in proxy paths

There are reserved paths in Cyclos that cannot be used as path in proxies. For example, a proxy could handle requests to https://www.my-project.com/app and redirect them to http://tomcat:8080/cyclos. In this case, the /app path part is public, used by clients, but never visible on Tomcat. To handle such cases, the list of reserved paths is used to generate the correct URIs for scripts and stylesheets, and having the any of the reserved paths in the proxy would prevent the URI generation from working correctly.

The list of reserved paths is:

cyclos
cyclos.gwt
fonts
js
pay
robots.txt
sitemap.xml
sitemap-index.xml
sitemap.xstl
network-information-id.html
activate-access-client
external-redirect-callback
run
content
web-rpc
java-rpc
api
sms
push-notifications
global

1.3.7. Enabling Google Maps

Cyclos supports displaying maps using Google Maps. This has to be enabled in the Cyclos configuration. Starting in June 2016, the Google Maps requires API Keys in order to use. For details on the free daily quota for map views and geocode requests, see this page. As of June 2016, the free quota is 25,000 map views and 2,500 geocode requests. Cyclos uses the geocode requests to map the user-informed address fields to a position (latitude/longitude).

There are 2 API keys that can be set in the Cyclos configuration: The server-side API key and the browser API-key. Each one needs to be generated in the API Manager.

  • Enabling the APIs: on the "Library" menu, search for the following APIs and enable them: "Google Maps JavaScript API" and "Google Maps Geocoding API";
  • Creating the API keys: on the "Credentials" menu, choose "Create credentials", then choose "API key". Choose "Server key" and specify a name for it. Save and then create a new one, this time as "Browser key".

Once the API keys are ready, they can be copied / pasted into the Cyclos configuration, on the corresponding "Google maps server API" and "Google maps browser API" fields. The API Manager allows monitoring of requests performed by each API.

On the main Cyclos web application, addresses are geolocated on the client-side, before saving it. In such cases, the browser API key is used. However, sometimes addresses can be saved without being geolocated, either by thirdy-party software or by importing new users into Cyclos. In such cases, a background task will attempt to geolocate them (might take up to 24 hours for this) using the server API key.

1.3.8. External content storage

1.3.8.1. Storage types

Starting with Cyclos 4.5 you have the possibility to configure the storage type to be used for images, documents and imported files.

Cyclos comes with three implementations out of the box:

  • Database: the content is stored in conjunction with all data in the database. This is the default implementation.
  • File system: the content is stored outside the database in a specific path.
  • Amazon S3: Amazon Simple Storage Service, the content is stored outside the database in a specific bucket.

Besides the built-in implementations you can create your own custom implementation. To do that you must create a Java class implementing org.cyclos.impl.storage.StoredFileContentManager

The followins are the properties you need to configure in the cyclos.properties

Storage type property

Database storage specific properties

There are no additional properties to be configured.

File system storage specific properties

  • cyclos.storedFileContentManager.rootDir: the root directory where the contents will be stored.
  • cyclos.storedFileContentManager.maxSubDirs: tha maximum count of diretories to be created below the root directory where the content will be stored.

Amazon S3 storage specific properties

  • cyclos.storedFileContentManager.bucketName: the name of the bucket to be created.
  • cyclos.storedFileContentManager.accessKeyId: the AWS access key.
  • cyclos.storedFileContentManager.secretAccessKey: the AWS secret access key.

1.3.8.2. Storage migrator utility class

If you already have a running Cyclos instance and want to change the storage type to use then there is an utility class that will allow to migrate the contents from the current storage to a new one. To use it you must have Java configured in your path then go to the <TOMCAT_DIR>/webapps/<cyclos_dir> directory and execute:

java -cp "WEB-INF/classes:../../lib/*:WEB-INF/lib/*" \
             org.cyclos.impl.storage.utils.StoredFileContentMigrator

and just follow the instructions shown in the usage help.

1.3.9. Report fonts

It is possible to customize the font family used by Cyclos when exporting reports to PDF. For example, two different font files are needed to support Chinese and Arabic characters. By default Cyclos bundles the following subset of Google Noto fonts:

  • Noto Sans (for Latin and Greek)
  • Noto Kufi Arabic (for Arabic)
  • Noto Sans CJK (for Chinese)

In case additional fonts are required, please, follow these steps:

  • Backup the jasperreports-fonts-x.x.x.jar located on the cyclos-4.x.x/web/WEB-INF/lib directory. Unpack the contents in another folder (for example, fonts);
  • Download the TTF font you will use. It is recommended to use other variation of the Google Notofont;
  • Make sure the font supports basic Latin characters, or characters like numbers won't be printed out. If not, merge the downloaded file (both the regular and bold variations) with the NotoSans-{Regular|Bold}.ttf file. A possible tool for that is a python library called fonttools. It includes the pyftmerge script for merging two TTF files;
  • Add the new TTF files to the fonts folder. Your fonts folder should now look like this:
    • fonts.xml
    • jasperreports_extension.properties
    • NotoSans-Bold.ttf
    • NotoSans-Regular.ttf
    • (other variations)
    • NewFont-Bold.ttf (added)
    • NewFont-Regular.ttf (added)
  • Modify the fonts.xml file adding the correponding section. For example, for the Japanese language:
                    <?xml version="1.0" encoding="UTF-8"?>
                    <fontFamilies>
                    
                       <fontFamily name="CyclosFont">
                           <normal><![CDATA[NotoSans-Regular.ttf]]></normal>
                           <bold><![CDATA[NotoSans-Bold.ttf]]></bold>
                           <pdfEncoding><![CDATA[Identity-H]]></pdfEncoding>
                           <pdfEmbedded><![CDATA[true]]></pdfEmbedded>
                       </fontFamily>
                    
                       <fontFamily name="CyclosFont_ja">
                           <normal><![CDATA[NewFont-Regular.ttf]]></normal>
                           <bold><![CDATA[NewFont-Bold.ttf]]></bold>
                           <pdfEncoding><![CDATA[Identity-H]]></pdfEncoding>
                           <pdfEmbedded><![CDATA[true]]></pdfEmbedded>
                       </fontFamily>
                    </fontFamilies>
                    

    The name of the font family needs to follow the exact syntax: CyclosFont_[lang iso code]_[country iso code]. [country iso code] is optional. When exporting to PDF, Cyclos will resolve the language and country of the logged user and set the most specific font. So if the logged user's locale settings resolves to Japanese / Japan (ja_JP), Cyclos will try to find a font in the following order:

    • CyclosFont_ja_JP
    • CyclosFont_ja
    • CyclosFont

    The font file name needs to have a ttf extension. If you have a font with an otf extension, just rename it to ttf.

  • Modify the jasperreports_extension.properties and add the corresponding line at the end of the file, it should look like this:
                net.sf.jasperreports.extension.registry.factory.fonts=
                net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
                net.sf.jasperreports.extension.simple.font.families.CyclosFont=fonts.xml
                net.sf.jasperreports.extension.simple.font.families.CyclosFont_ja=fonts.xml
                
  • Pack the files (inside the fonts folder) in a jar (is just a zip file with the renamed extension). Place this file in cyclos-4.x.x/web/WEB-INF/lib directory, and remove the other jasperreports-fonts-x.x.x.jar file. Be sure to keep the original file structure of the jar.