Results for category "Apache"

19 Articles

TeamCity: Make the NuGet repository available without authentication

Task for today was making the NuGet repository of TeamCity available in our local network. Sounds easier as it was as our TeamCity instance is available from the Internet but you can only access the rontend with valid domain credentials (LDAP/Active Directory authentication). Enabling the guest account feature in TeamCity would have mean a small security break in our concept.

Here is the success-story how to enable the NuGet repository for local accesss without authentication and without enabling the TeamCity guest account:

Creating a new proxy instance

One of our Apache servers got the order to act as reverse proxy for the NuGet repository. The reverse proxy was running inside an own virtual host (nuget.mydomain.local) and was registered in our local DNS.
The configuration looked like this:


<VirtualHost *:443>
 SSLEngine on
 SSLProxyEngine on

ServerAdmin admin@mydomain.local
 ServerName nuget.mydomain.local
 ServerAlias nuget.mydomain.de

LogLevel warn
 ErrorLog logs/nuget.mydomain.local-error_log
 CustomLog logs/nuget.mydomain.local-access_log combined

# Map other source to FeedService URL
ProxyPass / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/
ProxyPassReverse / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/

</VirtualHost>

Adding a domain proxy user

With the configuration above we still have the problem that there is no user who can access the repository without credentials. So we need to add a domain user, let me call it teamcity-nugetfeed-proxy-user. For security reasons this account has a very long random generated password and is only used for providing the NuGet feed.

Using the domain proxy user in our Apache configuration

We have to add a Basic Authorization header in our Apache configuration so that every access between the proxy and TeamCity is authenticated.


RequestHeader set Authorization "Basic <Base64(teamcity-nugetfeed-proxy-user:$password)>"

The phrase <Base64(teamcity-nugetfeed-proxy-user:$password)> must be replaced by an base64-encoded string with format $username:$password.

After restarting the Apache you should be able to use the NuGet stream in Visual Studio. Look at the Apache logs for occuring errors.

TeamCity is failing – NuGet return “File contains corrupted data”

Till yet you are able read the stream but TeamCity will fail downloading your repository files. The reason is easy: The FeedService.svc returns an XML file which contains absolute pathes mapping to teamcity.mydomain.local and not nuget.mydomain.local. So again we have no authorization and the error File contains corrupted data occurs because of the TeamCity login dialogue.

We have to rewrite the content of the FeedService.svc replacing every occurence of teamcity.mydomain… with nuget.mydomain…
At first I tried mod_proxy_html but in the end using mod_substitute was the only solution that worked.


# Rewrite FeedService URL
 Substitute "s|https://teamcity.mydomain.local/guestAuth/app/nuget/v1/FeedService.svc|https://nuget.mydomain.local|i"
# Rewrite repository access
Substitute "s|https://teamcity.mydomain.local/guestAuth/repository|https://nuget.mydomain.local/repository|i"

# Execute every substitution only for XML/HTML
 AddOutputFilterByType SUBSTITUTE text/html
 AddOutputFilterByType SUBSTITUTE text/xml
 AddOutputFilterByType SUBSTITUTE application/atom+xml

# Map local repository path to remote repository
 ProxyPass /repository https://teamcity.mydomain.local:443/guestAuth/repository
 ProxyPassReverse /repository https://teamcity.mydomain.local:443/guestAuth/repository

# Unset gziped accepted encoding
RequestHeader unset Accept-Encoding

After restarting everything should work fine.

The complete configuration is now


<VirtualHost *:443>
 SSLEngine on
 SSLProxyEngine on

ServerAdmin admin@mydomain.local
 ServerName nuget.mydomain.local
 ServerAlias nuget.mydomain.de

LogLevel warn
 ErrorLog logs/nuget.mydomain.local-error_log
 CustomLog logs/nuget.mydomain.local-access_log combined

# Rewrite FeedService URL
 Substitute "s|https://teamcity.mydomain.local/guestAuth/app/nuget/v1/FeedService.svc|https://nuget.mydomain.local|i"
 # Rewrite repository access
 Substitute "s|https://teamcity.mydomain.local/guestAuth/repository|https://nuget.mydomain.local/repository|i"

# Map local repository path to remote repository
 ProxyPass /repository https://teamcity.mydomain.local:443/guestAuth/repository
 ProxyPassReverse /repository https://teamcity.mydomain.local:443/guestAuth/repository
 # Map other source to FeedService URL
 ProxyPass / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/
 ProxyPassReverse / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/

# Execute every substitution only for XML/HTML
 AddOutputFilterByType SUBSTITUTE text/html
 AddOutputFilterByType SUBSTITUTE text/xml
 AddOutputFilterByType SUBSTITUTE application/atom+xml

RequestHeader set Authorization "Basic <Base64(teamcity-nugetfeed-proxy-user:$password)>"

 # Unset gziped accepted encoding
 RequestHeader unset Accept-Encoding
</VirtualHost>

mod_proxy_connect.so: The specified module could not be found.

Today I wanted to update the Apache configuration in our DMZ to enable Git over SSH over SSL (another story) but while deploying the changes from my workstation to the server, Apache threw the error

Cannot load modules/mod_proxy_connect.so into server: The specified module could not be found.

As always procmon.exe was my friend and showed that mod_proxy_connect.so was referencing the msvcr100.dll which was not existent on the server
I fixed the error by copying msvcr100.dll from my %WINDIR%\SysWOW64 to the SysWOW64 directory of the server (both are x64 machines).

Windows Server 2003 as a central Git repository with Apache 2.2

