Next Level (Step 2: Renewal á la Cron)

Seit Dezember ist nun ganz offiziell die Public Beta von Let’s Encrypt verfügbar. Die ersten Zertifikate laufen nun innerhalb der nächsten 2 Wochen aus, also auch bei mir (ich hatte ja auch Ende November bereits alles auf SSL-only umgestellt).

Mit einem Blick in den Kalender wurde es daher auch mal zeitig, den automatischen Renewalprozess ans Laufen zu kriegen, denn die Zertifikate sind ja bekanntlich immer nur 90 Tage gültig. Manuelles Eingreifen als Dauerlösung scheidet aus. Ich bin ja Entwickler, da ist jede Wiederholung eines Arbeitsschrittes schon eine zu viel.

Das Szenario

Ein Multi-Site-Konfiguration verschiedener Domains für einen Nginx, jede Domain hat ihre eigenes Zertifikat und Private Key. Diese Paare müssen jeweils validiert und ggf. erneuert werden.

Für den initialen Einrichtung hatte ich auch den offiziellen Client verwendet. Aber erstens ist dieser zu mächtig mit vielen Abhängigkeiten, denn ich will nur (neue) Zertifikate und keine Modifizierung irgendwelcher Konfigurationsdateien. Das Zweite wiegt noch schwerer: Dieser funktioniert (aktuell) nur als root vernünftig, was unter anderem auch daran liegt, dass er kurzfristig Port 80 nutzen muss. Das ist aber natürlich mit einem laufenden Webserver „unhandlich“.

Aber es gibt Alternativen: Und mal gar nicht so wenige. Prinzipiell sind Ideen wie der Caddy Server (ein „Webserver“ mit eingebautem ACME-Client) interessant, aber nicht für diese Situation angebracht. Ein ander Mal vielleicht…

Exkurs: Der gesamte Prozess der Verifizierung der Domain geschieht über das Protokoll „Automated Certificate Management Environment“ oder kurz ACME. Das wurde ursprünglich von Let’s Encrypt entwickelt und aktuell ist es als IETF Protokoll im Gespräch. Das Protokoll beschreibt, wie Clients (bspw. für Webserver) ein Zertifikat anfragen und bestätigen können. Daher kann prinzipiell jeder einen solchen Client (und natürlich auch Server!) bauen; fertige Plugins „out-of-the-box“ für Webserver sind also auch bald möglich — Caddy macht es sogar schon vor.

Meine Wahl fällt auf letsencrypt.sh von Lukas Schauer: ein kleines und überschaubares Bashscript, welches sich ebenfalls einfach konfigurieren lässt. Da ich ja bereits „Bestandsdaten“ hatte, wollte ich natürlich gerne die vorhandenen Zertifikate und Schlüssel „importieren“. Netterweise hat Lukas dafür schon etwas vorbereitet. 😎

