Dieses Silvester hatte ich ungefähr hundert Neujahrsvorsätze. Unter anderem wollte ich mehr Geld sparen. Eine einfach kürzbare monatliche Ausgabe beläuft sich auf 15–17 Euro, je nach Wechselkurs, für Linode.

Nicht falsch verstehen: Linode ist ein super VPS-Hoster. Das Problem ist nur, dass ich eigentlich keinen VPS brauche.

Gekauft habe ich ihn unter dem Vorwand von

  • mehr Kontrolle und Unabhängigkeit (im Vergleich zu Tumblr oder auch Uberspace z.B.),
  • mehr Möglichkeiten (Git Repos selbst hosten) und
  • Stolz: Wenn das ganze Internet wegen einem Fehler bei AWS unerreichbar ist, hält mein Blog die Uptime-Flagge hoch.

Tatsächlich bringt aber alles Nachteile mit sich, auf die ich keine Lust habe. Kontrolle und Unabhängigkeit sind großartig, den nginx einzurichten hat auch ein bisschen Spaß gemacht. In Zeiten von Heartbleed und Shellshock müsste man sich aber jede Woche einloggen und Sicherheitsupdates einspielen. Die Möglichkeiten sind mit einem VPS wirklich endlos, aber ich nutze sie nicht. Zumal gitolite einzurichten schon ein Krampf war und ich inzwischen vergessen habe, wie es funktioniert. Das ist auch wieder Wartungsaufwand, den ich nicht will. Dann lieber notfalls 7 $ für privates Github zahlen. Und Stolz, naja. Get over it.

Als Alternative zum VPS scheint Hosting bei Amazon Web Services eine beliebte und—in Anbetracht meiner geschätzten Anzahl an Lesern—billige Lösung zu sein. Es gibt zahlreiche Anleitungen im Internet, die sehr weiterhelfen. Ein paar Stolpersteine verschweigen sie aber (oder sie sind nicht gestolpert) und darum geht es hier.

S3

Um bei S3 eine Website zu hosten, muss man alle Dateien in ein Bucket hochladen und für dieses ein Häkchen setzen: “Enable Website Hosting.” BOOM—Done!1

Über den “hochladen” Part: Es gibt viele, viele Tools dafür. Ich dachte eine Kombination aus AWS CLI und gulp-shell funktioniert am besten, zumal die AWS CLI sowieso benötigt wird, um Zertifikate hochzuladen. (Ich bevorzuge immer nur ein Tool pro Job installiert zu haben.) Stellt sich allerdings heraus, dass die AWS CLI mit Dateiname und Timestamp arbeitet. Das bedeutet, dass nach touch _site/index.html diese Datei erneut hochgeladen wird, obwohl der Inhalt unverändert ist. Das bedeutet weiterhin, dass der gesamte _site Ordner jedes Mal komplett gesynct wird, inklusive Bilder, Videos und anderes. Weil s3_website Ruby UND Java benötigt (…the fuck) kann ich s3cmd sort of empfehlen. Die Installation ist umständlich: Man muss zur Zeit vom master Branch selbst bauen, weil Amazon das Signaturschema geändert hat und sowieso ist keine aktuelle Version davon in pip, brew oder sonstwo. Fragt mich nicht, wie man da Updates macht. Dafür funktioniert es aber wie es soll.

Zwei Probleme:

  • Die URL hat das Format BUCKET.s3-website.REGION.amazonaws.com. In der Regel will man das nicht. Die Lösung ist der DNS von AWS, Route 53.2
  • S3 kann nur HTTP, für das grüne Schloss neben der Adresse muss man ein CDN via CloudFront einziehen. Das hat dann wieder ein paar leicht störende Implikationen auf das Dateienmanagement im Bucket: Der CloudFront Cache ist by default 24h gültig, d.h. für ein Update muss die betroffene Datei erst invalidiert werden.3

Route 53

Der DNS von Amazon ist ziemlich schnell eingerichtet. Eine neue Hosted Zone machen, dort einen ALIAS Record für das S3 Bucket eintragen, beim Domain Provider die AWS Nameserver eintragen, fertig. Wenn es nicht sofort klappt, cacht euer DNS zu lange. Ich habe bei der Gelegenheit von OpenDNS zu Google4 gewechselt.

Wenn ihr für die Domain—wie ich—auch Email eingerichtet habt, müsst ihr bei Route 53 noch MX (Mail Exchange) Records eintragen,5 damit ihr Nachrichten empfangen könnt.

CloudFront

CloudFront ohne TLS einzurichten ist auch wieder kein Problem. Eine neue Distribution erstellen, die Webaddresse des Buckets als Origin eintragen, im Route 53 als ALIAS die CloudFront Domain eintragen, fertig.

Ja, wo waren eigentlich die Stolpersteine, von denen ich anfangs sprach?