After some years of working with (and fighting against) Subversion I decided to setup a Git repository for our company. Every developer should decide on their own what Version Control System he wants to use.

Jeremy Skinner wrote an excellent article about hosting a Git repository on Windows which was really helpful. Nevertheless I had to do some customizing.

Using ScriptAliasMatch

This drove me crazy: ScriptAliasMatch seemed to work, but the parameter (the repository argument) was not passed to git-http-backend.exe. Instead of this, I saw that Apache/httpd.exe tried to open c:/program files/git/libexec/git-core/git-http-backend.exe/repositoryname.

Solution: You must enable mod_cgi, otherwise the path argument “…exe/$1” can not be resolved.

Using Apache 2.2 + SSL

The git client failed to connect with the virtual host which was secured with SSLv3. I received the error message “error:140920DF:SSL routines:SSL3_GET_SERVER_HELLO:parse tlsext“. Anders Brownworth had this problem too, but I fixed it by disabling SSLv3 – which was at least acceptable in our environment. Use this line in your ssl.conf:

SSLProtocol +SSLv2 +SSLv3

Using a network share as repository

Our Subversion and Git repositories are stored on a network share, primarily for backup reasons. The path of the network share must be entered as “\\srv\share” – the doublequotes are important!
We are using the direct network share path and not a network drive, because after a restart of the machine containing the network share, the network drive will be unavailable. Windows marks it as offline and you can not use it longer until you restart the machine or reconnect the network drive manually. Accessing the network share via full UNC path will work as soon as the share is available again.

Running Apache on Windows 7 / Windows Server 2008

I tested the Git environment on Windows 7 (German) machine, and ran into the problem that the git-http-backend.exe could not be found. My fault was that I used “c:/Programme/Git/libexec/git-core/git-http-backend.exe” and notc:/Program Files“. Apache seems not be able to access the junction “c:/Programme“.

If you are running a x64 operating system, you must use c:/Program Files (x86)/Git/libexec/git-core/git-http-backend.exe.

Error “Client denied by server configuration”

Receiving the error “Client denied by server configuration” while setting up Git means in most cases that the access to the Git directory is denied. Take a look at the Apache Wiki and change your Order directives.

Running Apache under a special service account

If you want to run Apache under a special network service account (e.g. webserver@yourdomain.local), you have to keep this in mind.
The service account must have full access to your network share (\srvshare) and the repositories inside of it. Otherwise you will receive errors inside your Apache log like “error: insufficient permission for adding an object to repository database ./objects“.

Using Active Directory authentication/authorization for your Git repository

Authentication and authorization against the Active Directory/LDAP can be easily done with mod_authnz_ldap (mod_ldap prior to Apache 2.2.x):


	SetEnv GIT_PROJECT_ROOT "\\srv\git_repos"
	SetEnv GIT_HTTP_EXPORT_ALL
	ScriptAliasMatch "(?x)^/git/(.*/(HEAD | info/refs | objects/(info/[^/]+ | [0-9a-f]{2}/[0-9a-f]{38} | pack/pack-[0-9a-f]{40}.(pack|idx)) | git-(upload|receive)-pack))$" "c:/program files/git/libexec/git-core/git-http-backend.exe/$1"

	AuthType Basic
	AuthName "LDAP"
	AuthBasicProvider "ldap"
	AuthzLDAPAuthoritative Off
	require valid-user
	require ldap-group CN=security group,OU=your security group container,DC=domain,DC=local

All bare Git repositories inside of \srvgit_repos are available to http://webserver/git/. Every repository access will be authenticated and authorized against the given ldap-group. Authenticated read-only access can be done with LimitExcept directives.
Please note, that the centryl Git repository on your share must have the “sharedRepository = true” option. The option http.receive-pack is not needed, because every access is already authenticated through mod_authnz_ldap.

A working example without Active Directory authentication

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

<Directory "C:/srv/document_root">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

<Directory "c:/Program Files (x86)/git/libexec/git-core/">
    Order allow,deny
    Allow from all
</Directory>

SetEnv GIT_PROJECT_ROOT "C:/repos_git"
SetEnv GIT_HTTP_EXPORT_ALL

# Using x64 operating system
ScriptAliasMatch "(?x)^/git/(.*/(HEAD | info/refs | objects/(info/[^/]+ | [0-9a-f]{2}/[0-9a-f]{38} | pack/pack-[0-9a-f]{40}.(pack|idx)) | git-(upload|receive)-pack))$" "c:/Program Files (x86)/git/libexec/git-core/git-http-backend.exe/$1"

Global-Bind-User Patch für mod_auth_ldap (Apache 2.0.63) und mod_authnz_ldap (Apache 2.2.18) veröffentlicht

Heute habe ich es endlich geschafft, den Patch für mod_auth_ldap (Apache 2.0.63) und mod_authnz_ldap (Apache 2.2.18) auf Github zu veröffentlichen. Die Version für mod_auth_ldap hatte ich bereits vor zwei Jahren fertig gestellt, aber nie öffentlich zugänglich gemacht. Stattdessen schlummerte Kompilat und Sourcen in unserem Unternehmens-Repository.

Der Patch stellt drei Einstellungen für die Apache-Konfiguration zur Verfügung, die als Default für alle Verzeichnis-Absicherungen über mod_auth_ldap gelten. Damit spart man sich bei komplexen Konfigurationen jede Menge doppelten Code, z.B. wenn man viele Ordner hat, deren Zugriff auf immer verschiedene Active Directory-Gruppen eingeschränkt ist.