Schritt-für-Schritt vom Import bis zur Cron:

  • Annahme: www-data ist die Gruppe des Webserver-Users.
  1. Wir legen einen neuen dedizierten UNIX-Account an, nennen wir ihn doch bill (Name d. Red. bekannt). Denn Bill is smart!
    1. Nicht vergessen: bill darf sich nicht einloggen können; am Besten kein Passwort und keinen Public Key hinterlegen.
    2. bill wird Mitglied der Gruppe www-data (als Default-Gruppe): usermod -g www-data bill
  2. Das Git-Repository klonen wir uns einfach in sein Home-Verzeichnis: git clone https://github.com/lukas2511/letsencrypt.sh.git
    1. In das Verzeichnis wechseln.
    2. Testweise ./letsencrypt.sh -e starten. Sollte kein Fehler kommen, erstmal alles gut. Ansonsten dafür sorgen, dass openssl und curl installiert sind.
  3. Wir legen das Verzeichnis /etc/letsencrypt.sh an und passen die Rechte so an, dass nur Mitglieder der Gruppe www-data dort lesen und bill zusätzlich schreiben darf: chown bill:www-data /etc/letsencrypt.sh && chmod 750 /etc/letsencrypt.sh
  4. Vorbereitungen
    1. In bill wechseln… su – bill
    2. … und in das eben erstellte Verzeichnis /etc/letsencrypt.sh wechseln.
    3. Eine leere domains.txt erstellen (siehe domains.txt.example)
    4. Jeweils die config.sh.example und hooks.sh.example aus dem Repository kopieren und ablegen, dabei den Suffix .example entfernen.
    5. In der nun frischen config.sh das BASE_DIR setzen: BASEDIR=/etc/letsencrypt.sh
    6. Einen symbolischen Link auf /var/www/letsencrypt erstellen: ln -s /etc/letsencrypt.sh/.acme-challenges /var/www/letsencrypt
    7. Das eben erwähnte Linkziel anlegen: mkdir /var/www/letsencrypt
    8. Aus Sicherheitsgründen sollten die Dateien und Verzeichnisse nur für bill zu schreiben sein.
  5. Letsencrypt.sh-Konfiguration
    1. Falls schon mit Hilfe des offiziellen Client Zertifikate installiert wurden:
      1. Mit dem Importscript (siehe hier) werden alle Zertifikate ermittelt und sauber in /etc/letsencrypt.sh/certs/$DOMAIN geschrieben.
      2. Einen eventuell vorhandenen Account Key kann man mit dem zweiten Importscript auch ermitteln; falls vorhanden einfach unter /etc/letsencrypt.sh/private_key.pem ablegen.
    2. Falls noch keine Zertifikate angelegt wurden, dann muss man die Datei domains.txt selber editieren — oder auch nachträglich korrigieren.
  6. Nginx-Konfiguration
    1. In Punkt 4 wurde bereits das Verzeichnis /var/www/letsencrypt für die Response-Challenges angelegt. Da das Protokoll vorsieht, das der Webserver unter Port 80 (!) und dem Pfad /.well-known/acme-challenge die Antworten liefern kann, muss jeweils bei jeder Seite eine kleine Ergänzung eingefügt werden. Relativ am Anfang innerhalb der HTTP-Direktive sollte daher ein Verweis auf eben genanntes Verzeichnis stehen. Im Folgenden ein Beispiel zusammen mit HTTPS-Always: .
      Es muss sicher gestellt werden, dass für alle(!) Domains (inkl. Subdomains) das Verzeichnis über HTTP erreichbar ist. Am Besten eine Datei mit „Hello World“ ablegen und die Erreichbarkeit versuchen. Idealerweise mit curl, denn der Browser forciert u.U. die HTTPS-Erreichbarkeit (etwa durch HSTS oder Plugins).
    2. Und schließlich muss der Pfad zum Zertifikat und zum Schlüssel umgebogen werden.
      1. Entweder: In der Nginx-Site-Konfiguration wird auf den neuen Pfad gezeigt: /etc/letsencrypt.sh/certs/$DOMAIN/fullchain.pem und ~privkey.pem
      2. Oder: Man kopiert sich die Dateien im Hook (siehe unten).
  7. Hooks
    1. Letsencrypt.sh bietet aktuell drei Hooks an, davon ist interessant: deploy_cert (nachdem ein neues Zertifikat erstellt wurde)
      1. Falls man das Zertifikat erst noch an die richtige Stelle kopieren will (als bill).
      2. Es bietet sich an, den Webserver neu zu starten: sudo service nginx reload
  8. Cron finally
    1. Anschließend runden wir das Ganze noch ab: Wir richten eine wöchentliche Cron auf, der letsencrypt.sh aufruft und die Renewals automatisch anwirft. Beispiel: 0 0 * * Sat (cd /home/bill/letsencrypt.sh; ./letsencrypt.sh -c > /home/bill/last_renew_check.log) für einen wöchentlichen Check am Samstag.

Zusammenfassung

Mit der Anleitung haben wir nun einen wöchentlichen Cronjob, der jeweils prüft, ob die konfigurierten Domains ein Zertifikat haben und ob dieses noch gültig ist. Dabei wird bereits im Voraus ein neues generiert. Die Verifikation geschieht ganz regulär über ACME, der Webserver liefert die entsprechenden Daten im laufenden Betrieb.

letsencrypt.sh liegt im Homeverzeichnis eines dedizierten Benutzers ($HOME/letsencrypt.sh), die Konfiguration unter /etc/letsencrypt.sh.

Die Zertifikate und Schlüssel liegen in /etc/letsencrypt.sh/certs (BASE_DIR).

Alle Pfade lassen sich natürlich ändern, dafür einfach in der Dokumentation nachschauen.

