mod_rewrite, Let’s Encrypt en een vervelend probleem

Ik ben een fervent aanhanger van Let’s Encrypt, en van het versleutelen van verkeer in het algemeen. Al mijn publieke en private sites hebben dus een certificaat, dat bij voorkeur automatisch vernieuwd wordt.

Maar al een tijdje is er een probleem met één van die sites (een Mattermostserver achter een Apache reverse proxy), waarbij het hernieuwen van het certificaat (via cron) om de één of andere reden faalt. Op zich was er met DNS en de configuratie van de webserver niets mis: die was identiek aan alle andere systemen.

Het enige verschil was dat er een groot aantal RewriteRule’s geconfigureerd stonden, met daarin een aantal catch-all-regels. Nu is het zo dat ik altijd een RewriteRule toevoeg voor Let’s Encrypt, om alle aanvragen voor de acme-challenge-token op een centrale plaats te bewaren:

RewriteRule ^/\.well-known/acme-challenge/(.*)$ /var/letsencrypt/.well-known/acme-challenge/$1

Door een te snelle lezing van de documentatie was ik vergeten dat Apache alle regels die matchen uitvoert, niet alleen de eerste. Hierdoor werden alle aanvragen doorgestuurd naar de achterliggende Mattermostserver, die, terecht, antwoordde met 403 Forbidden. En dan werkt Let’s Encrypt natuurlijk niet.

De oplossing staat gelukkig ook in de documentatie:  de [L]-flag zorgt ervoor dat de huidige regel meteen ook de laatste regel is die wordt geïnterpreteerd.

RewriteRule ^/\.well-known/acme-challenge/(.*)$ /var/letsencrypt/.well-known/acme-challenge/$1 [L]

Et voilà, alle requests van Let’s Encrypt worden meteen correct doorgestuurd, en de andere regels worden zelfs niet meer bekeken. En ik heb mijn certificaten terug!

Environment variables in wsgi-applicaties

Soms heb je een environment variable nodig in een wsgi-applicatie, zoals bijvoorbeeld LD_LIBRARY_PATH omdat een bepaalde C-bibliotheek niet beschikbaar is.

De normale manier om dat te doen is via mod_env en SetEnv in de VirtualHost-definitie, maar voor wsgi-applicaties werkt dat niet, omdat die gestart worden voor de eerste request. SetEnv wordt enkel uitgevoerd bij de eerste request naar een site.

Je moet die variables meegeven aan het apache (httpd)-proces zelf, met dien verstande dat ze dan voor alle websites en webapplicaties beschikbaar zijn (wat misschien niet de bedoeling is). Op Ubuntu-gebaseerde systemen moet dat in /etc/apache2/envvars, op RedHat-gebaseerde systemen gebruiken /etc/sysconfig/httpd:

LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/opt/apa/lib"

Een procesherstart later zou de variable moeten beschikbaar zijn.

WSGIPassAuthorization

Een RESTful API die niet helemaal publiek mag zijn is meestal beveiligd met een variant van HTTP Basic Authentication. Daar is natuurlijk niets mis mee, want het is veruit de eenvoudigste manier voor een API-consument om zich te identificeren.

Maar onlangs stootte ik toch op een caveat emptor: wanneer je een API in Python (bv. Flask) via mod_wsgi beschikbaar stelt, dan werkt het plots niet meer om met Basic Authentication aan te melden. Mysterie, mysterie, zeker omdat het in de ontwikkelomgeving wel werkte.

Na lang (~ 1/2e dag) zoeken bleek mod_wsgi de schuldige (en mijn gebrekkige lectuur van de handleiding, want het is gedocumenteerd). In een standaardopstelling worden de authorization headers immers onderschept door Apache, die zelf de authenticatie wil uitvoeren. Uiteraard wist ik dat niet, en had Apache ook niet zo geconfigureerd. En dus stond ik voor het mysterie van de missende header.

Maar via wat google-fu en een blogpost kwam ik dan toch op de oplossing. In de vhost (of elders, al naargelang) waar je de wsgi-applicatie hebt geconfigureerd moet je de instelling WSGIPassAuthorization op On zetten (en dan de webserver herstarten, dat spreekt).

WSGIPassAuthorization On

En hoera! De authenticatie werkt! Op naar het volgende probleem!