Da ich heute Ablenkung von Java brauchte, habe ich den alten Quellcode auf Apache 2.2.18 portiert. Dabei ist mir dann auch folgendes im Zusammenspiel mit der Kompilierung unter Visual Studio 2008 aufgefallen: Ich hatte die Apache.dsw geöffnet, den Patch eingespielt und dann versucht Das Projekt mod_authnz_ldap zu kompilieren. Allerdings brach die Kompilierung immer mit einem “rc.exe meldete Fehler 1” ab.
Nach kurzem Überprüfen der Log-Dateien stellte ich fest, dass die Präprozessordefinitionen für alle notwendigen Projekte (libhttpd, mod_ldap und mod_authnz_ldap) so nicht unterstützt werden. In den Definitionen steht z.B. BIN_NAME=”mod_authnz_ldap.so”, was allerdings auf der Kommandozeile zu … rc.exe /d “BIN_NAME=””mod_authnz_ldap.so”” … wird. Das Quoting ist also falsch. Deshalb musste ich alle Vorkommen der doppelten Anführungszeichen durch einfache Anführungszeichen innerhalb der Projekteigenschaften (Ressourcen > Allgemein > Präprozessordefinition) ersetzen. Danach funktionierte auch die Kompilierung.

Im Github-Repository des Patches liegen neben dem eigentlichen Patch auch die fertig kompilierten Module für Windows bereit, so dass man diese sofort nutzen kann.

Update 2012-01-19: In Version 2.2.21 ist der Fehler immer noch vorhanden.

Funkauswerter monitord um ActiveMQ-Support erweitern

Unsere bisherigen Pläne für ZABOS sehen vor, dass wir die Funktionalität der Alarmierung per SMS über Fünfton-Folgeruf (ZVEI) erweitern – nämlich um die digitalen Protokolle POCSAG und FMS. Unserer bisheriger Fünfton-Folgerufempfänger ist ein selbstenwickeltes Board mit Mikrocontroller, der die Fünfton-Folgen aufnimmt und dann über die serielle Schnittstelle an ZABOS weiterreicht.

Bereits vor einiger Zeit hatten wir begonnen, uns nach Alternativen umzuschauen, denn die Eigenentwicklung der Auswertungssoftware für die digitalen Protokolle würde zu viel Zeit in Anspruch nehmen. Außerdem war für jedes neue ZABOS-System ein neues Mikrocontroller-Board nötig. Da wir keine Hardwareschmiede sind, wollten wir darauf verzichten.
Als Alternativen liefen uns schließlich FMS Crusader über den Weg und außerdem auch das Open Source-Tool monitord, das recht gut in unsere zukünftige Produktstrategie passte.

monitord ist in C++ geschrieben und läuft sowohl unter Linux als auch unter Windows. Mit einer Lua-Scripting-API kann die Funktionalität des Systems erweitert werden. Allerdings wollte ich aus architekturbedingten Gründen keinen REST-Service auf ZABOS-Seite zum Übermitteln der Alarmierungen schreiben.
Stattdessen sollten die eingehenden Alarmierungen für zukünftige ZABOS-Versionen in einer Message-Queue in Form von ActiveMQ auflaufen.
ZABOS kann dann innerhalb von ActiveMQ z.B. drei verschiedene Topics (zabos.pocsag, zabos.zvei und zabos.fms) abonnieren und die jeweiligen Schleifen im System alarmieren, sobald eine Nachricht in dem jeweiligen Topic veröffentlicht wird. monitord hingegen nimmt die Funksignale über zwei Soundkarten auf (damit werden wir das Board mit dem Mikrocontroller und die nötige serielle Schnittstelle los), verarbeitet sie und und publiziert sie im passenden Topic der Queue.

Neben der Lua-API existiert in monitord auch eine eigene Plugin-Schnittstelle die z.B. für das MySQL-Plugin genutzt wird. Diese Plugins können dann direkt in C++ geschrieben werden.

Nachdem ich mich mit dem gesamten Konzept sehr schnell angefreundet hatte, schaute ich erst einmal nach, ob ActiveMQ denn irgenwelche Bindings für C++ besaß. Glücklicherweise haben die Jungs von Apache (wie so oft) gute Arbeit geleistet und somit konnte ich mir ActiveMQ-CPP herunterladen und kompilieren. Vorher musste ich noch libaprutil1-dev nachinstallieren.
Das der ActiveMQ-CPP-Distribution beiliegende Beispiel unter src/examples/producers/SimpleProducer.cpp war für mein Vorhaben genau der richtige Einstieg – mehr sollte mein Plugin ja auch eigentlich nicht können.

ActiveMQ-CPP hatte ich also soweit installiert – nun ging es daran, monitord mit ActiveMQ bekannt zu machen. Zuerst ließ ich den monitord-Quellcode kompilieren und testete, ob dieser soweit überhaupt in Ordnung war.
Das Kompilieren funktionierte zwar, allerdings konnte der Daemon meine Soundkarte nicht erkennen. Erstaunlicherweise stand im Log, dass er über OSS auf die Soundkarte zugreifen wollte, obwohl ich explizit in der monitord.xml als Soundkarte “plughw” verwendet und auch beim Kompilieren –with-alsa mitgegeben hatte. Ich schaute mir die configure-Ausgabe noch einmal an und stellte fest, dass das Paket libasound2-dev für ALSA benötigt wurde – ist irgendwie auch klar -, ich es aber nicht installiert hatte. Das configure-Script brach aber bei fehlenden Headern nicht ab, sondern kompilierte stattdessen mit OSS-Support.
Ich also libasound2-dev nachinstalliert und meine Soundkarte wurde schließlich erkannt.

