Python, urllib und CERTIFICATE_VERIFY_FAILED

Da denkt man sich einfach "mal kurz mein Blogging-Container neu bauen" und schon funktioniert nichts mehr. Es fing schon damit an das mein Ansible Playbook Probleme hatte Files herrunterzuladen. urllib brach immer mit urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] ab. Wie aus dem nichts. Vor ein paar Wochen gab es keine Probleme mit der urllib. Es gab sogar ein StackOverflow Posting welches mir riet einfach mal die ca-certificates zu installieren. Das Paket war aber schon installiert. Nach viel googeln un verzweifeln bin ich auf eine obskure Seite gestoßen von der ich kaum etwas verstand. Es gab wohl die Möglichkeit urllib über eine Environment-Variabel den Ort der CA-Zertifikate mitzuteilen. Dies scheint auch in der PEP 476 so beschrieben. requests scheint dies per Default zu machen. Ein export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt fixed die Situation in Alpine Linux. Ich frage mich nur was sich in ein paar Wochen so verändert hat das dieses Problem auftritt.

Das Letsencrypt Docker Moped

Darauf haben wir lange gewartet: SSL für alle!!1!!!111! Wie oft musste ich Leuten erzählen das sie keine Angst haben müssen vor dem Polizisten mit dem Schlagstock (zumindestens nicht vor dem im Chrome Browser). Ich hatte persöhnlich nie etwas gegen selbstsignierte Zertifikate, wenn ich nicht gerade Geld über diese Seiten schubsen musste. Aber könnte nun alles der Vergangenheit angehören. Letsencrypt bieten nun einen einfachen Weg an um sich seine Zertifikate unterschreiben zu lassen. Und nicht nur das. Sie wollen Tools mitliefern, die ohne viel Vorwissen die Arbeit für einen erledigen. Sprich Keys erzeugen und den Zertfikatsrequest. Dann findet eine überprüfung statt ob die Domain für die ihr das Zertifikat haben wollt, auch wirklich euch gehört. Ein wichtiger Schritt. Nur weil ihr keine Hunderte von Euro für ein Zertifikat ausgibt, sollt ihr trotzdem das Recht haben das der Transport über das Netz verschlüßelt ist. Ohne Schlagstock-Polizisten-Warning. Nun geht das Projekt in die BETA-Phase und die üblich Verdächtigen(WARNUNG: fefe link) ranten rum. Räudiges Python und alles viel zu einfach... und vor allem... jeder weiß doch wie man openssl richtig bedient und wo man richtige Zertifikate bekommt. Ich finde den Ansatz gut. Natürlich ist der Client noch nicht dort angekommen wo er ankommen will. Aber es funktioniert. Der Client bietet mehrere Modi an. Der eine öffent die Ports 80 und 443 und Letsencrypt schaut dann ob sie erreichbar sind und tauschen ein paar Sachen aus. Problem: Auf dem Server läuft meist schon ein Webserver und blockiert die Ports. Ich muss also NGINX stoppen, den Client starten und dann NGINX wieder anschieben. Irgendwie nicht so schön. Eine andere Möglichkeit ist webroot. Dafür legt der Client ein paar Ordner in das Root-Verzeichnis des Webservers und der Dienst schaut dann dort nach. Problem hierbei: Ich habe in meine NGINX keine Files im Root. Ich benutze NGINX nur als Reverse-Proxy. Es gibt Hilfe:

Das NGINX Snippet

location /.well-known/acme-challenge {
    alias /etc/letsencrypt/webrootauth/.well-known/acme-challenge;
    location ~ /.well-known/acme-challenge/(.*) {
        add_header Content-Type application/jose+json;
    }
}

Und wenn wir dieses Snippet nun in den server-Teil der NGINX config "includen" haben wir die passende Location damit Letsencrypt alles findet.

Letsencrypt im Docker-Container

Der Client versucht sich vor jedem Ausführen nochmal selber zu updaten. Keine schlechte Idee in der BETA-Phase. Ich dachte nur ich versuche alles ein wenig aufzubrechen und in ein Docker-Image zu gießen. Dies sollte (Dank Alpine-Linux) schön klein Sein und schon alle wichtigen Optionen im ENTRYPOINT beinhalten. Das man nur noch die Domains, für die man Zertifikate haben will, anhängt. Der Gedanke dahinter: Man kann es einfach in die Crontab packen und alle Abhängigkeiten sind im Container gefangen. Die Repo liegt hier. Für noch mehr Convenience nutze ich auch mein geliebtes docker-compose.

