Hot on the heels of yesterday’s posting about my brief experience with YSlow in a Selenium framework, comes the release of Cesium 0.1, a tool specifically designed for automating YSlow runs.

I don’t care enough about YSlow tests to setup and maintain another test harness just for that purpose, so I don’t think I’ll be trying it myself. For others who get in to this sort of thing, here’s something to get in to.

Adam Goucher has a clever idea to automate YSlow reporting to a Show Slow database with Selenium.

Well, I tried this with my Selenium RC setup, testing one page. In my setup, two beacons are sent by YSlow to the Show Slow database but the urls recorded,

http://localhost:3000/selenium-server/core/Blank.html?start=true
and
chrome://src/content/RemoteRunner.html?sessionId=2d7695881a3881f940ada5bc889a6dee&multiWindow=true&baseUrl=http%3A%2F%2Fintegrate.crashingdaily.com&debugMode=false&driverUrl=http://localhost:3000/selenium-server/driver/

are not the ones for the page under test. Neither report generated from selenium matches the report I get from the standalone browser so it’s not just a matter of the url being misreported to Show Slow.

disappointing.

The at command can be used in CGI scripts to remotely trigger non-recurring jobs on a web server. This is especially useful for long running jobs that clients don’t need to wait on and jobs that need to persist across web daemon restarts.

My initial simple attempts to schedule a job with at in a CGI script executed by an Apache webserver were met with the failure response “This account is currently not available“. That message being the output of /sbin/nologin which is the shell value set for the apache user in /etc/password.

The fix is to set the SHELL environment variable to a proper shell when executing at. Here’s a simple, proof-of-concept CGI script.

#!/usr/bin/perl
use CGI;
my $cgi = new CGI;
print $cgi->header;

system(’echo “/usr/bin/env > /tmp/apacheenv” | SHELL=/bin/bash at now’);

Calling the CGI script triggers the at scheduling. In this case, the execution of /usr/bin/env is immediate and successful.

$ cat /tmp/apacheenv
SERVER_SIGNATURE=
SHELL=/bin/bash
HTTP_USER_AGENT=Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/528.18.1 (KHTML, like Gecko) Version/4.0 Safari/528.17
SERVER_PORT=80

Related:

at man page

Firefox 3.0.3 (on OS X at least) launched by Selenium RC 1.0 beta 1 displays an alert dialog box:

Alert
sb-ssl.google.com:443 uses an invalid security certificate.
The certificate is not trusted because the issuer certificate has expired.
(Error code: sec_error_expired_issuer_certificate)

I’ve seen a report that the nightly build doesn’t have this issue but, as a quick fix for Se 1.0b1, one can edit /Applications/Firefox 3.0.3.app/Contents/MacOS/defaults/pref/firefox.js
and change a couple of "browser.safebrowsing" preferences to false.

pref("browser.safebrowsing.enabled", false);
pref("browser.safebrowsing.malware.enabled", false);

That disables Google Safe Browsing – a feature which isn’t important to me but maybe it is to you, in which case this solution isn’t your cup of tea.

Related

Browser.safebrowsing.enabled
Google Safe Browsing for Firefox

Having successfully enabled Hudson security with authentication against an LDAP server, I wanted to tweak the configuration to use a secure connection.

I tried using the ldaps scheme, ‘ldaps://ds1.crashingdaily.com’, as per the inline documentation but was confronted with the error “Syntax of this field is SERVER or SERVER:PORT”. I puzzled over this message for awhile, triple checked the syntax, consulted the support forums and then the source code to confirm that the syntax was indeed supported. Finally, I ignored the error, saved the configuration and tried a login. Then Hudson spit out a helpful exception in its Tomcat log:

hudson.security.AuthenticationProcessingFilter2
onUnsuccessfulAuthentication
INFO: Login attempt failed
org.acegisecurity.AuthenticationServiceException: Unable to connect to
LDAP server; nested exception is javax.naming.CommunicationException:
simple bind failed: ds1.crashingdaily.com:636 [Root exception is
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target]; nested exception is
org.acegisecurity.ldap.LdapDataAccessException: Unable to connect to
LDAP server; nested exception is javax.naming.CommunicationException:
simple bind failed: ds1.crashingdaily.com:636 [Root exception is
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target]
	at
org.acegisecurity.providers.ldap.LdapAuthenticationProvider.retrieveUser
(LdapAuthenticationProvider.java:238)

The key phrase to me there was ‘unable to find valid certification’. I suspected the Java classes used to negotiate the ldap connection were balking at my self-signed certificate coming from the ldap server. This wasn’t really a failure of Hudson specifically but rather a common issue for many language libraries and SSL applications. Typically the solution is as simple as pointing the library and/or application at the Certificate of Authority (CA) that was used to self-sign the server’s SSL cert. A quick Google search turned up sufficient information for me to import our CA into a keystore for Tomcat’s JVM.

