Abiquo UI theme per subdomain

Introduction

You might wish to provide Abiquo access to different departments or organisations with a different DNS subdomain name and a customized Abiquo UI theme per subdomain. There are several ways to achieve this setup depending on the needs and resources available but this document covers a simple path with minimal resources required.

Sample Configuration

This document contains a sample configuration but as sysadmin, it is your responsibility to choose the setup that best fits your needs and resources.

Scenario

We are going to describe this setup assuming the scenario described below. Note that you will need to adjust the rest of the commands in this document to suit your environment (mostly host names and IPs).

MachineHost nameIP Address
Abiquo Monolithic & Apache2 SSL front-end

api.example.com
theme1.example.com
theme2.example.com 

192.168.1.100
Datacenter2 Remote Servicesdc2rs.example.com192.168.1.150

Setup disclaimer

  • Users must be able to reach api.example.com, theme1.example.com and theme2.example.com (i.e. DNS names must be resolvable)
  • Abiquo API and RS must be able to reach each other by DNS names

After configuring this environment, make the following changes to achieve Abiquo UI theming per subdomain.

Abiquo UI configuration

In order to provide different themes per subdomain, we need to make some changes in the default UI folder structure, which by default looks like:

# tree /var/www/html/ui/

/var/www/html/ui/
|-- config
|   |-- client-config-custom.json
|   |-- tutorials
...
|-- theme
|   |-- abicloudDefault
...

We need to create a configuration file and theme folder per subdomain. In the sample scenario the structure will look like:

# tree /var/www/html/ui/
/var/www/html/ui
|-- config
|   |-- theme1.json
|   |-- theme2.json
...
|-- theme
|   |-- abicloudDefault
...
|-- theme1
...
|-- theme2
...

You can have different property configurations in each config json file, but it is very important that the config.endpoint property points to its related API subdomain URL:

# grep endpoint /var/www/html/ui/config/*
/var/www/html/ui/config/theme1.json: "config.endpoint": "https://theme1.example.com/api",
/var/www/html/ui/config/theme2.json: "config.endpoint": "https://theme2.example.com/api",

Now place your customized themes in the Abiquo UI root folder with a suitable name and ensure that the structure is as described above.

When users load the UI, to prevent a 404 error in the browser console because the default theme.css is not found, set a default theme, which can be the empty theme.
See Abiquo+branding+guide#Set-a-default-theme-for-login-and-enterprises-without-themes.

Apache SSL front-end

Apache will provide the SSL layer to Abiquo components and serve the Abiquo UI. We need to tell Apache to work with NameVirtualHost directive because we want to serve different content depending on the accessed hostname.
Ensure that NameVirtualHost directive is enabled for both HTTP and HTTPS protocols:

# grep "NameVirtualHost" /etc/httpd/conf/httpd.conf
NameVirtualHost *:80
NameVirtualHost *:443

We need a VirtualHost configuration file per subdomain plus a VirtualHost configuration file for API and Appliance Managers:

# tree /etc/httpd/conf.d/

/etc/httpd/conf.d
|-- api.conf
|-- theme1.conf
|-- theme2.conf

First of all you need to change api.conf. This VirtualHost configuration file will group SSL access for all Abiquo Appliance Manager webapps and the API endpoint: (api.conf)

<VirtualHost *:80>
    RewriteEngine On
    RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [L,R=301]
</VirtualHost>