Für CloudFront mit TLS braucht man zuerst ein Zertifikat, z.B. von StartCom. Hier ist unbedingt darauf zu achten, dass der Private Key dafür nicht länger als 2048 Bit ist. Ich hatte bereits ein Zertifikat mit 4096 Bit Key (because why not) und es dauerte ewig, das herauszufinden.

Als nächstes muss das Zertifikat zu AWS IAM hochgeladen werden. Dazu ist ein User nötig, der Administrator Zugriff hat, also alles darf. Die genauen Schritte habe ich vergessen, aber ich habe eine neue Gruppe mit Admin Zugriff erstellt und einen neuen User, den ich dann dieser Gruppe zugewiesen habe. Die AWS CLI habe ich dann für den neuen User konfiguriert.

Danach kann das Zertifikat zu Amazon geschickt werden. Der Befehl dafür ist etwas tricky:

aws iam upload-server-certificate 
  --server-certificate-name NpiccolottoCom 
  --certificate-body file://npiccolotto.com.pem 
  --private-key file://npiccolotto.com.key 
  --certificate-chain file://startcom.pem 
  --path /cloudfront/npiccolotto-com/

Erklärung:

  • server-certificate-name: Egal, ein Name für das Zertifikat halt.
  • certificate-body: Das eigentliche Zertifikat.
  • private-key: Der Private Key zu dem Zertifikat.
  • certificate-chain: Zusätzliche Zertifikate optional mit Root Zertifikat. Im Fall vom gratis StartCom Zertifikat sind das: sub.class1.client.ca.pem und ca.pem, wobei letzteres das Root Zertifikat von StartCom ist. Die Zertifikate müssen in einer Datei in umgekehrter Reihenfolge zusammengefasst werden, das Root Zertifikat also zuletzt.6
  • path: Wieder egal, muss aber mit /cloudfront/ anfangen, mit Slash aufhören und darf—glaube ich—nur alphanumerische Zeichen und Dashes enthalten.

Wenn alles geklappt hat, antwortet AWS CLI mit irgendeinem JSON. Danach kann man zu CloudFront gehen und bei den Settings das Custom SSL Certificate auswählen. Außerdem wichtig: “Only Clients that support SNI” anwählen, sonst wird es teuer. Als “Default Root Object” index.html eintragen, sonst weiß CloudFront nicht, was es für die Startseite ausgeben soll und ihr bekommt eine Fehlermeldung. Als CNAME noch eure Domain (npiccolotto.com) eintragen, damit diese anstatt der CloudFront Domain (xyz.cloudfront.net) verwendet werden kann.

Sollte beim Speichern der neuen Konfiguration ein generischer Fehler geworfen werden, der irgendwas mit Zertifikat sagt, ist hier die Checkliste:

  1. Länge des Private Keys überprüfen. Muss kleiner gleich 2048 Bit sein.
  2. Korrektheit des Private Keys überpüfen
  3. Certificate Chain überprüfen
  4. ???

Fazit

So, was habe ich erreicht?

AWS Rechnung

Nur noch 3 % der Kosten! Ursprünglich bin ich sogar von 0.20 $ ausgegangen, aber da hatte ich Route 53 nicht einberechnet, was mit 0.50 $ für die Hosted Zone den Löwenanteil ausmacht. Es muss außerdem dazugesagt werden, dass ich wegen des AWS Free Tiers nichts für S3 und CloudFront zahle, in 12 Monaten ist das also noch ein bisschen mehr. Ich bezweifle aber irgendwie, dass es viel mehr als 1–2 $ insgesamt wird.

Ein weiterer Vorteil ist die Flexibilität der AWS Infrastruktur. Sollte ein Post unwahrscheinlicherweise viral gehen, kriegt AWS den Traffic auf alle Fälle gehandelt. Ich zahle dann halt für die Aufmerksamkeit. Bei meinem SPOF nginx wäre ich mir nicht so sicher.

Die Nachteile halten sich eigentlich in Grenzen. Die Größten sind meiner Meinung nach das fitzelige Dateihandling wegen CloudFront und dass Dateien nicht mehr geGZIP’t sind, wenn ich es nicht selbst mache (ging vorher über nginx).

Alles in allem also ein voller Erfolg. Hat auch nur zwei Jahre gedauert!

  1. Nachdem die Bucket Policy angepasst wurde, sodass der Inhalt public ist.

  2. Jein. Wenn ihr von einer Subdomain serven wollt, geht es auch ohne Route 53.

  3. Was Geld kostet und idealerweise im Buildprozess der Website abgebildet sein muss, weil niemand will das manuell machen.

  4. 8.8.8.8 und 8.8.4.4.

  5. Welche das für Fastmail sind.

  6. cat sub.class1.client.ca.pem ca.pem > startcom.pem