菜单

TYPO3 Backend Via SSL Proxy

2010年07月26日 - typo3
TYPO3 Backend Via SSL Proxy

Breaking news: TYPO3 4.2 has reverse proxy support

If you use TYPO3 version 4.2.x, you don’t need the patch below any more. Masi has added some code to the core to get it work. New options are available in the install tool:

  • reverseProxyIP: list of IP addresses. If TYPO3 is behind one or more (intransparent) reverese proxies the IP addresses must be added here
  • reverseProxyHeaderMultiValue: “none”,”first”,”last”: defines which values of a proxy header (eg HTTP_X_FORWARDED_FOR) to use, if more than one is found. “none” discards the value, “first” and “last” use the first/last of the values in the list.
  • reverseProxyPrefix: optional prefix to be added to the internal URL (SCRIPT_NAME and REQUEST_URI).
  • reverseProxySSL: ‘*’ or list of IP addresses of proxies that use SSL (https) for the connection to the client, but an unencrypted connection (http) to the server. If ‘*’ all proxies defined in SYS[proxyIP] use SSL.
  • reverseProxyPrefixSSL: prefix to be added to the internal URL (SCRIPT_NAME and REQUEST_URI) when SSL accessing the server via an SSL proxy. This setting overrides SYS[proxyPrefix].

Check bug 7397 on the bugtracker for further details. (I currently don’t have time to add details here.)

Abstract

This is a tutorial on how to enable the TYPO3 backend to be accessed via a SSL proxy through manual changes to the TYPO3 php file class.t3lib_div.php. At this time the changes suggested here are not meant to be used on a productive environment. The code changes weren’t tested by many users yet so it is possible that there are hidden errors in it. It is planned to integrate the ideas from patches that other TYPO3 developers have already provided on the TYPO3 bugtracker regarding reverse proxies. Finally it should hopefully lead to code that can be integrated into the TYPO3 core.

If you have comments, questions or ideas regarding this tutorial please add a comment at the end of this page.

I started writing this tutorial on January 09, 2007. It was last updated on September 10, 2008. I regret the tutorial is not available in German language.

Introduction

Many web hosting service providers offer low-cost web space packages that are good enough to host a few small websites based on TYPO3. One drawback of a low-cost solution is that encryption of the communication between client and server via SSL/HTTPS is not (fully) included. The webmaster of a webspace package either has only limited freedom of configuring the web server – so that he can’t set it up for HTTPS, or he may not be willing to pay the amount of money that an individual SSL certificate will cost.

Some web hosting agencies offer so called SSL proxies to be used as an alternative to a full-featured encryption solution with an own SSL certificate. A small hobby website project may never ask its users to submit credit card numbers or other personal information, so SSL encryption is not necessarily must-have in that regard. But the adminstrative interface of the content management system – in our case the TYPO3 backend – seems to be a sensible part of the website. If username and password for a backend user’s account or the admin-password or the install tool password get’s captured in some way by another person, this might involve some sleepless nights for the webmaster.

TYPO3 is of course able to deal with SSL/HTTPS, there is core functionality for this and there are even TYPO3 extensions like https_enforcer too that make it easier to set up some frontend pages to be only accessed encrypted. But something is still missing: Support for SSL proxies (or more general: reverse proxies) is not included yet in the TYPO3 core and not available as an extension. Until now it is not possible for owners of low cost webspace to have this out-of-the-box without any further modifications of the setup. Or to put it in other words: The TYPO3 backend does not work via a SSL proxy.

Let’s change it.

If you want to follow the steps of this tutorial to see how the suggested code change works for you, please give me a feedback via a comment or an email. Please tell me if it works for you – or what does not. Without feedback the coding will not be improved and will therefore probably not integrated into the core.

Important notes

Before you read this tutorial or try to implement any code shown here to your own TYPO3 installation please keep the following notes in mind:

What is a SSL proxy server?

A SSL proxy server is a form of reverse proxy server that encrypts the webpages using SSL before delivering it. In this tutorial I will use the terms “reverse proxy” and “SSL proxy” as synonyms. Keep in mind that you can use reverse proxies for other purposes too.

Learn more about proxy servers here:

http://en.wikipedia.org/wiki/Proxy_server

There are plans to improve the performance of the website typo3.org through reverse proxy servers. This has nothing to do with SSL proxies, but if you are interested, please check this web page:

http://wiki.typo3.org/index.php/TYPO3.org_-_improvements

Status Quo: TYPO3 doesn’t notice a reverse proxy

The checks of TYPO3 regarding the settings of environmental variables don’t notice if a page was requested through a SSL or reverse proxy. Basically the value of HTTP_HOST stays the same regardless if the web page delivered by TYPO3 was requested through a reverse proxy or not. Since TYPO3 trusts the value of HTTP_HOST it has no chance to notice the presence of a SSL proxy. Because of that the links and HTTP redirects of TYPO3 are generated wrongly. The user is redirected to a non-HTTPS page or stuck in a loop.

Luckily we are able to detect the presence of a reverse proxy through the php $_SERVER environment variables.

Learn more about your SSL proxy server

Even if you know that your web hoster offers a SSL proxy it is not clear which proxy server product is used. It will most likely be Apache/mod_proxy, but there is a chance that it might be a different product. You should find out about it because not all products can be detected in the same way.

If it is not documented which kind of SSL proxy server is used you may be able to detect the type of the reverse proxy through the HTTP header that the proxy sends you if you request a page that does not exist.

If you use Mozilla Firefox you can install the browser extension LiveHTTPHeaders to  view the HTTP header. Example: Your SSL proxy host name is

ssl.mysslproxy.com

Normally you reach your website through the proxy via

https://ssl.mysslproxy.com/www.mydomain.com

But to see the SERVER-String of the proxy you have to request a page from the proxy that is not part of your own domain (or any other domain). You just want to be sure that the page you request now comes straight from the proxy and is not passed through the proxy from another web server. The safest solution is to ask for a page that does not exist. It could look like this:

https://ssl.mysslproxy.com/this-page-definitly-doesnt-exist.html

Use Firefox with LiveHTTPHeaders to request the described kind of non existing page. Right click on the web page (probably a 404 error message) and choose “View Page Info” from the context menu. The extension has inserted a tab called “Header” into the tab strip. Within this tab you will find the table “Response”. In there hopefully is a row with the parameter “SERVER” and a value for it.

If you’re lucky you will find out that the proxy server is based on Apache/mod_proxy with OpenSSL. Then it should like this line:

Apache/x.x.x (Unix) Debian GNU/Linux mod_ssl/x.x.x OpenSSL/x.x.x

(The x’s are placeholders for version numbers.)

Detect Apache/mod_proxy using PHP

The Apache module mod_proxy adds three lines to each HTTP header that is sent to the ordinary web server as part of each request.  Those requests contain (amongst others) three special parameters in the HTTP header (the values of these parameters are explained below):

If you are curious where these lines come from please take a look at the source code of the Apache module proxy/mod_proxy_http. Search for the string “X-Forwarded”, for example in the file mod_proxy_http.c.

If these HTTP headers are present, the ordinary web server turns them into three server variables to make them accessible for us through the PHP array $_SERVER. We have to use these array entries to detect the presence of the Apache based reverse proxy:

But we must not trust the assumption that the request came from our reverse proxy just because these three parameters are there and their values look all right. The content of these values can easily be faked using any other reverse proxy server or – even easier – using tools to generate HTTP requests like cURL.

To make sure that only the expected reverse proxy is talking with our TYPO3 system we have to check if the IP address in REMOTE_ADDR is the one of our proxy server.

However we are not able to see if the reverse proxy encrypted the web page before it was delivered. (This is important, because we have to be aware to not only think of the protocol HTTPS, because it could be just an HTTP reverse proxy.)

Other proxy server products (Squid, Varnish, …)

Besides Apache/mod_proxy there are many other proxy server products that are dedicated reverse proxy servers (Varnish) or may be used as a reverse proxy (Squid). But when a SSL proxy is needed, Apache/mod_proxy will most likely be the choice. Sqid and Varnish don’t support HTTPS natively, other tools have to be used in addition to achieve that.