Anmerkungen

  • Man kann auch ein anderes BASE_DIR (anstatt /etc/letsencrypt.sh verwenden; die config.sh muss  weiterhin unter /etc/letsencrypt.sh (oder lokal) vorhanden sein.
  • bill is smart und darf deswegen natürlich keinen Service einfach neu starten. Das lässt sich mit sudoers lösen: bill ALL = NOPASSWD: /usr/sbin/service nginx *

CSS3: SASS, Compass und PIE

Nach einer längeren Pause gibt es heute ein paar kurze Notizen zum Thema CSS3 und Internet Explorer.

Aber der Reihe nach.

SASS

So schön CSS ist, so alt und stupide ist der Spielraum zum entwickeln und konzipieren. CSS kann keine Vererbung, keine Strukturierungen, keine Kapselungen bzw. Mixins. Das einzige, was CSS kann: eine Aneinanderreihung von Tags, bspw. #element ul > li

Mit SASS ist das alles möglich. Es sind hierarchische Strukturen möglich, man kann Mixins (Kurzanweisungen für ein Set von Funktionen/Attributen, Stichwort: Browser-Cross-Features) oder endlich Variablen im CSS nutzen. Der Code-Syntax ist dabei eine Erweiterung von CSS3* (d.h. CSS selber ist weiterhin möglich) und wird üblicherweise in .scss-Dateien abgelegt. Jedes öffentlich zugängliche Stylesheet muss dann in ein „normales“ CSS umgeschrieben werden, das erledigt SASS wahlweise auf Knopfdruck oder mit einem Watchdog (Befehl watch). Mit letzterem geschieht die Generierung on-the-fly, d.h. man kann zwischen SCSS und Browser genauso wechseln wie beim guten alten CSS.

* So lange man natürlich kein CSS3 nutzt, ist man auch CSS2 kompatibel.

Compass

Als Erweiterung für SASS gedacht, erweitert Compass das Funktionsspektrum um einige hilfreiche CSS(3)-Features. Die Referenz zeigt stets den aktuellen Stand und auf GitHub sind die Sourcen offen zugänglich. Um beispielsweise die mittlerweile zahlreichen (6!) vendor-spezifischen Anweisungen eines linearen Gradienten (hier: weiß zu schwarz) zu erzeugen, reicht das simple Mixin linear-gradient(color-stops(white, black)).

Nett: Als Pull bekam das Projekt auch schließlich einen experimentellen Hack, damit Gradienten out-of-the-box im IE6-8 funktionieren. So funktioniert Open Source.

PIE

CSS3PIE ist eine winzige Library, die in Form einer HTC (so genannte HTML Components, eine microsoft-spezifische JavaScript-Datei) weitestgehend die CSS3-Features border-radius, box-shadow und linear-gradient bereitstellt.

In Compass ist PIE optional verfügbar. Mit der Ergänzung einer Zeile SCSS-Code ist damit ein runder, schattierter Button real.

Beispiele

Da war noch was…

CSS3PIE wurde dieses Jahr ein Teil von Sencha. Da ist es nicht verwunderlich, dass Sencha selber für ihren Theme-Generator (Bestandteil der Sencha SDK Tools)  für ExtJS/SenchaTouch auf Compass, PhantomJS u.a. setzt.

Autodiscovery der Searchengine in Google Chrome + OpenSearch

Unter dem Titel „Suche in Chrome per type=search anmelden“ fand ich die Tage einen netten Hinweis von Markus. Es geht darum, dass der Chrome automatisch Suchmaschinen hinzufügt. Auch ich dachte bisher, dass Google einfach nur eine gute Liste eingepflegt hat (bspw. mit amazon.de), aber mitnichten: Die Amazon.de-Suchmaschine ist bei mir nur deswegen drin, weil ich die Amazonseite besucht habe und Chrome dort eine Suchmaschine „gefunden“ hat. Aber warum gerade Amazon.de? Warum dict.cc?

Markus erster Hinweis war, dass es mit dem neuen HTML-Inputfeld-Typ „search“ zusammenhängt. Ein anderer Kommentator brachte noch OpenSearch ins Spiel.. und als meine ersten Tests überhaupt nicht fruchteten, setzte ich mich mal genauer an das Thema heran. Den Großteil des Ergebnisses habe ich bereits in Markus‘ Beitrag als Kommentar geschrieben.

OpenSearch

Beginnen wir mit OpenSearch: Dies ist ein — noch nicht standardisierter — Entwurf für ein Dokument, welches die Metainformationen einer Suchmaschine beschreibt: Name, Beschreibung, URL-Pattern, Sprache, usw.

Ein solches OSDD (OpenSearch Description Document) sieht dabei vereinfacht und exemplarisch so aus:

<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Example</ShortName>
<Description>My example search engine</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image height="16" width="16" type="image/x-icon">http://example.org/favicon.ico</Image>
<Url type="text/html" template="http://example.org/index.html#search={searchTerms}"/>
</OpenSearchDescription>

Damit ein Browser die OSDD auch finden kann, muss sie innerhalb des HTML-Heads als Metainformation eingefügt werden:

<link title="Example" type="application/opensearchdescription+xml" rel="search" href="http://example.org/opensearch.xml">

Voilá! Ein Firefox findet damit bereits die Suchmaschine und bietet, wenn man auf den kleinen Pfeil unter dem aktuellen Suchmaschinen-Icon klickt, das Hinzufügen der neuen an. Das gilt im Übrigen auch für den Internet Explorer 8. Ab FF 2 und IE7 kann zudem explizit auf eine solche OSDD per JavaScript verwiesen werden:

window.external.AddSearchProvider("http://example.org/opensearch.xml");

Autodiscovery Mode in Chrome

Google Chrome erkennt auch diese OSDD, stellt aber zur Zeit keine geeignete UI wie etwa Firefox oder Internet Explorer zur Verfügung. Vielmehr werden hier die Suchmaschinen automatisch erkannt, eingetragen und über die kombinierte Adress- und Suchleiste zur Verfügung gestellt.

Der Trigger für den Autodiscovery Mode ist dabei zwar ein INPUT-Field mit dem Typ „search“ oder wahlweise mit dem Typ „text“ und dem Namen „s“. Der eigentliche Clou Witz an der Sache ist jedoch, dass Chrome dieses HTML-Element explizit auf der Startseite der Domain erwartet. Es muss also auf der example.org/index.html oder example.org/startseite.jsp auftauchen. Funktioniert die OSDD prinzipiell von allen Seiten aus, so funktioniert der Autodiscovery Mode in keinem (virtuellen) Unterverzeichnis.

Interessanterweise spielt es aber keine Rolle, wie lange die Seite angezeigt wird. Liegt also bspw. eine example.org/index.html vor, die einen Meta-Refresh nach 0 Sekunden (sprich: sofort) macht, dann funktioniert der Autodiscovery Mode dennoch. Zusätzlich kann man das Formular auch unsichtbar machen, dies hat keine Auswirkungen.

Im Grunde reicht also dieses Schnippselchen, sofern man keine adäquate oder sinnvolle Startseite hat:

<form style="display: none;"><input type="search" name="s" /></form>

Die eigentliche Konfiguration holt sich Chrome wiederum aus der OSDD.

Drawbacks

  • Apple Safari scheint bis dato keinerlei native Unterstützung für OSDD oder vergleichbares zu haben. Am nächsten kommt das Plugin Inquisitor.
  • Internet Explorer 9 scheint auf den ersten Blick keine Unterstützung für OSDD zu haben. Evtl. habe ich es aber auch noch nicht gefunden?
  • Opera hat auch keine Unterstützung.
  • Spezialfall: Im Verbund mit einem Suchparameter via Hash (Ajax/RIAs) wird bei Firefox nur der location.hash ausgetauscht (die Applikation bleibt). Google hingegen veranlasst einen kompletten neuen Request auf location.href, womit die gesamte Applikation neu geladen wird.

tl;dr: in a nutshell und lost facts

  • Die opensearch.xml stellt Metainformationen für eine Suchmaschine bereit und muss auf der HTML-Seite verlinkt werden.
  • Für den Autodiscovery Mode von Chrome muss auf einer Seite im Domain-Root ein INPUT-Feld „type=search“ oder „name=s“ vorhanden sein.
  • Das Formular respektive Feld muss weder für den Benutzer sichtbar sein (CSS), noch lange angezeigt werden (Redirect nach 0 Sekunden).
  • Manuelles Hinzufügen über eine API in nahezu allen aktuellen Browsern möglich.
  • Es können mehrere Suchmaschinen je Seite verwaltet werden (betrifft OSDD).

Links

Update: Martin Thoma hat einen englischen Artikel (http://martin-thoma.com/search-engine-autodiscovery/) geschrieben, der auf diesem basiert. Ergänzende Informationen sind zum Beispiel die aktualisierten Kompatibilitäten (IE7).

IE9 – 99% von was?

IE9 HTML5 Tests 99%… Bogus

IE9 has none of them, nor does it have any validation, or ranges or the new types… and worst of all for my Uploader widget, still, after all these years, does not support multiple file uploads. So the 99% isn’t 99% of all HTML5 features — it’s 99% of the tests that IE implemented. This is like writing your own test and bragging that you passed it.

Ein (weiterer) schöner Artikel über den Bullshit Microsofts. Obwohl der IE9 grundlegende und zahlreiche neue Features von HTML5 implementiert, fehlen im Vergleich zu den anderen Browsern noch etliche.. abgesehen davon, das auch diese noch nicht alles implementiert haben.

Insofern ist die Aussage einer 99% HTML5 (Test)-Abdeckung genauso viel aussagekräftig, wie das Entwickeln und Nutzen von 99 Testfällen bei Hunderten Fallunterscheidungen.

Wetten, dass demnächst etliche Zeitungen, Magazine oder gar Blogs von der überragenden HTML5-Kompatibilität der IE9 berichten werden?