<VirtualHost *:443>
    ServerName api.example.com
    RewriteEngine On
    ProxyRequests Off
    ProxyPreserveHost On
    # Avoid CORS when uploading a template from different domains
    <IfModule mod_headers.c>
       SetEnvIfNoCase Origin "https?://(api\.example\.com|theme1\.example\.com|theme2\.example\.com|dc2rs\.example\.com)(:\d+)?$" AccessControlAllowOrigin=$0
       Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
    </IfModule>

    # Subdomain1 download RewriteRule
    RewriteCond %{HTTP_REFERER} ^https://theme1\.example\.com/ui/ [NC]
    RewriteCond %{REQUEST_URI} ^/(.*)/files/.*$ [NC]
    RewriteRule /(.*)/files/(.*) https://theme1.example.com/$1/files/$2 [R,L]

    # Subdomain2 download RewriteRule
    RewriteCond %{HTTP_REFERER} ^https://theme2\.example\.com/ui/ [NC]
    RewriteCond %{REQUEST_URI} ^/(.*)/files/.*$ [NC]
    RewriteRule /(.*)/files/(.*) https://theme2.example.com/$1/files/$2 [R,L]

    <Location /api>
        ProxyPass ajp://localhost:8010/api retry=0
        ProxyPassReverse ajp://localhost:8010/api
    </Location>

    # All Abiquo Appliance Managers managed in each datacenter
    # Datacenter1 Appliance Manager
    <Location /am>
        ProxyPass ajp://192.168.1.100:8010/am retry=0 timeout=1800
        ProxyPassReverse ajp://192.168.1.100:8010/am
    </Location>
 
    # Datacenter2 Appliance Manager
    <Location /am-barcelona>
        ProxyPass ajp://192.168.1.150:8010/am retry=0 keepalive=On timeout=1800
        ProxyPassReverse ajp://192.168.1.150:8010/am
    </Location>

    SSLEngine on
    SSLProtocol all -SSLv2
    SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW
    SSLCertificateFile /etc/httpd/ssl/example.com.pem
    SSLCertificateKeyFile /etc/httpd/ssl/example.com.key

    CustomLog /var/log/httpd/api-access.log combined
    ErrorLog /var/log/httpd/api-error.log
</VirtualHost>

There are a few things we would like to highlight about the api.conf:

<IfModule mod_headers.c> ... </IfModule> section: This section is intended to deal with CORS browser protection when different subdomains interact together for the Template upload functionality. As you can see, if new Datacenters or subdomains are added to the environment, this section will need to be modified to allow those new subdomains.

Subdomain X download RewriteRule section: This section is intended to deal with CORS browser protection when different subdomains interact together for the Template download functionality. As you can see, if new subdomains are added to the environment, you will need to create the corresponding RewriteCond and RewriteRule to allow the download.

Datacenter X Appliance Manager section: This section is intended to provide the SSL layer to the Appliance Manager webapp in all Datacenters. If the Abiquo UI is running SSL, the Appliance Manager endpoint should also be accessed through SSL to avoid the browser's Mixed-Content protection. Because of this, is a good practice to proxy all Appliance Manager requests through the Apache2 front-end, which will provide the SSL layer. As you can see, if new Datacenters are added to the environment, you will need to create the corresponding Location section for the new Datacenter Appliance Manager.

There are other sections and parameters such as the certificate configuration, Apache log files and ProxyPass extra options such retry, keepalive and timeout that can be modified depending on your environment. Refer to Apache website documentation for further information.


Now, we should create a VirtualHost configuration file per subdomain. All files will look almost the same:

theme1.conf

<VirtualHost *:80>
    RewriteEngine On
    RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [L,R=301]
</VirtualHost>

<VirtualHost *:443>
    ServerName theme1.example.com
    RewriteEngine On
    ProxyRequests Off
    ProxyPreserveHost On

    <Directory "/var/www/html/ui">
        Options MultiViews
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>
    RewriteRule ^/$ /ui/ [R]
 
    # Theme and config AliasMatch
    AliasMatch ^/ui/theme/default/(.*)$ /var/www/html/ui/theme1/$1
    AliasMatch ^/ui/config/client-config-custom.json /var/www/html/ui/config/theme1.json

    <Location /api>
        ProxyPass ajp://192.168.1.100:8010/api retry=0
        ProxyPassReverse ajp://192.168.1.100:8010/api
    </Location>

    <Location /m>
        ProxyPass ajp://192.168.1.100:8010/m retry=0
        ProxyPassReverse ajp://192.168.1.100:8010/m
    </Location>

    <Location /am>
        ProxyPass ajp://192.168.1.100:8010/am retry=0 timeout=1800
        ProxyPassReverse ajp://192.168.1.100:8010/am
    </Location>

    <Location /am-barcelona>
        ProxyPass ajp://192.168.1.150:8010/am retry=0 keepalive=On timeout=1800
        ProxyPassReverse ajp://192.168.1.150:8010/am
    </Location>

    <Location /legal>
        ProxyPass ajp://192.168.1.100:8010/legal retry=0
        ProxyPassReverse ajp://192.168.1.100:8010/legal
    </Location>

    SSLEngine on
    SSLProtocol all -SSLv2
    SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW
    SSLCertificateFile /etc/httpd/ssl/example.com.pem
    SSLCertificateKeyFile /etc/httpd/ssl/example.com.key

    CustomLog /var/log/httpd/theme1-access.log combined
    ErrorLog /var/log/httpd/theme1-error.log