My impression is: The HTTP header entries created by Apache/mod_proxy I listed above may not be the same using other proxy servers. So the code snippet below may only work if Apache/mod_proxy is used as a proxy server.

The header “X-Forwarded-For” seems to be used by all three proxy servers. But it seems that Squid and Varnish don’t use “X-Forwarded-Host” and “X-Forwarded-Server”. For further information please check the Squid sources, especially the file  HttpHeader.c, or the source code of Varnish.

What needs to be changed in TYPO3?

The most important function for our purpose is function getIndpEnv($getEnvName) in class.t3lib_div.php (linked is the trunk version in repository).

This function is responsible for providing all information of the web server’s environment that TYPO3 needs to know (independent from which web server is being used). All the spots where the function relies on the value of variable HTTP_HOST are failing in case of a SSL proxy. Those parts have to be changed to make them aware of the SSL proxy. This is the case with at least the following parameters handed over to the function via $getEnvName:

Besides these the processing of more values has to be changed even if they don’t contain the host name:

Workaround to test the backend through a SSL proxy

The following instructions are meant as a temporary workaround to make it easy to enable SSL Proxy backend support for testing purposes on your TYPO3 4.1.1 installation without the need to change a lot of coding in a lot of different places. The patch provided below will only change class.t3lib_div.php.

The existing function getIndpEnv() will be renamed to getIndpEnvClassic(). A new wrapper function named getIndpEnv() will be added that will return the values needed for successfully using a SSL proxy. If no reverse proxy is detected the wrapper function will then call the function getIndpEnvClassic().

The patch only works with TYPO3 version 4.1.1. It is not a well designed solution meant to be integrated into the TYPO3 core. It’s only a “fast-food” product. Be aware that an upgrade to a newer version of TYPO3 will overwrite your manual changes. Also be aware that this code will not disable unencrypted HTTP access to the backend! Without using the SSL Proxy you can login without SSL through using own domain names. To disable HTTP access have a look at the TYPO3 configuration parameter lockSSL in your Install Tool (or in localconf.php).

Please follow these steps to enable SSL proxy support for your TYPO3 backend for testing purposes:

  1. Backup the file /t3lib/class.t3lib_div.php of your TYPO3 4.1.1 installation to be able to get rid of the changes you are making now in case of a problem.
  2. Download the patch displayed below and apply it to your TYPO3 4.1.1 installation. (Typo3.org provides a short tutorial on using the diff/patch tools.) The file /t3lib/class.t3lib_div.php will be modified.
  3. After applying the patch open file /t3lib/class.t3lib_div.php to change the IP address of the used reverse proxy in line 3024: Replace the string 0.0.0.0 in the line $sslProxyIP = ‘0.0.0.0’; with the local IP address of your SSL proxy (see value of $_SERVER[‘REMOTE_ADDR‘]). Don’t take the external IP address (not the one that you get via ping ssl.mysslproxy.com).
  4. Save the file (and upload it if necessary).

Use this patch in step 2 (you can also download the patch as a file from here):