For starters, I made a copy of the cacerts that ships with the JDK. This keystore contains a set of common, trusted CA certs. I don’t need them for Hudson but they may come in handy for future use.

$ cp $JAVA_HOME/jre/lib/security/cacerts /usr/local/tomcat/conf/

Now, I need to get the CA certificate for the ldap server. I have that file around here somewhere but rather than digging it up it’s simple to just get it from the ldap server using openssl.

$ openssl s_client -connect ds1.crashingdaily.com:636 -showcerts

This openssl command prints a list of certificates from the server, the last certificate is the CA. I copy that last certificate to a file, my-ca.crt, and import it into the cacerts keystore.

$ $JAVA_HOME/bin/keytool -import -alias myca -keystore cacerts \
-trustcacerts -file my-ca.crt

The -alias is not too important, it just has to be unique in the keystore. You can list the keys in the store with

$ $JAVA_HOME/bin/keytool -list -keystore  cacerts

Then I add a system property for Tomcat’s JVM that specifies the trustStore as my new cacert. For example, this can be added to the CATALINA_OPTS environment variable if you use the catalina.sh script that ships with Tomcat.

-Djavax.net.ssl.trustStore=/usr/local/tomcat/conf/cacerts

I restart the Tomcat instance for my Hudson installation and now Hudson is able to authenticate over a secure channel.

Related:

Apache Tomcat 5.5 SSL Configuration HOW-TO
Troubleshooting javax.net.ssl.SSLHandshakeException

Enabling security to my Hudson installation was a fairly straight-foward task although it did take a little trial and error to get it all straight. I chose to authenticate against our existing LDAP directory. One of the things that caught me off guard at first was the ‘User search base’ and ‘Group search base’. These settings are Relative Distinguished Names (ou=People), not the full Distinguished Name (ou=People,dc=crashingdaily,dc=com). Not a big deal; in fact I could have (should have, but I like to tinker) just left these fields empty and Hudson would have figured it out on its own.

Here is my almost final configuration (my final configuration makes use of StartTLS for a secure connection to the LDAP server – more on that in a later post).

hudson_ldap.gif

The Manager DN is an account that has read-only search permissions. This is required because our LDAP directory is not searchable with anonymous binds.

The LDAP group configuration took me a bit more effort and debugging. I have an HudsonAdmin LDAP group in my directory. The DN for it is cn=HudsonAdmin,ou=Groups,dc=crashingdaily,dc=com

To get this work I had to upgrade to the 1.261 release which adds uniqueMember, memberUid to the existing member in the search filter (my HudsonAdmin group has ‘uniqueMember’ entries). Incidentally, one of the things I love about Hudson is when I run into a bug or missing feature I can usually count on it be addressed in a newer release – and upgrading to a new release is quick and painless.

A not-so-obvious aspect to the configuration is that, when adding an LDAP group, Hudson wants the group name in all uppercase and with ‘ROLE_‘ prefixed to it. So, ‘cn=HudsonAdmin,...‘ becomes ‘ROLE_HUDSONADMIN‘. I did find this requirement documented after a little Google searching, it’s just not spelled out in the admin form’s help.

Tip: When screwing around with your Hudson Authorization, make frequent backups of the working $HUDSON_HOME/config.xml so if you make a mistake and lock yourself out you can restore a working configuration from backup, reload Hudson and be back in business. The Hudson documentation does include a note about recovering from a lockout by setting useSecurity to false in the config.xml file but I find that doing that causes a loss of all your security settings and you’ll have to re-enter all the LDAP values and Authorization settings.

Hudson rocks. I mean, holy shit, this is one nice software package. I’m just starting my first foray into continuous integration (CI) and haven’t compared Hudson to other CI software but so far Hudson is so feature rich and easy to use that I don’t see a need to evaluate others.

Hudson has a clean user administration interface but happily obliges me if I want to manually tinker with the XML-format configuration files – something I had to do several times after repeatedly locking myself out trying to get the security settings correct. Editing the configuration files also comes in handy for making bulk changes across a dozen of job settings – for example, adding Jabber notification or renaming projects.

The formal documentation is a little on the light side but the GUI interface and layout of the configuration files is intuitive enough that I usually didn’t need to RTFM. The active support forum helped fill in many knowledge gaps and a little experimentation on my own has so far solved the rest.

Overall, I’m really amazed and thrilled how seamlessly it works with our existing build and testing system which consists of hobbled together shell scripts wrapping other Perl scripts and Ant tasks. I’ll be adding Selenium tests for our web components in the future.