</VirtualHost>


theme2.conf

<VirtualHost *:80>
    RewriteEngine On
    RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [L,R=301]
</VirtualHost>

<VirtualHost *:443>
    ServerName theme2.example.com
    RewriteEngine On
    ProxyRequests Off
    ProxyPreserveHost On

    <Directory "/var/www/html/ui">
        Options MultiViews
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>
    RewriteRule ^/$ /ui/ [R]
 
    # Theme and config AliasMatch
    AliasMatch ^/ui/theme/default/(.*)$ /var/www/html/ui/theme2/$1
    AliasMatch ^/ui/config/client-config-custom.json /var/www/html/ui/config/theme2.json

    <Location /api>
        ProxyPass ajp://192.168.1.100:8010/api retry=0
        ProxyPassReverse ajp://192.168.1.100:8010/api
    </Location>

    <Location /m>
        ProxyPass ajp://192.168.1.100:8010/m retry=0
        ProxyPassReverse ajp://192.168.1.100:8010/m
    </Location>

    <Location /am>
        ProxyPass ajp://192.168.1.100:8010/am retry=0 timeout=1800
        ProxyPassReverse ajp://192.168.1.100:8010/am
    </Location>

    <Location /am-barcelona>
        ProxyPass ajp://192.168.1.150:8010/am retry=0 keepalive=On timeout=1800
        ProxyPassReverse ajp://192.168.1.150:8010/am
    </Location>

    <Location /legal>
        ProxyPass ajp://192.168.1.100:8010/legal retry=0
        ProxyPassReverse ajp://192.168.1.100:8010/legal
    </Location>

    SSLEngine on
    SSLProtocol all -SSLv2
    SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW
    SSLCertificateFile /etc/httpd/ssl/example.com.pem
    SSLCertificateKeyFile /etc/httpd/ssl/example.com.key

    CustomLog /var/log/httpd/theme2-access.log combined
    ErrorLog /var/log/httpd/theme2-error.log
</VirtualHost>

The only differences are the ServerName directive and the AliasMatch rules required to apply the desired theme per subdomain:

theme1.example.com

ServerName theme1.example.com
AliasMatch ^/ui/theme/default/(.*)$ /var/www/html/ui/theme1/$1
AliasMatch ^/ui/config/client-config-custom.json /var/www/html/ui/config/theme1.json

theme2.example.com

ServerName theme2.example.com
AliasMatch ^/ui/theme/default/(.*)$ /var/www/html/ui/theme1/$1
AliasMatch ^/ui/config/client-config-custom.json /var/www/html/ui/config/theme2.json

Abiquo.properties configuration

You only need to ensure that the abiquo.server.api.location property points to the common API URL on the Abiquo API Server and the Abiquo Remote Services:

# grep api.location /opt/abiquo/config/abiquo.properties
abiquo.server.api.location = https://api.example.com/api

Add certificates to Tomcat trust store

In the sample setup we used a wildcard certificate which enables us to use the same certificate file for all VirtualHost configuration files. However, if you are using different certificates for each DNS name, you will need to specify the corresponding certificate, key and CA files in the Apache VirtualHost configuration files.

We will need to add the corresponding certificates and CA to all Abiquo Tomcat trust store files. You must follow these steps:

  • Find and backup original cacerts file
  • Download certificates
  • Add certificates and CA to Tomcat trust store (cacerts file)
  • Restart abiquo-tomcat service

In the sample environment we've performed the following steps to both Abiquo Monolithic server and Datacenter 2 Abiquo Remote Services node:

#1# find / -iname cacerts
#2# cp /usr/java/jdk1.7.0_21/jre/lib/security/cacerts /root/cacerts.201410011200
#3# echo -n | openssl s_client -connect api.example.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > example.com.cert
#4# keytool -import -file example.com.cert -alias example.com -keystore /usr/java/jdk1.7.0_21/jre/lib/security/cacerts
#5# service abiquo-tomcat restart

If you are not using a wildcard SSL certificate, repeat steps #3# and #4# for each DNS name involved in the environment. (In the sample scenario: theme1.example.com, theme2.example.com, api.example.com and dc2rs.example.com)


Having done the steps indicated above, you can now access https://theme1.example.com and https://theme2.example.com with each having a different theme.


Unable to render {include} The included page could not be found.