Posts Tagged ‘ Apache

Patching Seaside for deployment

I’m currently in the process of deploying a Seaside application on my server at the University, and I noticed something that I needed to fix before I went any further. By default, the server adapters that ship with Seaside will listen on all interfaces for connections. Since I plan on proxying my Pharo image behind an Apache server, with Apache doing all of the authentication, I did not want my Seaside server to be accessible to outside clients on the port its listening on. This would allow them to bypass my authentication mechanisms I have in place.

I dug through the Seaside code a bit and found what I needed to patch. Note here, I only am patching the WAListenerAdaptor since that is the adaptor which supports Comet (which my application makes heavy use of).

Here is the original code:

WAListenerAdaptor>>listenLoop
	| socket |
	socket := Socket newTCP.
	socket 
		listenOn: port
		backlogSize: 50.
	socket isValid ifFalse: [ self error: 'Cannot listen on port ' , port greaseString ].
	
	[ 
	[ socket isValid ifFalse: [ ^ self listenLoop ].
	self waitForConnection: socket ] repeat ] ifCurtailed: 
		[ (Delay forMilliseconds: 10) wait.
		socket destroy ]

And the patched code:

WAListenerAdaptor>>listenLoop
	| socket |
	socket := Socket newTCP.
	socket 
		listenOn: port
		backlogSize: 50
		interface: NetNameResolver loopBackAddress.
	socket isValid ifFalse: [ self error: 'Cannot listen on port ' , port greaseString ].
	
	[ 
	[ socket isValid ifFalse: [ ^ self listenLoop ].
	self waitForConnection: socket ] repeat ] ifCurtailed: 
		[ (Delay forMilliseconds: 10) wait.
		socket destroy ]

Notice how I changed the socket initialization message to include the interface keyword, and supplied it with the loop back address. Now my application is only accessible via 127.0.0.1 or localhost, and not via its external IP.

The alternative would have been to leave the code unpatched and instead write some iptables firewall rules to block on the port the Seaside adaptor is listening on, but this seemed like a simpler solution and allows me to leave the rest of my system untouched. Also, this solution is the only possible way to do it if you do not have root access to the machine to add iptables rules.

Apache-based authentication for Seaside

I recently posted about a completely Smalltalk-based solution for LDAP authentication for Seaside applications. After I got that working for my application, I realized that once I submitted my application to our security group here on campus for review, it would never pass inspection due to the fact that user passwords are accessible to my code. I can imagine many scenarios where programmer access to user credentials is unacceptable.

Fortunately, our university provides its own kerberos-based authentication solution for applications called Bluestem. It provides the user first with a screen to enter their username, then a second screen to enter their university kerberos password. Upon successful authentication, the user is given access to the proper resources on the server. Bluestem is written in Perl and there exists an Apache module to integrate it with web applications. I previously set up the server I plan to deploy my Seaside application on for Bluestem authentication for other web applications that I host on it, but I did not know of a way that I could use this type of authentication with Seaside since I was planning on using mod_proxy to proxy requests from my external-facing Apache server to my headless Pharo VM.

While I was researching how to do this, I came across an Apache module that someone has posted on Github at https://github.com/aimxhaisse/mod-proxy-add-user which takes the authenticated user name set by Apache and forwards it on to servers being proxied. I was able to compile it (manually, the Makefile appeared to be broken) and get it up and running on my CentOS server within a few minutes. I configured my Apache configuration file with:

<Proxy *>
    Order deny,allow
    Allow from all
    ProxyAddUser On
    ProxyAddUserKey "HTTP_X_FORWARDED_USER"
</Proxy>

And low and behold, I was able to access the authenticated user name in my proxied Seaside application with:

username := self class decodeUserIdFrom: (self requestContext request headerAt: 'http_x_forwarded_user' ifAbsent: [username := 'cemeyer2']).

Let me explain a little bit. In most cases, just self requestContext request headerAt: ‘http_x_forwarded_user’ will give you the authenticated user name, but our university’s kerberos solution sets this variable to ‘username@uiuc.edu/kerberos’, so the decodeUserIdFrom message strips off everything but the user name. The above line also is useful for when accessing the application when not being proxied behind university authentication (such as during development), as it will default to return my user name if the header is not found.