Die rudimentäre Logik zum Laden der ActiveMQ-Konfiguration innerhalb der monitord.xml, dem Initalisieren der Verbindung u.s.w. tipperte ich in knapp drei Stunden runter, nachdem ich immer wieder mal in die C++-Dokumentation schauen musste. Glücklicherweise konnte ich viel von dem ActiveMQ-Beispiel als auch von dem monitord-MySQL-Plugin übernehmen.

Anschließend erweiterte ich die configure.ac und Makefile.ac um die nötigen ActiveMQ-Abhängigkeiten und konnte mit –with-activemq auch mein Plugin kompilieren.

Das ActiveMQ-Plugin ist noch nicht vollständig, sowohl Testen als auch das Veröffentlichen der Nachrichten in der Queue ist noch nicht implementiert. Ist aber auch nicht wild, da es mir erst einmal um die Evaluierung des Konzepts ging. Und das ging alles recht schnell, obwohl ich von C++ und den GNU-Buildtools null Ahnung habe 😉
Die geänderten Sourcen habe ich auf Github veröffentlicht – enthält natürlich noch massig Bugs und ist ungetestet.

Apache Tomcat für Azubis

Nachdem ich vor ein paar Wochen den Artikel über Spring für Azubis veröffentlicht habe, wird es heute Zeit für “Apache Tomcat für Azubis”.
Dieser Artikel hat nicht ansatzweise den Anspruch, die komplette Konfiguration eines Tomcat-Servers zu beschreiben – dafür ist die offizielle Dokumentation gedacht. Hier geht es darum die grobe Funktionsweise zu erklären. Im Folgenden definiere ich Web-Applikation als eine Anwendung, die in Java geschrieben ist und in einem Applikationsserver läuft.

Was ist der Apache Tomcat?

Spätestens ein paar Stunden nach dem Einstieg in die Web-Programmierung mit Java stellt sich die Frage: “Wie bekomme ich den Mist überhaupt zum Laufen?”. Von der Entwicklung mit PHP ist man gewöhnt, dass die Applikation nur in eines der Verzeichnisse des Web-Servers geschoben werden muss und das war es dann schon – klar, PHP muss vorher noch in den Webserver als CGI oder Modul eingebunden sein.

Der Apache Webserver liefert bei einer Anfrage über HTTP oder HTTPS die jeweiligen (geparsten) Dateien zurück.
Nichts Anderes macht der Apache Tomcat auch. Tomcat ist ein Webserver, der speziell auf die Anforderungen von Web-Applikationen unter Java zugeschnitten ist.

Spezifikationen

Einige Hintergrundinfos sind nötig: Über den Java Community Process (JCP) werden in regelmäßigen Abständen so genannte Java Specification Requests (JSR) veröffentlicht. Diese beinhalten Standardisierungen für die Java-Umgebung. Oracle, damals Sun, hat 1997 mit der Version 1.0 die erste Version der Servlet-API definiert und diese mit der Version 2.3 im August 2001 dem JCP zugängig gemacht. Seit dieser Zeit werden Änderungen in der Servlet-API gemeinsam von der Java-Community bestimmt.

Die Servlet-API beschreibt anhand von Interface-Definitionen, welche Schnittstellen ein Applikations-Server implementieren bzw. bereitstellen muss.

Warum die Spezifikation?

Für verschiedene Anwendungsfälle gibt es verschiedene (Java-)Applikationsserver. Der bekannteste unter ihnen ist sicherlich Apache Tomcat. Daneben gibt es noch Jetty (freie Implementierung, ist relativ klein und kann in Embedded-Umgebungen gut eingesetzt werden), Websphere (kommerzielle Lösung von IBM) und einige andere.

Damit nun eine Applikation unter allen Applikationsserver funktionieren kann, muss jeder Server die Servlet-API bereitstellen. Oracle/Sun sichert über Test-Suiten ab, dass die Applikationsserver auch mit der Servlet-API kompatibel sind und den Anforderungen in dem JSR entsprechen.

Der Entwickler einer Webapplikation kann sich nun sicher sein, dass die Anwendung unter jedem Applikationsserver lauffähig ist. Die Entscheidung, welcher Server einzusetzen ist, hängt von den Anforderungen des Kunden oder des persönlichen Geschmacks ab.

Versions-Wirr-War(r)

Mit jeder neuen Version der Servlet-API werden neue Funktionalitäten aufgenommen, die die aktuellen Anforderungen im Web-Bereich widerspiegeln.

Wenn man sich den Eintrag in der englischen Wikipedia zu Servlets anschaut sieht man, dass einige Versionen existieren. Aktuell ist die Version 3.0, die im JSR 315 definiert ist. Eine der größten Erweiterungen ist die Definition von asynchronen Servlets, die durch die starke Verbreitung von AJAX-Funktionalitäten im Internet längst überfällig gewesen ist. Der Apache Tomcat 7 implementiert die Version 3.0 der Servlet-API und kann somit auch die neuen Funktionaliten bereitstellen.

Allerdings wird bei vielen Projekten weiterhin die letzte Version 2.5 eingesetzt – hierfür wird der Apache Tomcat 6 eingesetzt. Da Version 3.0 aber i.a.R. abwärtskompatibel ist, können für die Servlet-API 2.5 auch der Tomcat 7 eingesetzt werden.

Übersicht über die Servlet-API

Die Spezifikation für JSR 153/Servlet 2.5 ist knapp 350 Seiten lang, diese hier alle zusammenzufassen wäre unsinnig. Statt dessen will ich kurz die wichtigsten Themen behandeln.

