Tag Archives: lighttpd

Troubleshooting nextcloud running on lighttpd

For historic reasons my personal nextcloud runs on lighttpd. (In fact I started in 2014 with ownCloud on Debian 8 (Jessie) and never changed the webserver.) This works, although it is not a first citizen supported platform, but after all nextcloud just needs some PHP/MySQL, so the webserver should not matter, should it?

After upgrading to nextcloud 15 and installing the social app/plugin, I got some warnings about missing rewrites/redirects. So it seems there are some new rewrites for service discovery in town, you can learn about those on the troubleshooting page of the administration manual.

You get config snippets for Apache and nginx (which are by far the most used webservers, I don’t blame nextcloud for not giving advise on every exotic webserver out there). For lighttpd you have to do it on your own and this is what I came up with. Load mod_redirect and mod_rewrite in your main lighttpd.conf and for the nextcloud config add this:

        # for social app
        # TODO  After upgrade to lighttpd > 1.4.50 revisit
        #       https://redmine.lighttpd.net/projects/1/wiki/docs_modrewrite
        url.rewrite-once = (
                # Match when there are no query variables
                "^/.well-known/webfinger$" => "/public.php?service=webfinger",
                # Match query variables and append with &
                "^/.well-known/webfinger\?(.*)$" => "/public.php?service=webfinger&$1"
        )

        url.redirect = (
                "^/.well-known/caldav$" => "/remote.php/dav",
                "^/.well-known/carddav$" => "/remote.php/dav"
        )

If you installed in a subfolder (the subfolder is still called owncloud here, adapt if needed):

        # for social app
        # TODO  After upgrade to lighttpd > 1.4.50 revisit
        #       https://redmine.lighttpd.net/projects/1/wiki/docs_modrewrite
        url.rewrite-once = (
                # Match when there are no query variables
                "^/owncloud/.well-known/webfinger$" => "/owncloud/public.php?service=webfinger",
                # Match query variables and append with &
                "^/owncloud/.well-known/webfinger\?(.*)$" => "/owncloud/public.php?service=webfinger&$1"
        )

        url.redirect = (
                "^/owncloud/.well-known/caldav$" => "/owncloud/remote.php/dav",
                "^/owncloud/.well-known/carddav$" => "/owncloud/remote.php/dav"
        )

For the future I consider changing the webserver, because lighttpd is not recommended by SabreDAV, which is used by nextcloud. But that’s not decided yet, for today this is it.

Getting CDash to work on Debian 9 (stretch) with lighttpd and MariaDB

After reading It’s Time To Do CMake Right and The Ultimate Guide to Modern CMake I stumbled about the slides of Effective CMake by Daniel Pfeifer. (I did not watch the related video C++Now 2017: Daniel Pfeifer “Effective CMake” though.)

What attracted my attention where the commands ctest_coverage() and ctest_memcheck() from the slide about CTest which comes with CMake and which I already use. In libcgi and some non free projects I create additional tests to be run with valgrind if that tool is found on the build host, but when using CTest/CDash I don’t need to do that and also get coverage tests on top, so I set up a local CDash server on my workstation, which was painful in multiple ways.

After extracting the CDash archive and configuring lighttpd to server its PHP files the install.php came back with the following error message:

Specified key was too long; max key length is 767 bytes

It was not easy to find the cause. The web says this is fixed in MariaDB 10.2.x while my Debian stable still has 10.1.x … and I found only some workarounds on that problem for other projects than CDash. I could “solve” that by changing the database collation from utf8mb4_unicode_ci to utf8_unicode_ci in phpMyAdmin on the still empty database cdash before running install.php.

The more challenging problem was to actually submit results to the CDash server when calling CTest. The webserver always responded with HTTP Status Code 417. That was only partly fault of CDash, which seems to call curl with some strange (?) headers for submission. That turned out to trigger some (from my side) unexpected behavior in lighttpd, for which several tickets exist, I found #1017 eventually, which led me to the lighttpd 1.4.21 release info giving the hint I needed. I added this somewhere in my lighttpd config files:

server.reject-expect-100-with-417 = "disable" 

In the end I have a local CDash instance now and could already submit some helpful coverage and memcheck results. Best thing: This way I can remove the error prone additional tests from my CMakeLists.txt and still run the tests with valgrind, even more flexible than ever.

letsencrypt mit acme.sh und lighttpd

Vorgeschichte

