Archive for August 2008

Ubuntu, Apache, SSL, Subversion and LDAP

At work we needed a new source control system and we decided to use Subversion. This blog will give details about the installation of how to use Apache (with SSL connection) to control subversion. Access to the subversion repository is done with LDAP connected to an Active Directory

Unbuntu installation

I used the ubuntu 8.0.4 desktop version. Installation with the wizard is straight forward and therefore not discussed. The hostname in this example is: jdk-svn . After installation of Ubuntu I checked for new package update (sudo apt-get update and sudo apt-get upgrade).

Installation of Apache

To install apache2 you need to install the apache2 package

sudo apt-get install apache2

Besides the package apache2 also the packages apache2-mpm-worker, apache2-utils, apache2.2-common, libapr1, libaprutil1 and libpq5 are installed

After installation you can check if the Apache webserver is running by pointing your browser to the http://jdk-svn location (from the server also http://localhost will work). When the server is running a webpage will be served with the simple textmessage It Works

Installation SSL

For authentication we don’t want to send plain text on the network. SSL need to be installed to support secure connections. SSL needs the packages openssl and ssl-cert which are installed by default with the Ubuntu 8.0.4 installation (otherwise use sudo apt-get install openssl and sudo apt-get install ssl-cert to install these packages)

A certificate is needed for SSL. I will use a self signed certificate which can be created with make-ssl-cert

Note: a lot of websites are talking about the script apache2-ssl-certificate to make a certificate. The problem is that for some reason this script is not anymore part of the apache2 package. There are ways to extract the script from other apache2 packages but I found it easier to create a certificate with make-ssl-cert

Note 2: make-ssl-cert generated a certificate which is by default expired after 30 days. After 30 days it can still be used but it will give another warning inside the browser. Does somebody know how to generate a certificate which expires after 365 days (which I though is the maximum days)?

sudo make-ssl-cert /usr/share/ssl-cert/ssleay.cnf /etc/ssl/private/localhost.pem

When using make-ssl-cert some question will be asked by the script which need to be answered for the generation of the certicate:

  • Country Name
  • State or Province Name
  • Locality Name
  • Organisation Name
  • Organisation Unit Name
  • Host name: jdk-svn
  • Emailaddress

The apache website configuration should be changed to make use of SSL and the certificate. Apache can have multiple website configuration (stored inside /etc/apache2/sites-available). A site is accessable (enabled) when it is inside the etc/apache2/sites-enabled directory. This, enabling a site, can be done with the a2ensite tool. With a2dissite, a site can be disabled.

The default website configuration (created by default serving the It works website) will be used for the new SSL configuration

cd /etc/apache2/sites-available
sudo cp default ssl

Edit the ssl configuration with your favorite texteditor (for example gedit: sudo gedit ssl). Make the following changes (port 443 is the port number used for https)

NameVirtualHost * -> NameVirtualHost *:443
<VirtualHost *> -> <VirtualHost *:443>

Add (between <VirtualHost> tags. For example directly below ServerAdmin):

SSLEngine On
SSLCertificateFile /etc/ssl/private/localhost.pem

The ssl website should be enabled. Besides enabling the website also the apache ssl module should be enabled (which can be done with the a2enmod script)

sudo a2ensite ssl
sudo a2enmod ssl

Note 3: I only want to use the server for SVN access. Therefore I want to disable the website running on port 80. Use sudo a2dissite default to disable the website at port 80.

After creating the ssl configuration and enabling the website, Apache should be restarted

sudo /etc/init.d/apache2 restart

Note 4: when (re)starting Apache I got an error that the server name could not be determined. I fixed this by adding the ServerName to the httpd.conf. Open the httpd.conf file (sudo gedit /etc/apache2/httpd.conf), this file is empty by default, and add the line ServerName jdk-svn

After restarting Apache the ssl website should be accessable by pointing your webbrowser to https://jdk-svn (or from the machine: https://localhost). The same It works! website will be served (because we did not changed what will be served inside the ssl configuration). When you disabled the default site, you will get a Not found website when requesting http://jdk-svn. By the way, the first time you will access the website which will make use of SSL, your browser will give some security exception about that the certificate cannot be verified. This is true because we created a self signed certificate. Make an exception for this website by allowing access to this website inside your browser configuration.

Installation of Subversion

Apache2 and SSL is running, it is time for Subversion. Install the subversion package:

sudo apt-get install subversion

After subversion is installed a subversion repository should be made. This repository will be accessible by Apache (SSL) with the use of LDAP for authentication and authorization. I created the subversion repository inside the /var directory

sudo mkdir /var/svn
sudo mkdir /var/svn/myrepository

