cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
2158
Views
5
Helpful
0
Comments
Gergely Szabo
VIP Alumni
VIP Alumni

This document was inspired by this forum topic (restricted).
 

Problem: UCCX Premium and IP IVR allow the use of HTTP triggered applications, providing a great yet simple platform to create web applications. Unfortunately, there is no HTTPS support. This might raise some security concerns.

Analysis: There is no access to the configuration files to the Tomcat server on UCCX so it's not possible to turn on the HTTPS functionality. However, we can use an entity that accepts HTTPS requests from clients, transforms the query to plain HTTP and does the same transformation the other way. However, the UCCX application must be configured to accept requests from this entity only, while also checking other information as well.

Solution: The Apache HTTP Server can be configured easily as a Reverse Proxy: it accepts a HTTPS request, forwards it to UCCX using HTTP, reads the response and then transmits it over the secured connection to the client. 

Disclaimer: yes, this is the usual blah-blah. I am not responsible if something goes wrong, space pirates steal the soul of your contact center etc. This is not a bulletproof and 100% secure approach. You have been warned. 

This is the logical schema of the solution:

An "Internet client" (in light blue) requests some information served by an UCCX HTTP triggered app. Naturally, since the communication is supposed to travel across the Internet and it may contain sensitive information, we want to secure it. This means that the client is told to connect the Apache HTTP Server (in yellow), using HTTPS (on TCP port 443) - it talks to the "outside" (red) port of the server. The Apache HTTP server transforms this request and sends the information out using its "inside" port (orange) to UCCX (green), to TCP port 8080 (UCCX 7.x) or TCP port 9080 (UCCX 8.x and later), visualised as a black sphere.

Naturally, the UCCX script triggered by this request must check whether the request came from the Apache HTTP server's "inside" (orange) - plus a number of other infromation (available in the request headers).

A very important note: this is not an end to end HTTPS communication. Nothing prevents a client from creating a simple HTTP request, decorate it with the necessary HTTP request headers and make UCCX believe it came from the Reverse Proxy server. This may be seen as a weak link. However, this might be a simple task for the network administrator to completely deny traffic to UCCX's TCP port [89]080, except from traffic coming from the "internal" (orange) port from the host running the Apache HTTP server. On the other hand, we can see it as a possibility to give a somewhat easier and faster direct access to our trusted clients on the intranet (dark blue).

The next figure shows a more "visually appealing" form of the above schema:

Getting and installing Apache HTTP server is relatively easy on all operating systems, especially on Linux distributions where everything is prepackaged. In this document a Ubuntu 14.10 system would be used. It's free and it can be installed with minimum effort.

In a terminal window, issue the following commands: 

sudo apt-get install apache2

Then enable the necessary modules:
sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod headers

Then turn on the "Default SSL" site:

sudo a2ensite default-ssl.conf

And finally, restart the Apache HTTP server:

sudo service apache2 restart

Now, let's stop for a moment and think. We have installed the reverse proxy server and now we need to configure it to work correctly. How? By proxying requests arriving at an IP address - port - application combo to another IP address - port - application combo.

In this example, the Ubuntu server has got two interfaces, as seen on the above schema, one "external" (red, 192.168.56.1) and one "internal" (orange, 172.16.200.9). The UCCX server happily serves HTTP requests on its "black" interface (192.168.210.14), on port 8080 (since this is a UCCX 7.0 version system, if it were something newer, like UCCX 8.0, it would be TCP port 9080).

About the application: in this document, it's named "httpapp". So at this moment, it is available on http://192.168.210.14:8080/httpapp and it triggers a script named httpscript.aef. It's possible that there would be other HTTP enabled applications too, so what we do is simply copy the application name from the original request.

So what we need to tell our Apache HTTP server: "If a HTTPS request comes in at 192.168.56.1, port 443, asking for /httpapp then send a HTTP request to 192.168.210.14, port 8080 asking for /httpapp; wait for the HTTP response and then send it back to the original caller over HTTPS".

Easy.

We have already enabled the "Default SSL" site in Apache (using that a2ensite command). Let's edit its configuration file:

sudo nano /etc/apache2/sites-enabled/default-ssl.conf

Now I know this is a lot of text to read. It's a good read but if you are in a hurry, simply modify or create the following configuration directives:

ServerAdmin webmaster@concentra.sk
SSLCertificateFile /etc/apache2/ssl/apache.crt
SSLCertificateKeyFile /etc/apache2/ssl/apache.key        
# Uncomment the following two rows for CCX 8.0 and later
#ProxyPass /httpapp http://192.168.210.14:9080/httpapp
#ProxyPassReverse /httpapp http://192.168.210.14:9080/httpapp
# The following two rows work with CCX 7.0:
ProxyPass /httpapp http://192.168.210.14:8080/httpapp
ProxyPassReverse /httpapp http://192.168.210.14:8080/httpapp
ServerName strawberry.somesite.net
RequestHeader append IsHTTPS %{HTTPS}s
RequestHeader append CipherUsekeysize %{SSL_CIPHER_USEKEYSIZE}s