Auf meinem Heimserver läuft owncloud, nein nicht nextcloud, sondern das Debian-Paket1. Konkret läuft das in einer virtuellen Maschine (troubadix) auf einem lighttpd. Ganz grob hatte ich das schonmal in owncloud mit lighttpd auf Debian Jessie beschrieben. An die VM komm ich aber nicht von außen ran, wenn ich das will, muss ich über den Reverse Proxy (nginx), der auf einer anderen VM (susan) läuft. Da mobile Geräte intern wie extern über die gleiche Adresse zugreifen sollen, gibt es dafür die Adresse owncloud.home.example.com2 und von extern landet man damit über die externe IPv4 und Port Forwarding im Router auf dem Reverse Proxy. Mein interner DNS3 löst die selbe Adresse als Alias bzw. cname direkt auf die VM mit der owncloud Instanz auf. Zugriffe sollen alle verschlüsselt ablaufen, zuvor sorgte ein von Hand installiertes Zertifikat von CAcert dafür. Der aufmerksame Leser wird sich denken können: dieses musste einmal im lighttpd (troubadix) und einmal im nginx (susan) installiert werden.

Das funktionierte alles soweit, bis das Zertifikat abgelaufen war. Aus diversen Gründen sollte das neue Zertifikat von letsencrypt kommen.

Zertifikate von letsencrypt

Was letsencrypt ist oder macht, die Erklärung spar ich mir an dieser Stelle. Fakt ist, dass es Alternativen zum offiziellen sogenannten certbot gibt und nicht zu knapp: ACME Client Implementations. Ich hatte mir kurz certbot und dehydrated angesehen, einige der gelisteten Implementierungen wird es ab Debian 9 (Stretch) auch direkt als Debian-Paket geben. Zum Einsatz kommt bei mir jetzt aber acme.sh, da ich es schnell verstanden habe und zügig einrichten konnte, naja fast. Ein bisschen tricky war es die webroot-Methode so einzurichten, dass auf der eigentlich voll verschlüsselten Domain das acme challenge Verfahren unverschlüsselt ablaufen kann.

acme.sh

Zur Installation von acme.sh hab ich als normaler User (alex) das GitHub-Repository geklont und etwas widerwillig mit ./acme.sh --install --accountemail post@lespocky.de installiert. Der Empfehlung das als root zu machen, würde ich nicht folgen. Eher legt man sich dafür noch einen extra User an, ich hab hier meinen normalen User benutzt, was aus security-Sicht auch nicht zu empfehlen ist. ;-) Letztlich braucht dieser Nutzer nur Schreibrechte in seinem eigenen $HOME und in einem Ordner, den der Webserver als webroot ausliefern kann.

Blöd: acme.sh hängt ungefragt folgende Zeile an meine ~/.zshrc an:

. "/home/alex/.acme.sh/acme.sh.env"

Der Inhalt dessen:

export LE_WORKING_DIR="/home/alex/.acme.sh"
alias acme.sh="/home/alex/.acme.sh/acme.sh"

Die beiden Zeilen hab ich aus der ~/.zshrc entfernt und stattdessen richtigerweise an ~/.zshenv angehängt. Ein entsprechendes Ticket upstream ist angelegt. Davon abgesehen, funktioniert das Skript aber sauber. Folgenden Aufruf hab ich benutzt:4

acme.sh --issue -d owncloud.home.example.com -w /var/www/html/le --reloadcmd 'sudo -n /usr/sbin/service lighttpd reload' --post-hook '/home/alex/.acme.sh/create-lighty-pemfile.sh owncloud.home.example.com'

Wie man schon sieht, werden die challenge sachen später in einem webroot in oder unterhalb von /var/www/html/le landen. Die Dateirechte und Gruppenzugehörigkeit dieses Ordners sind so gesetzt, dass der unpriviligierte Nutzer mit seinem cronjob drin schreiben und der webserver draus lesen kann.

Die nötige Zeile für /etc/sudoers, damit der cronjob auch den Webserver neu starten kann:

alex    ALL = NOPASSWD: /usr/sbin/service lighttpd reload

Daneben interessant ist sicherlich der post-hook, der nötig ist, weil lighttpd Zertifikat und private key zusammen in einer Datei erwartet, die Datei sieht so aus:

#!/bin/sh
ACMEDIR=/home/alex/.acme.sh
cat ${ACMEDIR}/${1}/${1}.cer ${ACMEDIR}/${1}/${1}.key > ${ACMEDIR}/${1}/${1}.lighty-pem

lighttpd

Der erste Teil der lighty-Konfiguration steht bei mir in /etc/lighttpd/lighttpd.conf und inkludiert meine ssl-Optionen:

$SERVER["socket"] == ":443" {
        include "ssl/lighty-ssl.conf"
}

Diese Datei hab ich mir vom Mozilla SSL Configuration Generator erstellen lassen und von Hand angepasst. Die für den Betrieb mit acme.sh angepassten Zeilen:

ssl.pemfile = "/home/alex/.acme.sh/owncloud.home.example.com/owncloud.home.example.com.lighty-pem"
ssl.ca-file = "/home/alex/.acme.sh/owncloud.home.example.com/ca.cer"

Wobei die erste Datei diejenige ist, die unser post-hook Skript generiert und die zweite das CA Zertifikat, das acme.sh runterlädt.

Fehlen noch zwei Puzzlestücke. Bei Debian schmeißt man die typischerweise nach /etc/lighttpd/conf-available und kann sie später mit lighty-enable-mod und lighty-disable-mod leicht aktivieren und deaktivieren. Teil eins ist etwas kürzer und steckt hier in der Datei 53-le.conf und sieht so aus:

alias.url += ( "/.well-known/acme-challenge" => "/var/www/html/le/.well-known/acme-challenge/" )

Gut zu sehen, das zuvor in der acme Konfiguration angegebene webroot. Knifflig und etwas umfangreicher jetzt die owncloud-Konfiguration, hier mal aus der Datei /etc/lighttpd/conf-available/59-owncloud.conf in voller Länge:

$HTTP["host"] == "owncloud.home.example.com" {
        # match all unencrypted traffic ...
        $HTTP["scheme"] == "http" {
                # but not the letsencrypt webroot ...
                $HTTP["url"] !~ "^/\.well-known/acme-challenge" {
                        # and redirect as described on https://redmine.lighttpd.net/projects/lighttpd/wiki/HowToRedirectHttpToHttps

                        # capture vhost name with regex conditiona -> %0 in redirect pattern
                        # must be the most inner block to the redirect rule
                        $HTTP["host"] =~ ".*" {
                                url.redirect = (
                                        ".*" => "https://%0$0"
                                )
                        }
                }
        }

        url.redirect = ( 
                "^/.well-known/caldav$" => "/remote.php/caldav/",
                "^/.well-known/carddav$" => "/remote.php/carddav/" 
        )

        alias.url += ( "" => "/usr/share/owncloud" )

        $HTTP["url"] =~ "^/data/" {
                url.access-deny = ("")
        }

        $HTTP["url"] =~ "^($|/)" {
                dir-listing.activate = "disable"
        }
} else $HTTP["host"] =~ "" {
        url.redirect = ( 
                "^/owncloud/.well-known/caldav$" => "/owncloud/remote.php/caldav/",
                "^/owncloud/.well-known/carddav$" => "/owncloud/remote.php/carddav/" 
        )

        # Make ownCloud reachable under /owncloud
        alias.url += ( "/owncloud" => "/usr/share/owncloud" )

        # Taken from http://owncloud.org/support/distro-notes, section "lighttpd":
        # As .htaccess files are ignored by lighttpd, you have to secure the /data
        # folder by yourself, otherwise your owncloud.db database and user data is
        # puplicly readable even if directory listing is off. 
        $HTTP["url"] =~ "^/owncloud/data/" {
                url.access-deny = ("")
        }

        $HTTP["url"] =~ "^/owncloud($|/)" {
                dir-listing.activate = "disable"
        }
}

Was hier realisiert wird, ist die Umleitung von allem unverschlüsselten Traffic auf https außer für den Teil, der für die acme challenge unverschlüsselt bleiben muss. Dazu weitere well-known URLs für einfacheren Kalenderzugriff aus bestimmten Anwendungen, das Verbot das Datenverzeichnis zu erreichen und die Deaktivierung von directory listing, alles für owncloud, hat nichts mehr mit acme zu tun, auch der unterste Abschnitt dient nur nochmal zum Zugriff auf owncloud über andere Adressen.

Erzwingt man hier keinen Zugriff über https, kann man sich einiges der Konfiguration sparen, aber der Sinn von letsencrypt ist ja grad, möglichst viel über https abzuwickeln.

Übertragen der Zertifikate auf Reverse Proxy

In der Praxis hab ich den lighttpd nicht direkt am Netz hängen, davor ist noch ein nginx als Reverse Proxy, der HTTPS mit dem gleichen Zertifikat sprechen soll. Weil das hier thematisch nicht mehr so gut dazu passt, hab ich eine kleine Serie von Beiträgen draus gemacht. Zu Teil 2 hier entlang: Unbeaufsichtigte Aktionen mit SSH.

  1. das wird noch ein Spaß mit dem Upgrade beim nächsten Release :-/ []
  2. Adresse von der Redaktion geändert []
  3. fli4l rockt! []
  4. streng genommen, war es ein Aufruf mit mehr Domains, alle mit eigenem -d davor, angepasst auf meine lokalen Bedürfnisse []

HowTo: ownCloud mit lighttpd auf Debian Jessie

Der Fortschritt macht nicht halt und dass jemand™ sich jetzt ein Fairphone mit Android drauf gekauft hat, war eine willkommene Gelegenheit sich mal eine eigene ownCloud-Instanz aufzusetzen. Debian Jessie ist zwar derzeit noch »testing« aber schon ganz gut nutzbar und da es dort fertige Pakete für ownCloud 7 gibt und eine passende VM auf dem Server zu Haus bereits lief, fiel die Wahl darauf. Ein lighttpd mit PHP lief auch schon und der Rest war auch so schwer nicht.

Im derzeitigen Stand des Paktes muss man die Datenbank für ownCloud noch selbst anlegen. Ich hab das mit phpmyadmin gemacht, wo das Debian-Paket sich selbst schon einfach in lighttpd integriert. Die Zugangsdaten gibt man später beim Einrichtungswizard vom ownCloud an.

Im ownCloud Paket ist eine Möglichkeit dokumentiert, das Paket mit lighttdp zu nutzen, die automatische Integration wie bspw. bei phpmyadmin gibt es so nicht. Der falsch vorgeschlagene Pfad in der Doku, wurde von mir gemeldet und ich hab noch ein paar Dinge ergänzt:

$HTTP["host"] == "owncloud.example.com" {
        url.redirect = (
                "^/.well-known/caldav$" => "/remote.php/caldav/",
                "^/.well-known/carddav$" => "/remote.php/carddav/"
        )

        alias.url += ( "" => "/usr/share/owncloud" )

        $HTTP["url"] =~ "^/data/" {
                url.access-deny = ("")
        }

        $HTTP["url"] =~ "^($|/)" {
                dir-listing.activate = "disable"
        }
} else $HTTP["host"] =~ "" {
        url.redirect = ( 
                "^/owncloud/.well-known/caldav$" => "/owncloud/remote.php/caldav/",
                "^/owncloud/.well-known/carddav$" => "/owncloud/remote.php/carddav/"
        )
        
        # Make ownCloud reachable under /owncloud
        alias.url += ( "/owncloud" => "/usr/share/owncloud" )
          
        # Taken from http://owncloud.org/support/distro-notes, section "lighttpd":
        # As .htaccess files are ignored by lighttpd, you have to secure the /data
        # folder by yourself, otherwise your owncloud.db database and user data is
        # puplicly readable even if directory listing is off.
        $HTTP["url"] =~ "^/owncloud/data/" {
                url.access-deny = ("")
        }
        
        $HTTP["url"] =~ "^/owncloud($|/)" {
                dir-listing.activate = "disable"
        }
}

Der untere Teil ist aus dem Vorschlag vom Paket übernommen, nur der alias-Pfad korrigiert. Der obere Teil ist für den direkten Zugriff über eine andere Subdomain statt über den hostname des Servers und einen Unterordner. An sich steht da aber das gleiche drin. Besonderes Augenmerk sei noch auf die redirects für die sogenannten well-known URLs nach RFC 5785 gelegt, die den Zugriff mit bestimmten Kalenderprogrammen deutlich vereinfachen.

Nachtrag: Dieses Mini-HowTo unterschlägt die Einrichtung von verschlüsseltem Zugriff über HTTPS. Ich habe das weggelassen, weil es a) an anderer Stelle gut beschrieben ist und b) ich hier noch einen nginx als Reverse Proxy vor dem lighty habe, der mit diesem HowTo rein gar nichts zu tun hat. ;-)