The repository will be called myrepository. Because users will add and modify files inside SVN by the use of Apache, the Apache user (www-data) should be the owner of the myrepository directory. If permissions are not set correctly, you will finish with a read only repository.

First set the ownership of the directory to the www-data user

sudo chown -R www-data /var/svn/myrepository

The www-data group (of which the user www-data is member of) should be set a ownership group of this directory

sudo chgrp -R www-data /var/svn/myrepository

It is important that when users are adding files to the repository, that these files have proper permissions set. Use the following command to do that

sudo chmod -R g+rws /var/svn/myrepository

Use svnadmin to make it a Subversion repository

sudo svnadmin create /var/svn/myrepository

Because svnadmin added directories and files to the myrepository folder which do not have the right group write access, it is important to repeat the chmod command

sudo chmod -R g+rws /var/svn/myrepository

The repository is now created. Let’s integrate it with Apache. We don’t use LDAP yet to see if the integration is working.

For Apache and Subversion integration, the libapache2-svn package is needed. Install this package

sudo apt-get install libapache2-svn

Change the ssl website configuration (sudo gedit /etc/apache2/sites-available) by adding the following part between the <VirtualHost> tags. For example just below the last </Directory> tag

<Location /repos>
   DAV svn
   SVNParentPath /var/svn
   SVNListparentPath on
</Location>

The location /repos means that you can access the repositories by going to https://jdk-svn/repos from within your browser. You cahttp://www.johandekoning.nl/wp-admin/post.php?action=edit&post=84&message=4n change this if you prefer a different location. SVNParentPath /var/svn is the location where the repositories are stored on the local file system. With SVNListParentPath on you will get a website showing the different repositories when opening https://jdk-svn/repos. If you set this to off (or remove the line) you will get a Not Found error. In this situation you can access a repository by going directly to the repository location (http://jdk-svn/repos/myrepository).

Restart apache (sudo /etc/init.d/apache2 restart) to make the changes visible. When opening the url http://jdk-svn/repos inside your browser, you will get a list of repositories available (in this case only the myrepository)

LDAP integration

Apache and subversion are working together but everybody has access to it. LDAP will be used to only allow access to members of an Active Directory. By using LDAP we can centralize authorization/authentication and don’t have to configure access inside different configuration files.

For LDAP integration two apache modules should be enabled: ldap and authnz_ldap. Ldap will automatically been enabled when enabling the authnz_ldap module:

sudo a2enmod authnz_ldap

Note 5: I am using Microsoft Active Directory, which does not allow anonymous access for retrieving the members inside a Active Directory. To solve this I created a new Windows Account inside the Active Directory which does not have terminal access (cannot login on Windows Workstations). Problem with not having anonymous access (or a more limited account than disabling terminal access) the username and password should be set as plain text inside the ssl configuration. For know I think that is fine but off course I prefer a saver solution. Post your ideas by adding a comment to this blog item).

Edit the ssl configuration file (sudo gedit /var/apache2/sites-available)

...
AuthType Basic
AuthBasicProvider ldap
AuthzLDAPAuthoritative on
AuthName "jdk-svn"
AuthUserFile /dev/null
AuthLDAPURL "ldap://activedirectory.johandekoning.nl:3268/DC=johandekoning,DC=nl?sAMAccountName?sub?(objectClass=*)"
AuthLDAPBindDN "CN=<ldap_username>,CN=users,DC=johandekoning,DC=nl"
AuthLDAPBindPassword  <ldap_password>
AuthLDAPGroupAttributeIsDN on
AuthLDAPGroupAttribute member

SSLRequireSSL
Require valid-user

Make changes to this configuration file confirming your active directory installation (the AuthLDAPURL, AuthLDAPBindDN and the AUTHLDAPBindPassword)

Restart apache (sudo /etc/init.d/apache2 restart) and go to https://svn-jdk/repos with a webbrowser. A login dialog will be shown asking a username and password. Use a windows username and password of a user which is part of the active directory. When login is successfully you will see the repository list again.

Clean up apache ssl configuration

The ssl configuration created still contains configuration which is not needed when you only want to use apache for subversion integration. For example the It works website is still served when opening https://jdk-svn. Clean up the ssl configuration (sudo gedit /etc/apache2/sites-available/ssl) by removing the following parts:

DocumentRoot /var/www/
<Directory />
   Options FollowSymLinks
   AllowOverride None
</Directory>
<Directory /var/www>
   ...
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
   ...
</Directory>

And remove the part

Alias /doc "/usr/share/doc/"
<Directory "/usr/share/doc/">
   ...
</Directory> 

Restart apache (sudo /etc/init.d/apache2 restart) and check that the It works site is not served anymore (https://jdk-svn will give back a Not found message)

What’s next?

You finished the installation and configuration of Apache, SSL, Subversion and LDAP integration. You can now use your favorite Subversion applications to modify the content of the repository.

The LDAP integration gives users access to Subversion which are inside the Active Directory. I am still searching for solutions to make more use of the Active Directory and LDAP mechanism. For example defining inside Active Directory the access to repositories for each user (and not inside Apache configuration files because I want to keep these settings centralized). If you have any idea how to define this within Active Directory, please post it as a comment. Besides accessing repositories you can also think about read-only access.

The SSL configuration

To summarize the changes made to the ssl configuration, the final ssl configuration file is added:

NameVirtualHost *:443
<VirtualHost *:443>
        ServerAdmin webmaster@localhost.com
        SSLEngine On
        SSLCertificateFile /etc/ssl/private/localhost.pem

        ErrorLog /var/log/apache2/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog /var/log/apache2/access.log combined
        ServerSignature On

        <Location /repos>
                DAV svn
                SVNParentPath /var/svn
                SVNListparentPath on

                AuthType Basic
                AuthBasicProvider ldap
                AuthzLDAPAuthoritative on
                AuthName "jdk-svn"
                AuthUserFile /dev/null
                AuthLDAPURL "ldap://activedirectory.johandekoning.nl:3268/DC=johandekoning,DC=nl?sAMAccountName?sub?(objectClass=*)"
                AuthLDAPBindDN "CN=<ldap_username>,CN=users,DC=johandekoning,DC=nl"
                AuthLDAPBindPassword <ldap_password>
                AuthLDAPGroupAttributeIsDN on
                AuthLDAPGroupAttribute member

                SSLRequireSSL
                Require valid-user
        </Location>
</VirtualHost>

Locatie gebaseerde NS Reisplanner

De Nederlandse Spoorwegen heeft sinds enkele maanden een mobile website (http://m.ns.nl) waarmee je een reis kunt plannen maar ook de actuele vertrektijden kan opvragen van treinen die vanaf een bepaald station vertrekken. Handig om op je mobiel te kijken of de trein vertraging heeft.

Een nadeel van een mobiele telefoon vind ik persoonlijk dat het intypen van tekst toch enige ervaring/tijd vereist. Op het moment dat ik klaar ben met het station in te typen voor de actuele vertrektijden is de trein al gearriveerd (of al drie keer langsgeweest).

Google heeft afgelopen vrijdag een nieuwe versie van Google Gears uitgebracht (http://gears.google.com). Google Gears biedt onder andere als uitbreiding op je webbrowser de mogelijkheid om gegevens van een website tijdelijk offline beschikbaar te stellen (indien de website hiervan gebruik maakt). In de nieuwe versie is Google Gears ook voorzien van een Geolocatie module. Met behulp van deze module kun je aan de hand van het ipadres een grove schatting maken van de locatie van de gebruiker. Deze informatie kan je gebruiken in een webpagina (de gebruiker zal Google Gears geïnstalleerd moeten hebben en zal bij het eerste bezoek de vraag krijgen of je de locatie gegevens wilt delen). Google Gears werkt buiten de desktop ook op Windows Mobile.

Google Gears, Windows Mobiles, NS Reisplanner… dat biedt de mogelijkheid om een lijstje stations weer te geven aan de hand van de locatie. De afwijking, 25 km (wat ik nu op mijn mobiel kreeg), is hierbij geen probleem omdat ik maximaal 10 stations binnen die cirkel (gesorteerd op degene welke het dichtste bij is) weergeef. Door een station te selecteren zal vervolgens de actuele vertrektijden pagina van het betreffende station geopend worden (wat gewoon de pagina van de mobiele NS website is).

Uiteraard is zo’n verhaal erg boeiend, maar uitproberen is altijd leuker: http://www.johandekoning.nl/treinplanner. Google Gears moet hiervoor zijn geinstalleerd maar als dit niet het geval is zal er een download link verschijnen.

Binnenkort zal ik in een nieuw (Engels) blog item de technische details vertellen over de werking van deze locatie gebaseerde treinplanner

Vandaag (27 augustus) de applicatie getest onderweg van huis naar het werk. Bij Amsterdam Amstel was de afwijking volgens Google Gears maar 500 meter. Het lijkt er naar mijn mening op dat bij de smartphones ook gebruik wordt gemaakt van de netwerk gegevens (afstand tot de mast).

HttpWebRequest on Compact Framework: The operation has timed-out

With .Net Compact Framework I developed an application that is doing a webrequest on interval bases. With the webrequest I sent data by providing some arguments inside the querystring. I am not interested in the response and therefore I did not assign the GetResponse() return value. I use the following code:

HttpWebRequest webRequest;
try
{
   webRequest = WebRequest.Create("http://someurl.com/with.aspx?querystring=value");
   webRequest.Method = "GET";
   webRequest.KeepAlive = false;
   webRequest.GetResponse();
}
catch (Exception e){
    //Something goes wrong. Implement handling of this exception
}
finally {
   webRequest = null; //To clear up the webrequest
}

This code works… for only 2 times. The third time it is executed I get a: The operation has timed-out exception. It looks like the GetResponse() method keeps a connection (or socket) open which make it not possible to make new connection at the third time the method is executed. I solved this problem by assigning the GetResponse() value to a WebRequest variable and execute the Close() method inside the finally part of the try catch:

HttpWebRequest webRequest;
WebResponse webResponse = null; //Need to assign this as null otherwise it does not compile
try
{
   webRequest = WebRequest.Create("http://someurl.com/with.aspx?querystring=value");
   webRequest.Method = "GET";
   webRequest.KeepAlive = false;
   webResponse = webRequest.GetResponse(); //Assign to webResponse
}
catch (Exception e){
    //Something goes wrong. Implement handling of this exception
}
finally {
   if(webResponse != null)
   {
      webResponse.Close(); //Close webresponse connection
   }
   webResponse = null; //To clear up the webresponse
   webRequest = null; //To clear up the webrequest
}

Command-line arguments Windows Mobile

In September I will go on holiday to the United States for 3 weeks. It would be nice to send updates to family and friends by placing images on Picasa. These web images will be taken with the photocamera from my mobile (which is Windows Mobile 6 based). I will upload the pictures when a free WiFi access point is available.

The program will place taken photos in a queue. When I have an internet connection available I can upload all the pictures from this queue to Picasa. This results in an application that can be seperated in two parts: the take a photo functionality and the upload photo-queue functionality.

Use cases explaining functionality

My mobile has a photo camera button which will trigger the default photo camera application. This default camera application can be replaced by changing the button settings on the mobile phone. When replaced (and when clicking the camera button) I want my application to start up ready to take a picture (so it will behave the same as the default camera application). After taking a picture you can type in some comment and add it to the queue.

When arriving at the hotel (or some other connective place) I want to upload the taken images. I can off course push the photo camera button again, but this time it is not my intention to take a new picture. In this case I want to start the application from the Start -> Programs menu and see the state of the queue with the option to upload the photos. The same application but with a different startup state.

The use of command-line arguments should solve this problem. But is this also possible with the Compact .Net Framework

Not a solution: Environment.GetCommandLineArgs

For Windows Form based application (which my mobile application is) the use of the Environment class is suggested to get the command-line arguments. The static method

<br />
public static string[] GetCommandLineArgs()<br />

The problem is that this method is not implemented inside the .Net Compact Framework and can’t therefore not been used.

Solution: static void Main(String[] args)

Just like console application (written in .Net) you have a Main method for your Windows Forms application. Besides the files created for the Form (by default Form1.cs and Form1.Designer.cs) a Program.cs file is created which contains a Main method. By adding a String[] args to the Main method, you can work with command-line arguments. An example of the new Main method

<br />
[MTAThread]<br />
static void Main(String[] args)<br />
   {<br />
      if (args != null &#038;&#038; args.Length > 0)<br />
     {<br />
         foreach (String arg in args)<br />
         {<br />
            MessageBox.Show(arg);<br />
         }<br />
     }<br />
     else<br />
     {<br />
         MessageBox.Show("No program arguments");<br />
     }<br />
     Application.Run(new Form1());<br />
}<br />

The arguments can be given to the form (Form1 in this example) by changing the constructor.

Creating a shortcut

The easiest way to create a shortcut is by using the Windows Explorer on your mobile phone. Navigate to the executable file of the application. Tap and hold the file selected with your styles to get the context menu. Select copy.

At the same location (or on a different location) tap (and hold) on an empty space to get the context menu. This context menu will give you the option to paste as a shortcut.

To add the command line argument inside the shortcut, i copied the shortcut (.lnk file) with ActiveSync to my desktop pc and edit the content with notepad. The file will now look like this:

59#”\Programmabestanden\ProgramArguments\ProgramArguments.exe” this_is_a_program_argument

My Windows Mobile OS is Dutch. On an English based OS the Programmabestanden part will be replaced by something like Program files. I don’t know if the number 59 before the line is mandatory. The number of characters with quotes is 59 long. So maybe it is used for reading optimalization. I really don’t know. I just added the this_is_a_program_argument at the end of the shortcut, saved the file and copied it back to my pocket pc.

Starting the application with the shortcut will show a messagebox with the text this_is_a_program_argument. When starting the application from the normal executable you will get a messagebox saving No program arguments.

Attachment: example project

You can download the example application from here

Special thanks

I want to thank Paul for discussing this problem and finding a solution.