Okay, let's talk about these a bit:

ServerAdmin: this must be filled in. There are situations where something goes wrong (server blows up) and you want the users to tell you about it.

The next two rows refer to the certificate file and the key. You should already have one. If you don't, you can create a self-signed certificate using the following command:

sudo openssl req -x509 -nodes -days 30 -newkey rsa:2048 -keyout /etc/apache2/ssl/apache.key -out /etc/apache2/ssl/apache.crt

Make sure you enter the FQDN or the IP address - whatever you tell your "external" users to access this server into the Common Name (CN) field.

The most important rows are ProxyPass and ProxyPassReverse. This is the mapping from "us" to "them".

Also, we set the ServerName to something (in this case, it's "strawberry.somesite.net"). Why? This is going to be sent as a header to UCCX and the script needs it. This is just one of the things our UCCX script expects.

Also there are the two RequestHeader directives. We will take a look at them later.

After reloading the Apache server (sudo service apache2 reload) observe the logs and the console output. Should not be any problems. If there are, they must be solved before moving on to the next step.

Now let's talk about the UCCX script, named "httpscript.aef" which is triggered by the application named "httpapp", by the URL fragment "/httpapp". We are going to use a set of variables:

cipherUsekeysize: String, initial value ""   
expectedRemoteAddr: String, initial value "172.16.200.9"    
expectedXForwardedHost: String, "192.168.56.1"    
expectedXForwardedServer: String, "strawberry.somesite.net"    
isHTTPS: String, ""    
remoteAddr: String, ""    
xForwardedFor: String, ""    
xForwardedHost: String, ""    
xForwardedServer: String, ""
 
  

First, let's insert a Get HTTP Contact Info step. On its "Headers" tab, let's map the following names to the local variables:

"X-Forwarded-For":  xForwardedFor
"X-Forwarded-Host": xForwardedHost
"X-Forwarded-Server": xForwardedServer
"IsHTTPS": isHTTPS
"CipherUsekeysize": cipherUsekeysize

And on the "CGI Variables" tab, map REMOTE_ADDR to the local variable named remoteAddr.

Why is this necessary? The Get HTTP Contact Info step actually reads the HTTP request parameters coming from the "internal" (orange) interface of the reverse proxy server and in the next step, it is comparing them to the expected values.

So let's insert an If step with the following code block:

remoteAddr.contentEquals(expectedRemoteAddr) &&
xForwardedServer.contentEquals(expectedXForwardedServer) &&
xForwardedHost.contentEquals(expectedXForwardedHost) &&
Integer.valueOf(cipherUsekeysize)>=128 &&
isHTTPS.contentEquals("on")

Let's stop for a moment. What we can see is a list of conditions, and since the AND clause (&&) is used, every one of them must be met.

remoteAddr.contentEquals(expectedRemoteAddr) compares the values of remoteAddr (just came in as REMOTE_ADDR) to expectedRemoteAddr. So this means that this expression evaluates to True if the REMOTE_ADDR CGI variable is 172.16.200.9, meaning that UCCX sees the reverse proxy's "internal" (orange) port as the client.

Next, the xForwardedServer value must equal to expectedXForwardedServer and also xForwardedHost must have the same value as expectedXForwardedHost. These two values came in as HTTP request headers X-Forwarded-Server and X-Forwarded-Host. What are these headers? The mod_proxy module documentation has the detailed answer:

  • X-Forwarded-Host: The original host requested by the client in the Host HTTP request header.
  • X-Forwarded-Server: The hostname of the proxy server.

The first one is actually the IP address or the FQDN you give out to your clients accessing your "external" port (and this should also be the CN on your server certificate). The second one is whatever you tell your Apache in the ServerName configuration directive.

Our next condition is a check for older, less secure browser. It basically asks for a minimum key size of 128 bits. This is set by the Apache HTTP server, like the last condition (which actually should have come first): whether HTTPS is used.

The rest is simple: if this condition rule set is not met, then the script execution continues using the False branch, probably sending back a simple document informing the user why the request failed. If, however, the conditions are met, we can continue with the script execution on the True step.

I use the following script in this example:

This is the "Headers" tab in the Get HTTP Contact Info step:

And this is the "CGI Variables" tab in the Get HTTP Contact Info step:

Okay, let's test it now:

  • accessing https://192.168.56.1/httpapp gives me "Welcome!"
  • accessing http://192.168.210.14:8080/httpapp gives me "GO AWAY!"

This means I can no longer access my application using HTTP, bypassing the Apache HTTP Reverse Proxy.

We can also add yet another condition to the condition rule set, allowing access from the "dark blue" clients, for instance, checking the value of the remoteAddr variable and if it is from an allowed host, it would also evaluate to True. 

Enjoy.

G.

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: