{"id":1746,"date":"2017-02-05T22:30:35","date_gmt":"2017-02-05T21:30:35","guid":{"rendered":"https:\/\/www.my-it-brain.de\/wordpress\/?p=1746"},"modified":"2017-02-05T22:30:35","modified_gmt":"2017-02-05T21:30:35","slug":"protokoll-zum-ausfall-eines-webdienstes-nach-der-fehlgeschlagenen-erneuerung-eines-lets-encrypt-zertifikats","status":"publish","type":"post","link":"https:\/\/www.my-it-brain.de\/wordpress\/protokoll-zum-ausfall-eines-webdienstes-nach-der-fehlgeschlagenen-erneuerung-eines-lets-encrypt-zertifikats\/","title":{"rendered":"Protokoll zum Ausfall eines Webdienstes nach der fehlgeschlagenen Erneuerung eines Let&#8217;s Encrypt Zertifikats"},"content":{"rendered":"<p>In diesem Beitrag werden die Ursache und der Verlauf der St\u00f6rung protokolliert, welche die vor\u00fcbergehende Nichterreichbarkeit einer Seafile[1. <a href=\"https:\/\/de.wikipedia.org\/wiki\/Seafile\">Seafile &#8211; Wikipedia<\/a>]-Installation zur Folge hatte, welche ich f\u00fcr den Freifunk Lippe[2. <a href=\"https:\/\/freifunk-lippe.de\/\">Webpr\u00e4senz Freifunk Lippe<\/a>] betreibe.<\/p>\n<h2>Beschreibung der Umgebung<\/h2>\n<p>Die Seafile-Installation wird ausf\u00fchrlich in &#8222;<a href=\"https:\/\/www.my-it-brain.de\/wordpress\/installation-von-seafile-auf-einem-ubuntudebian-server\/\">Installation von Seafile auf einem Ubuntu\/Debian Server<\/a>&#8220; beschrieben. Die aktuelle Installation wurde wie dort erw\u00e4hnt zus\u00e4tzlich mit HSTS (<a href=\"https:\/\/www.my-it-brain.de\/wordpress\/wp-content\/uploads\/2016\/08\/TLS-Kochbuch.pdf\" target=\"_blank\">TLS-Kochbuch, Abschnitt 2.7<\/a>) und HPKP (<a href=\"https:\/\/www.my-it-brain.de\/wordpress\/wp-content\/uploads\/2016\/08\/TLS-Kochbuch.pdf\" target=\"_blank\">TLS-Kochbuch, Abschnitt 2.8<\/a>) gesichert. Das ben\u00f6tigte TLS-Zertifikat stammt von Let&#8217;s Encrypt (<a href=\"https:\/\/www.my-it-brain.de\/wordpress\/wp-content\/uploads\/2016\/08\/TLS-Kochbuch.pdf\" target=\"_blank\">Abschnitt 3.4.1, TLS-Kochbuch<\/a>).<\/p>\n<p>Zur automatisierten Verl\u00e4ngerung wird das Skript <code>smartrenew.sh<\/code> verwendet, welches in <a href=\"https:\/\/www.my-it-brain.de\/wordpress\/wp-content\/uploads\/2016\/08\/TLS-Kochbuch.pdf\" target=\"_blank\">Abschnitt 5.4 im TLS-Kochbuch<\/a> beschrieben wird.<\/p>\n<h2>Eingang der St\u00f6rungsmeldung<\/h2>\n<p>Die St\u00f6rungsmeldung ging am 04.02.2017 per E-Mail. Die Meldung wies auf einen Fehler bei der Erneuerung des Zertifikats durch Let&#8217;s Encrypt hin:<\/p>\n<pre><code>\r\narsing account key...\r\nParsing CSR...\r\nRegistering account...\r\nAlready registered!\r\nVerifying seafile.example.com...\r\nTraceback (most recent call last):\r\n  File \"acme-tiny-by-frezbo\/acme_tiny.py\", line 200, in \r\n    main(sys.argv[1:])\r\n  File \"acme-tiny-by-frezbo\/acme_tiny.py\", line 196, in main\r\n    signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, verifychallenge=args.verifychallenge, log=LOGGER, CA=args.ca)\r\n  File \"acme-tiny-by-frezbo\/acme_tiny.py\", line 150, in get_crt\r\n    domain, challenge_status))\r\nValueError: seafile.example.com challenge did not pass: {u'status': u'invalid', u'validationRecord': [{u'url': u'http:\/\/seafile.example.com\/.well-known\/acme-challenge\/ag0bYKs4XzfkYlzRPYLC1cFtv-ypEIQCEVfYulPlMGk', u'hostname': u'seafile.example.com', u'addressUsed': u'IPv4-Adresse', u'port': u'80', u'addressesResolved': [u'IPv4-Adresse', u'IPv6-Adresse']}, {u'url': u'https:\/\/seafile.example.com\/.well-known\/acme-challenge\/ag0bYKs4XzfkYlzRPYLC1cFtv-ypEIQCEVfYulPlMGk', u'hostname': u'seafile.example.com', u'addressUsed': u'IPv4-Adresse', u'port': u'443', u'addressesResolved': [u'IPv4-Adresse', u'IPv6-Adresse']}], u'keyAuthorization': u'ag0bYKs4XzfkYlzRPYLC1cFtv-ypEIQCEVfYulPlMGk.pW0Gip0L_WLx7XEnmH_3ZArt9Vi4TbokaUuSXsc1dm4', u'uri': u'https:\/\/acme-v01.api.letsencrypt.org\/acme\/challenge\/u5hporNk0I5YunYlQQeJPKdRyd-9BKGMWjBhm_7Za9E\/578797992', u'token': u'ag0bYKs4XzfkYlzRPYLC1cFtv-ypEIQCEVfYulPlMGk', u'error': {u'status': 403, u'type': u'urn:acme:error:\r\n unauthorized', u'detail': u'Invalid response from http:\/\/seafile.example.com\/.well-known\/acme-challenge\/ag0bYKs4XzfkYlzRPYLC1cFtv-ypEIQCEVfYulPlMGk: \"\\r\\n404 Not Found\\r\\n\\r\\n404 Not Found\\r\\n\"'}, u'type': u'http-01'}\r\n * Reloading nginx configuration nginx\r\n   ...fail!\r\n<\/code>\r\n<\/pre>\n<p>Der Fehlermeldung ist zu entnehmen, dass die ACME-Challenge nicht verifiziert werden konnte, da der Server auf die Anfrage mit der Meldung &#8222;404 Not Found&#8220; antwortete. Dieser Fehler konnte durch Aufruf der URL mit dem Programm <code>wget<\/code> best\u00e4tigt werden.<\/p>\n<h2>Ergebnis der Systemanalyse<\/h2>\n<p>Die Analyse der NGINX-Konfiguration ergab, dass der von Let&#8217;s Encrypt signierte \u00f6ffentliche Schl\u00fcssel des Zertifikats nicht zum dazugeh\u00f6renden privaten Schl\u00fcssel passte. Dies hatte zur Folge, dass sich die Webseite im Browser, der HSTS und HPKP unterst\u00fctzt, nicht mehr aufgerufen werden konnte. Denn durch HSTS wird auch ein Aufruf der Seite \u00fcber HTTP automatisch auf HTTPS umgeleitet. Hier wird nun durch HPKP ebenfalls festgestellt, dass der gepinnte Key nicht mit dem vom Webserver ausgelieferten \u00fcbereinstimmte und der Browser verweigerte den Aufruf der Webseite.<\/p>\n<p>Die Ursache hierf\u00fcr lag in der fehlenden Fehlerbehandlung im folgenden Skript:<\/p>\n<pre><code>\r\npython acme-tiny-by-frezbo\/acme_tiny.py --no-verify --account-key \/var\/www\/seafile.example.com\/ssl\/account.key --csr \/var\/www\/seafile.example.com\/ssl\/seafile.example.com.csr --acme-dir \/var\/www\/seafile.example.com\/public\/.well-known\/acme-challenge\/ &gt; \/var\/www\/seafile.example.com\/ssl\/seafile.example.com.crt\r\n\r\ncat \/var\/www\/seafile.example.com\/ssl\/seafile.example.com.crt \/var\/www\/seafile.example.com\/ssl\/lets-encrypt-x3-cross-signed.pem &gt; \/var\/www\/seafile.example.com\/ssl\/seafile.example.com_chained.crt\r\n\r\nsudo service nginx reload\r\n<\/code>\r\n<\/pre>\n<p>Zuerst versucht das Skript, das Zertifikat zu erneuern und in die Datei <code>seafile.example.com.crt<\/code> zu schreiben. Schl\u00e4gt dieser Vorgang fehl, wird durch den zweiten Teil des Skripts trotzdem aus der korrupten Datei und dem Intermediate-Zertifikat die Zertifikatskette erstellt und in der Datei <code>seafile.example.com_chained.crt<\/code> gespeichert.<\/p>\n<p>In diesem Fall trat bei der Erneuerung des Zertifikats ein Fehler auf. Es wurde jedoch trotzdem eine Datei namens <code>seafile.example.com.crt<\/code> mit einer Gr\u00f6\u00dfe von 0 Byte erstellt. Daher fehlte das Zertifikat auch in der daraufhin erstellten Zertifikatskette. Dies hatte zur Folge, dass durch HPKP der Webbrowser den Zugriff auf die Domain verweigerte und die Anwendung nicht mehr erreichbar war.<\/p>\n<p>Eine \u00dcberpr\u00fcfung der Datensicherung ergab leider, dass das Verzeichnis mit den Zertifikatsdateien nicht Bestandteil des Backups war. Somit war die schnelle Wiederherstellung der Zertifikatskette nicht m\u00f6glich.<\/p>\n<h2>St\u00f6rungsbeseitigung<\/h2>\n<p>Um die St\u00f6rung zu beseitigen, wurde zuerst die bestehende vHost-Konfiguration f\u00fcr die Seafile-Instanz deaktiviert. Anschlie\u00dfend wurde ein neuer vHost eingerichtet, der ausschlie\u00dflich via HTTP das Verzeichnis ausliefert, welches die ACME-Challenge beinhaltet:<\/p>\n<pre><code>\r\nserver {\r\n        listen       80;\r\n        server_name  seafile.example.com;\r\n\r\n        root \/var\/www\/seafile.example.com\/public\/;\r\n        location \/ {\r\n        index index.html index.htm index.php;\r\n        }\r\n}\r\n<\/code>\r\n<\/pre>\n<p>Nun wurde das Skript zur Erneuerung des Let&#8217;s Encrypt Zertifikats erneut gestartet. Das Skript lief diesmal fehlerfrei durch und es wurde wieder eine vollst\u00e4ndige Zertifikatskette erstellt und in der vorgesehenen Datei gespeichert.<\/p>\n<p>Daher konnte die tempor\u00e4re vHost-Konfiguration nun wieder deaktiviert und die urspr\u00fcngliche Konfiguration wieder aktiviert werden.<\/p>\n<h2>Ma\u00dfnahmen zur Risikominimierung<\/h2>\n<p>Nachdem mich die St\u00f6rung nat\u00fcrlich zur ung\u00fcnstigsten Zeit (welche Zeit ist f\u00fcr eine St\u00f6rung schon g\u00fcnstig?) erwischt hat, habe ich mir Gedanken gemacht, wie sich das Risiko minimieren l\u00e4sst, dass mich der gleiche \u00c4rger in Zukunft nochmal ereilt.<\/p>\n<h3>Datensicherung anpassen<\/h3>\n<p>Zuerst habe ich meine Datensicherung angepasst und das Verzeichnis, welches die Zertifikatsdateien enth\u00e4lt, mit ins Backup aufgenommen. Selbstverst\u00e4ndlich ist das Backup nur die halbe Miete. Ob sich dieses erfolgreich wiederherstellen l\u00e4sst, muss nat\u00fcrlich auch getestet werden.<\/p>\n<h3>Fehler bei der Skript-Ausf\u00fchrung abfangen<\/h3>\n<p>Wie weiter oben bereits beschrieben, begann die ganze Problematik damit, dass das Skript zur Zertifikatserneuerung weiterarbeitete, obwohl bereits bei der Erneuerung des Zertifikats ein Fehler aufgetreten war.<\/p>\n<p>Zuk\u00fcnftig soll das Skript abbrechen, wenn bei der Verarbeitung ein Fehler auftritt. Dazu wurde es wie folgt erweitert:<\/p>\n<pre><code>\r\nfunction check() {\r\n  if [ $1 -gt 0 ]; then\r\n    echo \"Uuups, hier ist was schiefgegangen\"\r\n    echo \"exit $1\"\r\n    exit 1\r\n  fi\r\n}\r\n\r\npython acme-tiny-by-frezbo\/acme_tiny.py --no-verify --account-key \/var\/www\/seafile.example.com\/ssl\/account.key --csr \/var\/www\/seafile.example.com\/ssl\/seafile.example.com.csr --acme-dir \/var\/www\/seafile.example.com\/public\/.well-known\/acme-challenge\/ &gt; \/var\/www\/seafile.example.com\/ssl\/seafile.example.com.crt\r\n\r\ncheck $?\r\n\r\ncat \/var\/www\/seafile.example.com\/ssl\/seafile.example.com.crt \/var\/www\/seafile.example.com\/ssl\/lets-encrypt-x3-cross-signed.pem &gt; \/var\/www\/seafile.example.com\/ssl\/seafile.example.com_chained.crt\r\n\r\ncheck $?\r\n\r\nsudo service nginx reload\r\n<\/code>\r\n<\/pre>\n<p>Schl\u00e4gt die Erneuerung des Zertifikats fehl, gibt das Kommando einen Exit-Status gr\u00f6\u00dfer Null zur\u00fcck. Dies wird von der check-Funktion erkannt und das Skript abgebrochen. Dadurch bleiben die alte Datei mit der Zertifikatskette erhalten und der Dienst bleibt verf\u00fcgbar, bis das Problem mit der Zertifikatserneuerung behoben werden kann.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In diesem Beitrag werden die Ursache und der Verlauf der St\u00f6rung protokolliert, welche die vor\u00fcbergehende Nichterreichbarkeit einer Seafile[1. Seafile &#8211; Wikipedia]-Installation zur Folge hatte, welche ich f\u00fcr den Freifunk Lippe[2. Webpr\u00e4senz Freifunk Lippe] betreibe. Beschreibung der Umgebung Die Seafile-Installation wird ausf\u00fchrlich in &#8222;Installation von Seafile auf einem Ubuntu\/Debian Server&#8220; beschrieben. Die aktuelle Installation wurde wie<span class=\"continue-reading\"> <a href=\"https:\/\/www.my-it-brain.de\/wordpress\/protokoll-zum-ausfall-eines-webdienstes-nach-der-fehlgeschlagenen-erneuerung-eines-lets-encrypt-zertifikats\/\">[Weiterlesen&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_metis_text_type":"","_metis_text_length":0,"_post_count":0,"footnotes":""},"categories":[52],"tags":[379,430,305,422,318,417],"class_list":["post-1746","post","type-post","status-publish","format-standard","hentry","category-cloud-dienste","tag-lets-encrypt","tag-osbn","tag-planet","tag-seafile","tag-ssl","tag-tls"],"_links":{"self":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts\/1746","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/comments?post=1746"}],"version-history":[{"count":15,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts\/1746\/revisions"}],"predecessor-version":[{"id":1761,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts\/1746\/revisions\/1761"}],"wp:attachment":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/media?parent=1746"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/categories?post=1746"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/tags?post=1746"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}