Jede Web-Applikation basiert auf einem oder mehreren Servlets. Servlets sind Klassen, die von der abstrakten Klassen HttpServlet erben.Diese stellt Methoden bereit, die beim Aufruf des Servlets aufgerufen werden. doGet(…) wird bei einem HTTP-GET-Request, doPost(…) bei einem HTTP-POST-Request u.s.w.

Der Entwickler implementiert nun diese Methoden und kann über die Parameter vom Typ HttpServletRequest (eingehender HTTP-Request) und HttpServletResponse (zu erzeugende HTTP-Antwort) die Ein- und Ausgabe des HTTP-Streams steuern.

Klingt einfach, ist es an sich auch. Frameworks wie Spring kapseln all ihre Methoden um diese drei wichtigsten Klassen – und natürlich noch einigen anderen.

JavaServer Pages / JSP

JSP? WTF? Von PHP sind sicherlich Dateien bekannt, die wild PHP-Quellcode und HTML-Ausgabe mischen. JSP-Dateien sind das Gegenstück zu eben diesen PHP-Dateien. In ihnen kann HTML- und Java-Code parallel stehen.

Intern passiert beim Aufruf einer JSP-Datei nichts weiter, als dass bei ihrem ersten Aufruf die Datei in ein Servlet umgewandelt wird. Alle HTML-Fragemente werden über out.println() ausgegeben. Bei erneutem Aufruf der Datei wird nun das cachete Servlet ausgeführt. Sollte sich die JSP-Datei ändern, wird sie beim nächsten Aufruf neu kompiliert.

Datei web.xml und Ordner WEB-INF

Jede Web-Applikation muss im Hauptverzeichnis den Ordner WEB-INF und darin die Datei WEB-INF/web.xml besitzen. Der Inhalt des Ordners WEB-INF kann von außerhalb, d.h. vom Benutzer nicht aufgerufen werden. Er enthält für die Applikation alle wichtigen Dateien, dazu zählen die kompilierten Java-Dateien (Ordner WEB-INF/classes) und eben die Datei web.xml. Diese beschreibt die grundlegende Konfiguration der Applikation. Die web.xml ist ein so genannter Deployment Descriptor.

web.xml