letsencrypt:
  build: letsencrypt/
  volumes:
    - /etc/letsencrypt:/etc/letsencrypt
    - /var/lib/letsencrypt:/var/lib/letsencrypt
  environment:
    EMAIL: marvin@xsteadfastx.org
    WEBROOTPATH: /etc/letsencrypt/webrootauth
  command:
    "-d emby.xsteadfastx.org -d cloud.xsteadfastx.org -d reader.xsteadfastx.org -d git.xsteadfastx.org"

Ich benutze Compose so oft wie es geht. Dort kann ich schön alles definieren wie der Container ausgeführt werden soll. Eine saubere Sache. Erstmal werden die Volumes eingebunden. Diese beiden Verzeichnise braucht der Letsencrypt-Client. Dann werden zwei Environment-Variablen definiert. Email und der Pfad in dem der Client die Files zur Authentifizierung ablegt. Als command werden die Domains jeweils mit der Option -d angehängt. Mit docker-compose up wird erst das Image gebaut und dann ausgeführt.

Ich bin gespannt wie es weitergeht. Natürlich ist mein Container auch nur der erste Entwurf... aber ich werde weiter daran basteln.

NGINX force https

Am liebsten setze ich zur Zeit NGINX als Webserver ein. Vor allem für meine Owncloud installation und ein paar Python Sachen. Das "Problem" war bis jetzt, dass er Anfragen auf "http" nicht auf "https" weitergeleitet hat. Nicht wirklich ein Problem, sondern nur nervig wenn man wiedermal vergessen hat explezit "https" davor zu schreiben. In der Config mit dem funktionierenden SSL-Server Teil schreibe ich einen zweiten "server"-Bereich:

server {
    listen 80;
    server_name cloud.xsteadfastx.org;
    return 301 https://$server_name$request_uri;
}

Viele regeln das mit Regex, laut NGINX-Wiki ist dies aber die saubere Methode.

ssl und ngircd

Es kam die Idee auf für das Team einen IRC-Channel aufzumachen. Einfach um sich besser koordinieren zu können. Da gibt es natürlich zwei Möglichkeiten: Wir benutzen einfach Freenode oder wir setzen was eigenes auf mittles ngircd. Da ich das eh immer schonmal machen wollte, viel die Wahl auf Möglichkeit Nummer Zwei. Bis jetzt ist alles super einfach gehalten. Channels werden erstmal nur über die Config definiert und der Server spricht SSL only.

Erstmal irgendwie ein self-signed-el-billo Zertifikat erstellt:

certtool --generate-privkey --bits 2048 --outfile server-key.pem
certtool --generate-self-signed --load-privkey server-key.pem --outfile server-cert.pem
certtool --generate-dh-params --bits 4096 --outfile dhparams.pem

Dann haben ich die ngird.conf zusammen geknuppert:

[GLOBAL]
    Name = blubb.site 
    AdminInfo1 = Foo
    AdminInfo2 = Bar
    AdminEMail = marvin@blubb.site
    Info = IRC Server
    Listen = ::,0.0.0.0
    MotdFile = /etc/ngircd/ngircd.motd
    MotdPhrase =
    Password = 
    PidFile = /var/run/ngircd/ngircd.pid
    Ports = 
    ServerGID = irc
    ServerUID = irc

[LIMITS]
    ConnectRetry = 60
    MaxConnections = 500
    MaxConnectionsIP = 10
    MaxJoins = 10
    MaxNickLength = 9
    PingTimeout = 120
    PongTimeout = 20

[OPTIONS]
    AllowRemoteOper = no
    ChrootDir = 
    CloakHost = 
    CloakUserToNick = no
    ConnectIPv4 = yes
    ConnectIPv6 = yes
    DNS = yes
    MorePrivacy = no
    NoticeAuth = no
    OperCanUseMode = yes
    OperServerMode = no
    PredefChannelsOnly = yes
    RequireAuthPing = no
    ScrubCTCP = no
    SyslogFacility = local1
    WebircPassword =

[SSL]
    CertFile = /etc/ngircd/server-cert.pem
    DHFile = 
    KeyFile = /etc/ngircd/server-key.pem
    KeyFilePassword = 
    Ports = 6667

[CHANNEL]
    Name = #it
    Modes = n
    Key = 
    MaxUsers = 0
    Topic = Ecclesia IT
    KeyFile =

Ich habe die "Ports" in "[GLOBAL]" auskommentiert und dafür nur die Ports in "[SSL]" stehen. Das sollte Verbindungen nur über SSL erlauben. Desweiteren können nur die per Config definierten Channels benutzt werden. Das macht die Zeile "PredefChannelsOnly = yes".

Das nächste Problem was sich mir in den wegstellte, war das selbstsignierte Zertifkat und weechat. Der mochte sich einfach nicht verbinden. Nicht schön aber "/set irc.server.blubb.ssl_verify off" half.

Alles noch recht simpel. Läuft aber.