diff -ru t3lib.411.original/class.t3lib_div.php t3lib.411.reverseproxypatch/class.t3lib_div.php
--- t3lib.411.original/class.t3lib_div.php	2007-04-14 11:45:06.436670400 +0200
+++ t3lib.411.reverseproxypatch/class.t3lib_div.php	2007-04-14 13:03:56.077560000 +0200
@@ -3007,6 +3007,63 @@
 	}

 	/**
+	 * This patch should not be used in a productive environment.
+	 *
+	 * Wrapper method (Version 0.06 from 2007/04/14)
+	 * for old getIndpEnv that has been renamed to getIndpEnvClassic
+	 * This patch should not be used in a productive environment.
+	 *
+	 * This function serves as a temporary workaround to enable
+	 * SSL proxy support in TYPO3 4.1.1. You have to change the value
+	 * of $sslProxyIP manually to the IP address of you reverse proxy.
+	 *
+	 * More information regarding this method you will find here:
+	 * http://www.henningpingel.de/TYPO3-Backend-Via-SSL-Proxy.124.0.html
+	 */
+	function getIndpEnv($getEnvName) {
+        $sslProxyIP = '0.0.0.0'; //fill in the local IP address of your SSL proxy here
+		$retVal = '';
+		if ($_SERVER["REMOTE_ADDR"] == $sslProxyIP && $_SERVER["HTTP_X_FORWARDED_HOST"] != ''){ // && TYPO3_MODE=='BE'){
+			$sslProxy_folder = $_SERVER['HTTP_HOST'];
+			$sslProxy_host = $_SERVER['HTTP_X_FORWARDED_HOST'];
+			switch ((string)$getEnvName)	{
+				case 'HTTP_HOST':
+					$retVal = $sslProxy_host;
+				break;
+				case 'SCRIPT_NAME':
+				case 'REQUEST_URI':
+					$retVal = '/' . $sslProxy_folder . t3lib_div::getIndpEnvClassic($getEnvName);
+				break;
+				case 'TYPO3_REQUEST_HOST':
+					$retVal = 'https://'. $sslProxy_host; //this is now forced to https, this is a problem if reverse proxy does not use SSL
+				break;
+				case 'TYPO3_SSL':
+					$retVal = TRUE;//this is now forced to https, but this means a problem if the reverse proxy does not use SSL
+				break;
+				case 'TYPO3_HOST_ONLY':
+					$p = explode(':',$sslProxy_host);
+					$retVal = $p[0];
+				break;
+				case 'TYPO3_PORT':
+					$p = explode(':',$sslProxy_host);
+					$retVal = $p[1];
+				break;
+				case 'REMOTE_ADDR':
+					$retVal =  $_SERVER["HTTP_X_FORWARDED_FOR"]; //this contains ip address of enduser, REMOTE_ADDR contains IP of proxy
+				break;
+				default:
+					$retVal = t3lib_div::getIndpEnvClassic($getEnvName);
+			}
+		}
+		else{
+			$retVal = t3lib_div::getIndpEnvClassic($getEnvName);
+		}
+		//error_log("debug getIndpEnv case :" . $getEnvName . " - " . $retVal); //enable this for debugging purpose
+
+		return $retVal;
+	}
+
+	/**
 	 * Abstraction method which returns System Environment Variables regardless of server OS, CGI/MODULE version etc. Basically this is SERVER variables for most of them.
 	 * This should be used instead of getEnv() and $_SERVER/ENV_VARS to get reliable values for all situations.
 	 * Usage: 221
@@ -3014,7 +3071,7 @@
 	 * @param	string		Name of the "environment variable"/"server variable" you wish to use. Valid values are SCRIPT_NAME, SCRIPT_FILENAME, REQUEST_URI, PATH_INFO, REMOTE_ADDR, REMOTE_HOST, HTTP_REFERER, HTTP_HOST, HTTP_USER_AGENT, HTTP_ACCEPT_LANGUAGE, QUERY_STRING, TYPO3_DOCUMENT_ROOT, TYPO3_HOST_ONLY, TYPO3_HOST_ONLY, TYPO3_REQUEST_HOST, TYPO3_REQUEST_URL, TYPO3_REQUEST_SCRIPT, TYPO3_REQUEST_DIR, TYPO3_SITE_URL, _ARRAY
 	 * @return	string		Value based on the input key, independent of server/os environment.
 	 */
-	function getIndpEnv($getEnvName)	{
+	function getIndpEnvClassic($getEnvName)	{
 		/*
 			Conventions:
 			output from parse_url():
@@ -3284,7 +3341,9 @@
 	function getHostname($requestHost=TRUE)	{
 		$host = '';
 		if ($requestHost && (!defined('TYPO3_cliMode') || !TYPO3_cliMode))	{
-			$host = $_SERVER['HTTP_HOST'];
+			//$host = $_SERVER['HTTP_HOST']; //old version, why is getIndpEnv not used for this???
+			$host = getIndpEnv('HTTP_HOST');
+			//error_log("hepi: getHostname was called"); //enable this for debugging purposes
 		}
 		if (!$host)	{
 				// will fail for PHP 4.1 and 4.2

To be continued

This is a work in progress article. I have only limited time to work on it. Please be patient. Stay tuned. There are things that are not covered in here yet, for example:

发表评论

电子邮件地址不会被公开。 必填项已用*标注