Kommen wir nun zum simplen Aufbau des Deployment Descriptors

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
       <env-entry>
                <env-entry-name>environmentVariable</env-entry-name>
                <env-entry-value>true</env-entry-value>
                <env-entry-type>java.lang.Boolean</env-entry-type>
        </env-entry>
        <servlet>
                <servlet-name>myServlet</servlet-name>
                <servlet-class>de.ckl.servlet.MyServlet</servlet-class>
                <description>Serlvet</description>
                <init-param>
                        <param-name>debug</param-name>
                        <param-value>true</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet>
                <servlet-name>myLaterServlet</servlet-name>
                <servlet-class>de.ckl.servlet.MyLaterServlet</servlet-class>
                <description>Spaeter geladenes Servlet</description>
                <init-param>
                        <param-name>debug</param-name>
                        <param-value>false</param-value>
                </init-param>
                <load-on-startup>10</load-on-startup>
        </servlet>
        <servlet-mapping>
                <servlet-name>myServlet</servlet-name>
                <url-pattern>/servlet/*</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
                <servlet-name>myLaterServlet</servlet-name>
                <url-pattern>/later/*</url-pattern>
        </servlet-mapping>
</web-app>

Das Root-Tag web-app enthält den zu benutzenden Namespace und die dahinter liegende XSD. Wenn die Zuordnung nicht stimmt, kann vom Applikationsserver die Anwendung nicht gestartet werden. Also Achtung!

Das Tag env-entry legt anhand von Key-Value-Paaren applikationsweite Einstellungen fest, die für alle Servlets der Applikation gültig sind.

Über servlet werden nun die einzelnen Servlets definiert. servlet-name muss für jedes Servlet eindeutig sein, servlet-class verweist auf die Klasse, die HttpServlet implementiert. Mit init-param können Servlet-spezifische Einstellungen festgelegt werden. Im obigen Beispiel wird das Debugging für myServlet aktiviert, bei myLaterServlet hingegen deaktiviert.

Damit eine Reihenfolge zum Starten festgelegt werden kann – z.B. um Ressourcen wie Datenbankverbindungen zu initalisieren, wurde das Tag load-on-startup eingeführt. Je kleiner der Wert, desto früher wird das Servlet gestartet.

Wir haben nun also die Servlets und deren Einstellungen konfiguriert. Es geht nun darum, dass die Servlets auch im Browser aufgerufen werden. Im obigen Beispiel werden alle HTTP-Anfragen (das Verb – also GET, POST etc. ist dabei egal) auf die URL http://…/mein_context/servlet/* auf das Servlet myServlet gemappt (siehe erstes servlet-mapping-Tag), die URLs unter http://…/mein_context/later/* hingegen auf auf myLaterServlet.

Deployment

Das Einspielen der Webapplikation in den Tomcat nennt man Deployen. Entweder man hat die Applikation bereits manuell eingerichtet und kopiert die neuen Dateien einfach in das Applikationsverzeichnis oder aber man spielt ein .war ein. Ein WAR (Web Archive) ist nichts weiter als eine ZIP-Datei, die im Verzeichnis webapps des Tomcats kopiert wird. Der Tomcat überwacht den Inhalt des Verzeichnisses und extrahiert bei einer neuen .war-Datei diese in das Verzeichnis webapps/war-datei. Der Vorgang wird Hot-Deployment genannt.

Wir haben beispielsweise die Datei my-test-applikation.war. Diese wird nach webapps kopiert. Der Tomcat entpackt den Inhalt des Archivs automatisch nach webapps/my-test-applikation.

Das Archiv enthält alle Dateien, die von der Web-Applikation benötigt werden. Dazu gehören kompilierte Klassen (/WEB-INF/classes), abhängige Libraries (/WEB-INF/lib), der Deployment-Deskriptor (/WEB-INF/web.xml) und JSP-Dateien, Templates u.s.w.
Als Entwickler muss man immer im Hinterkopf behalten, dass die Anpassung der globalen conf/server.xml bzw. conf/web.xml des Tomcats während des Deployments nichts angetastet werden soll. Dadurch wird sichergestellt, dass die Applikationen zum einen einfach zu deployen und zum anderen unabhängig von dem eingesetzten Application-Server sind. In einem Jetty existiert z.B. keine server.xml!

Context Path

Die Applikation an sich ist dann unter http://localhost/my-test-applikation erreichbar. Den Pfad hinter http://localhost/ wird Context Path genannt und ist unabhängig von dem realen Ordner!

In der Datei conf/server.xml wird automatisch beim Hot Deployment ein neuer Konfigurationseintrag erzeugt:

<Host name="localhost" debug="0" appBase="webapps"
	      unpackWARs="true" autoDeploy="true">
    <Context path="my-test-applikation" docBase="webapps/my-test-applikation" debug="0"/>
</Host>

Über das path-Attribut wird festgelegt, dass alle Anfragen auf die URL http://localhost/my-test-applikation an die Applikation geht, die im Odner (docbase) webapps/my-test-applikation liegt.

Lifecycle

Beim Starten des Tomcats werden alle Applikationen, d.h. jeder definierte Context gestartet. Dazu überprüft Tomcat die Datei web.xml und startet jedes Servlet in der angegebenen Reihenfolge.

Sobald nun eine HTTP-Anfrage an den Tomcat gerichtet ist, wird zuerst überprüft, ob der Context Path definiert ist. Bei Existenz wird an das Servlet übergeben, auf dem das Mapping der URL zutrifft.

Beim Beenden des Tomcats werden alle laufenden Servlets beendet.

Wichtigster Unterschied zu PHP-Webapplikationen

Beim Umstieg von PHP-Webapplikationen zu Java gibt es neben den sprachlichen Unterschieden folgende, gravierende Differenz, die man als Entwickler immer im Hinterkopf haben muss: Unter PHP wird bei jedem Seitenaufruf das Script geparst – bei Java hingegen wird eine Applikation zu Beginn ihres Lebenszyklus gestartet und läuft die ganze Zeit!

Dafür ist es wichtig, dass man die verschiedenen Scopes kennt:

  • Web Context / Application – javax.servlet.ServletContext
    Dieser Kontext umfasst die komplette Applikation und existiert vom Start des Servlets bis zum Herunterfahren
  • Session – javax.servlet.http.HttpSession
    Eine Session unter PHP ist bekannt? Dieser Scope ist identisch dazu.
  • Request – javax.servlet.HttpServletRequest
    Der Request-Scope ist so lange verfügbar, wie eine HTTP-Anfrage dauert.
  • Page – javax.servlet.jsp.PageContext
    Bei der Arbeit mit JSP-Dateien existiert dieser Scope. In ihm sind nur Objekte verfügbar, die innerhalb der Seite definiert worden sind.

Objekte im Scope Application können von allen Servlets gesehen werden, es existiert immer nur ein Application-Scope innerhalb einer Anwendung. Session-Scopes existieren für jeden Benutzer, solange er mit der Applikation kommuniziert. Ein Benutzer kann nicht auf die Session eines anderen zugreifen (zumindest nicht ohne Tricks).
Bei 10 HTTP-Anfragen existieren auch 10 Request-Scopes, die i.a.R. jeweils einer Session zugeordnet sind.

Entwicklung mit Eclipse

Eclipse bzw. WTP bindet den Applikationsserver direkt in die IDE mit ein.

Integration

Zuerst muss unter Preferences -> Server -> Runtime Environments ein neuer Server hinterlegt werden. Dazu muss über den Button Add zuerst die verwendete Tomcat-Version ausgewählt und danach das Installationsverzeichnis angegeben werden. Unter File -> New -> Other -> Server -> Server muss danach ein neuer lokaler Server eingerichtet werden. Im folgenden Dialog muss unter Apache die verwendete Version ausgewählt werden. Die letzte Seite des Wizards mit dem Namen Add and Remove wird dazu verwendet, um Projekte vom Typ Dynamic Web Project automatisch der Tomcat-Instanz zuzuweisen.

Nachträgliche Änderung eines Projekts in Dynamic Web Project

Falls ein Projekt nachträglich in ein Dynamic Web Project umgewandelt werden soll, geschieht das über einen Rechtsklick auf den Projektordner im Project Explorer. Dort muss unter Project Facets der Eintrag Dynamic Web Module ausgewählt werden.
Die Aktivierung sorgt dafür, dass das Projekt von Eclipse als Web-Projekt erkannt wird.

Ordnerstruktur

Eclipse benutzt standardmäßig den Ordner WebContent des Projekts als Hauptverzeichnis für die Dateien, die automatisch im Tomcat deployt werden. Wenn das Verzeichnis nicht WebContent lauten soll, muss im Projektordner die Datei .settings/org.eclipse.wst.common.component angepasst werden. Dort lässt sich definieren, welche Dateien in den Tomcat deployt werden sollen.

Automatisches Deployment

Was macht nun Eclipse beim Starten der lokalen Tomcat-Instanz?

  • Zuerst werden alle Klassen kompiliert, die in einem Source-Ordner liegen.
  • Danach wird, falls noch nicht vorhanden, unter workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmpN/wtpwebapps der Ordner des Context-Paths erstellt – das N bei tmpN steht dabei für die Nummer der jeweiligen Serverinstanz
  • Anhand der XML-Datei .settings/org.eclipse.wst.common.component werden nun alle dort definierten Dateien in den Context-Path kopiert
  • Eclipse startet nun eine neue Tomcat-Instanz, bei der das Verzeichnis mit den Konfigurationsdateien auf Servers -> Apache Tomcat x.x at localhost-config gemappt wird. Somit wird sichergestellt, dass die lokale Tomcat-Installation autark von der Entwicklerinstanz betrieben werden kann.
  • Tomcat lädt die Apache Tomcat x.x at localhost-config/server.xml und erstellt einen neuen Context-Path in der Laufzeitumgebung des Tomcat
  • Alle HTTP(S)-Anfragen des Context-Paths werden somit auf den realen Pfad workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmpN/wtpwebapps/my-test-applikation gemappt

Server Name Indication (SNI) unter Apache 2.0.63 (Windows)

Heute kam bei uns in der Firma wieder das Thema Zertifikate zu sprechen. Unsere momentane Umgebung auf dem auch dieser Blog gehostet ist, ist ein Windows-System, auf dem Apache 2.0.63 läuft. Einige der Subdomains sind über SSL zugänglich, allerdings besteht bei SSL das generelle Problem, dass pro IP-Adresse nur ein SSL-Zertifikat benutzt werden darf. Wenn man mehrere Subdomains als virtuelle Hosts betreibt (https://v1.ecw.de, https://v2.ecw.de u.s.w.) wird immer nur das Zertifikat des ersten VHosts benutzt (im Beispiel das Zertifikat von https://v1.ecw.de).

Seit einiger Zeit bietet das Unternehmen StartCom kostenlose Zertifikate an. Das CA-Zertifikat von StartCom ist in den aktuellen Browsern sowie im Zertifikate-Speicher von Windows verfügbar. Leider besteht nicht die Möglichkeit, ein kostenloses Wildcard-Zertifikat zu erhalten. StartCom teilt nur Zertifikate vom Typ C1 und nicht C2 kostenlos aus.

Mit dem Wildcard-Zertifikat wäre es möglich gewesen, dass alle VHosts unter https://*ecw.de sich ein Zertifikat teilen. Die andere Möglichkeit, für jede Domain ein eigenes Zertifikat einzurichten, entfällt aus den bereits genannten Gründen: pro IP nur ein Zertifikat.

Seit 2003 existiert die RFC 3456, in der Server Name Indication (SNI) definiert wird. Damit ist es möglich, pro IP-Adresse mehrere Zertifikate zu benutzen. Leider implementieren bis jetzt nur wenige Webserver SNI nativ.

Als ich im Internet nach dem Thema googelte, wurde ich in Wolfs Blog fündig. Er beschrieb die Implementierung und Kompilierung von SNI unter Apache 2.2.xx. Durch seine Ausführungen war ich mehr als motiviert und hatte nun den Plan, SNI unter Apache 2.0.63 und Windows zum Laufen zu bringen.

Durch meine letzten Tätigkeiten beim mod_auth_ldap-Patch hatte ich noch die Sourcen von Apache 2.0.63 auf der Festplatte und sammelte nun die benötigten Dateien zusammen:

Nachdem ich die beiden Backport-Patches in meine Sourcen eingespielt hatte, musste ich noch die Lib-Pfade für die OpenSSL-Dateien einpassen und außerdem das zlib-Verzeichnis inkludieren, da OpenSSL irgendwie auf zlib referenzierte.

Die Kompilierung verlief danach ohne Probleme und ich machte mich an das Deployen der Dateien. Wichtig dabei war, das auf dem Server folgende neue Dateien kamen:

  • openssl.exe aus Wolfs-SSL-Package nach Apache2/bin
  • libeay32.dll und ssleay32.dll aus Wolfs-SSL-Package nach Apache2/bin bzw. %SYSTEM32%
  • Release/libhttpd.dll aus dem Apache-Kompiliat nach Apache2/bin
  • modules/ssl/Release/mod_ssl.so nach Apache2/modules

Das Starten des Apaches schlug beim ersten Mal fehl, da ich die libhttpd.dll vergessen hatte zu kopieren. Diese wird aber benötigt, da der Backport von ap_vhost_iterate_given_conn auf diese Library abzielt.
Danach fuhr der Apache normal hoch, ich richtete nun in aller Kürze zwei Virtuelle Hosts in meiner httpd.conf ein und erzeugte zwei Self Signed Certificates. Das Ergebnis war mehr als erfreulich, denn soweit funktionierte alles.
Der nächste Schritt wird für mich sein, dass ich nun unsere Zertifikate von der StartCom-CA signieren lasse.

[download id=”2″]

Sharepoint mit Apache als Reverse Proxy veröffentlichen

Heute stand ich vor der Aufgabe, dass unsere interne Sharepoint-Seite im Apache veröffentlicht werden sollte. An sich hätte das kein Problem sein sollen – war es im Endeffekt aber.

Bei uns schaut es schematisch so aus, dass der Apache alle Anfragen anhand des Domain-Namens auf die virtuellen Server weiterreicht bzw. als Proxy fungiert: Internet -> Apache / DMZ -> Virtueller Host / Backend Server.

Anhand des Blog-Eintrags von Todd Klint den Sharepoint-Service so eingerichtet, dass er auf die richtige URL lauscht. Die Konfigurationsdatei des virtuellen Hosts sah dann folgendermaßen aus:

<VirtualHost *:443>
SSLEngine On
SSLProxyEngine On
SSLCertificateFile "$CERT_FILE"
SSLCertificateKeyFile "$CERT_KEY_FILE"
SSLProxyCACertificateFile "$CERT_CA_FILE"
ProxyRequests Off
ProxyVia On

ServerAdmin admin@sps.mydomain.com
DocumentRoot $VHOST_DIR/sps/web
ServerName sps.mydomain.com:443
ErrorLog "|rotatelogs.exe $VHOST_DIR/sps/logs/%Y%m%d_ssl_error.log 86400"
CustomLog "|rotatelogs.exe $VHOST_DIR/sps/logs/%Y%m%d_ssl_access.log 86400" common

# AddDefaultCharset ISO
LogLevel debug
ProxyPreserveHost Off
KeepAlive Off

ProxyPass /  http://backend-srv:8080/
ProxyPassReverse /  http://backend-srv:8080/

<Proxy *>
Order allow,deny
Allow from all
</Proxy>

<Directory "$VHOST_DIR/sps/">
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

Es ist noch zu sagen, dass der Sharepoint nicht mit HTTPS sondern mit Plain-HTTP läuft. Der Apache kümmert sich um die Verschlüsselung. Im IIS muss außerdem als Authentifizierungsmethode für die Sharepoint-Seite Standardauthentifizerung und nicht Integrierte Windows-Authentfizierung benutzt werden. Apache 2.0.63 kam bei mir mit der Benutzung von NTLM nicht klar.

Ein weiterer Fehler der auftrat war, dass der Sharepoint-Server über zwei Netzwerkkarten verfügte, die zu veröffentlichende SharePoint-Instanz aber an keine IP fest gebunden gewesen ist. Der Apache hatte Probleme damit arge Probleme (sporadisches “Verbindung fehlgeschlagen”), deshalb habe ich die SP-Instanz einer IP direkt im IIS zugewiesen.

Rautiges 2009-09-01

Durch das Konzert und Projekte in der Firma bin ich immer noch im Stress, deshalb hier mal wieder etwas Rautiges.

  • Am Freitag den 4. September hat unsere Band – die Rhythmutants – unseren ersten Auftritt. Mittlerweile läuft auch alles so, wie es soll. Ich bin stark gespannt, wie das Konzert dann ablaufen wird. Die Woche ist noch geprägt von jeder Menge Bandproben.
    Den letzten Samstag hat die Band + Support-Crew bis spät in die Nacht geprobt und Party gemacht. War sehr lustig – aber auch anstrengend.
  • Für mod_auth_ldap habe in der vergangenen Woche einen Patch geschrieben, mit dessen Hilfe es möglich ist, für einen (virtuellen) Server einmalig die LDAP-Verbindungseinstellungen zu setzen, die dann für den kompletten (virtuellen) Server gelten. Mich hat es mehr als angepestet, dass ich für jede Ressource die Zugangsdaten (AuthLDAPBindDN, AuthLDAPBindPassword und AuthLDAPURL) immer wieder setzen musste. Nun existieren die Parameter AuthLDAPGlobalURL, AuthLDAPGlobalBindDN und AuthLDAPBindPassword.
  • Unser Haus-internes Kicker-Team, bestehend aus Christoph, Marc, Florian, Mandy, Hendrik und mir, hat am vergangenen Donnerstag den ersten Platz beim 1. IT-Region 38 Kicker-Cups belegt. Es war ein äußerst spannender und unterhaltsamer Abend, den unser ECW-Badabäääm-Team erfolgreich abschließen konnte. Fotos vom Turnier gibt es bei der IT Region 38, Christoph hat weiterhin noch ein kurzes Interview gegeben.
    Nach dem Turnier haben Marc und ich am Klieversberg noch bis in die frühen Morgenstunden unseren grandiosen Sieg gefeiert.
  • Beim Studium habe ich heute den Abschlusstest für Rechnerstrukturen und Betriebssysteme abgelegt – und überraschend nicht bestanden. 2 der Tests wurden meiner Meinung nach vom System falsch ausgewertet, obwohl sie offensichtlich richtig waren, denn es mussten nur die Lösungen aus dem Buch abgeschrieben werden. Ich habe Bianca über diesen Missstand informiert und hoffe auf eine baldige Antwort – mein Tutor ist leider noch bis zum 6. September im Urlaub, so dass mir der richtige Ansprechpartner momentan nicht zur Seite steht. Ist alles sehr ärgerlich.
  • Die Migration unserer virtuellen Maschinen auf den Xen-Server haben wir erfolgreich hinter uns gebracht, auch wenn es einige Probleme gab. Nun läuft aber wieder alles. Damit steht – sobald es Zeittechnisch machbar ist – der Einführung eines Build-Servers nichts mehr im Wege. Erste Tests mit CruiseControl und unserem Deployment-Tool habe in der letzten Woche bereits gemacht.
    Sobald die komplette Infrastruktur steht, werde ich dazu einen ausführlichen Artikel schreiben.
  • Heute ist das erste Treffen zur bevorstehenden Ruderregatta am 12. und  13. September am Allersee. Ich bin sicher, dass die Ruderregatta wie jedes Jahr eine Mordsgaudi wird 😉