I’ve hit a few, small stumbling blocks – nothing insurmountable – incorporating Hudson into my project and I’ll be posting about some of those. Still, I’m very excited about the prospects of having Hudson help me solve some long standing frustrations on this project, even those that aren’t really a component of continuous integration.

Today I compiled a 32-bit jsvc daemon tool linked to a 32-bit Java JDK on a 64-bit machine. Turns out I didn’t need it but am recording my notes in case someone else does need it.

I’m using the jsvc source that is bundled with Tomcat and Sun’s Java SE Development Kit 6u10 (jdk-6u10-linux-i586.bin).

$ JAVA_HOME=/usr/java/jdk1.6.0_10-32bit

$ cd /usr/local/apache-tomcat-5.5.23/bin/jsvc-src

$ CFLAGS=-m32 CPPFLAGS=-m32 CCASFLAGS=-m32 LDFLAGS="-L/usr/lib -L/lib" \
./configure --build=i686-pc-linux-gnu

$ make

make will error with

gcc -L/usr/java/jdk1.6.0_10-32bit/lib -L/usr/lib -L/lib -ldl -lpthread jsvc-unix.o libservice.a -o ../jsvc
/usr/bin/ld: skipping incompatible /usr/lib/libdl.so when searching for -ldl
/usr/bin/ld: skipping incompatible /usr/lib/libdl.a when searching for -ldl
....

Adjust that failing gcc command with the -m32 switch and compile manually.

$ cd native/
$ gcc -m32 -L/usr/java/jdk1.6.0_10-32bit/lib -L/usr/lib \
 -L/lib -ldl -lpthread jsvc-unix.o libservice.a -o ../jsvc

There should now be a 32-bit jsvc binary in the jsvc-src directory.

$ file jsvc
jsvc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped

Tip of the hat to a Tomcat mailing list thread started by Rob Tanner

A mod_rpaf story.

My Apache virtual hosts are dynamically configured from a library of Perl code using PerlSections.
A configuration file for one virtual looks something like:

<VirtualHost *:80>
 <Perl>
  require 'conf/lib/MasterConf.pm';
 </Perl>
</VirtualHost>

Aside from boostrapping all the basic virtual host configuration, MasterConf.pm establishes basic authentication (via mod_auth_tkt) for the <Location /> of the virtual host. Authentication is the desired default but, for some specific virtual hosts, I want to disable the authentication requirements. This I can do by appending a new <Location /> after the Perl Section.

<VirtualHost *:80>
 <Perl>
  require 'conf/lib/MasterConf.pm';
 </Perl>
 <Location />
  Allow from any
 </Location>
</VirtualHost>

This effectively overrides the deny from declared by the perl library and makes the site public. This has been working fine for me for years. Recently though, I put an Nginx reverse-proxy in front of some of my virtual hosts and installed mod_rpaf into Apache so the IP address it logged would be the client’s and not the proxy server.

There I hit a snag. Apache/mod_rpaf was ignoring the X-Forwarded-For header and only logging the IP address of the proxy. Actually the GET '/' request was logging correctly, but that was insufficient and no consolation.

Client logging was done correctly if I used a static, hand-crafted Apache configuration file (not using the Perl library) so I knew mod_rpaf was installed correctly. I began to suspect the dynamic Perl configuration and that made me nervous; I really didn’t want to give that up completely.

Well, cutting to the chase, it turns out that the ‘Allow from any’ added to disable the basic authentication was the culprit. I’ve been using ‘Allow from any’ for years and it does indeed allow any host access but looking at it anew today it suddenly struck me as being wrong. It’s normally ‘Allow from all‘, I must have had ‘Satisfy any‘ on the brain when I originally started using the override – propagating around through thoughtless copying/pasting. On a whim I changed it to ‘Allow from all‘ and, w00t!, mod_rpaf began logging the correct client address.

Are ‘allow from any’ and ‘allow from all’ supposed to mean different things to Apache? A little Googling turns up examples of people using ‘allow from any’ but I don’t see any indication that it has a special meaning.

I’m using the Apache mod_rpaf module to capture client IP addresses in the X-Forwarded-For header passed by an Nginx reverse proxy. This is good for logging and CGI environments but mod_rpaf does not fix up the client IP address sufficiently to be used in Apache’s allow/deny access control directives.

Almlys has a nice workaround.

Quoting the juicy bit from Almlys’s blog posting:

SetEnvIf X-Forwarded-For ^172\.26\.0\.17 let_me_in
Order allow,deny
allow from env=let_me_in

Clever.

Categories

 

July 2009
M T W T F S S
« Jun    
 12345
6789101112
13141516171819
20212223242526
2728293031  

Latest del.icio.us