Ohne jemanden nahezutreten, lässt sich wohl behaupten, dass die Veröffentlichung der Docker-Software ein Schnellschuss war. Erst später wurden viele Features eingeführt, die bitter nötig waren. Ein Beispiel dafür ist die Registry, der Speicherplatz für Docker-Images, die nicht einmal einen Mechanismus zur Authentifizierung besaß. Später wurde das Image-Format geändert und die Registry 2.0 (auch: "Docker Distribution") veröffentlicht, die mit vielen Beschränkungen aufgeräumt hat. Sie besitzt immerhin einen Basismechanismus zur Authentifizierung, der sich aber auf die von Apache bekannten "htpasswd"-Dateien beschränkt.
Der Mechanismus beschränkt sich aber auf die Authentifizierung und bietet keine fein abgestufte Autorisierung der authentifizierten Benutzer für einzelne Ressourcen, also Lese/Schreibzugriff auf Images. Für viele Anwender, die Docker nur mal ausprobieren wollen, und Teams, in denen jeder alles darf, ist das aber ja vollkommen ausreichend. Fügen Sie also der Htpasswd-Datei einen User hinzu und starten Sie den Registry-Container, in dem Sie das Verzeichnis mit der Passwortdatei mounten. Die Authentifizierungseinstellungen übergeben Sie als Umgebungsvariablen:
$ sudo mkdir /etc/docker-registry
$ htpasswd -Bbn oliver T0Ps3crEt | sudo tee /etc/docker-registry/htpasswd
oliver:$2y$05$lAmkjHRcR0.TK52/rHR/Pe86AGZqpRleXenHVT/eabFe8He5UZiPu
$ docker run -p 5000:5000 --name registry -v /etc/docker-registry/:/auth -e "REGISTRY_AUTH=htpasswd" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" registry:2
Nun können Sie versuchen, die Registry zum Speichern von Images zu verwenden. Dazu laden Sie zunächst ein Image aus dem offiziellen Docker Hub herunter, zum Beispiel mit »docker pull alpine:latest
«
ein Image der platzsparenden Alpine-Distribution. Um das Image in die eigene Registry laden zu können, müssen Sie es mit dessen Hostnamen taggen:
$ docker tag alpine:latest remote.repository.com:5000/alpine-latest
Wenn Sie nun versuchen, das Image hochzuladen, erhalten Sie eine Fehlermeldung "unauthorized: authentication required". Sie müssen sich erst mit den oben vergebenen Credentials per »docker login ...
«
anmelden; anschließend klappt es auch mit dem Upload (Listing 1). In dem Listing sehen Sie auch, dass die Login-Daten in "$HOME/.docker/config.json" gespeichert werden. Um sie von dort wieder zu entfernen, verwenden Sie »docker logout remote.repository.com:5000
«
.
Listing 1: Login und Upload in die Registry
$ docker login remote.repository.com Authenticating with existing credentials... Login did not succeed, error: Error response from daemon: login attempt to https://remote.repository.com:5000/v2/ failed with status: 401 Unauthorized Username (ofrommel): oliver Password: WARNING! Your password will be stored unencrypted in /home/oliver/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store $ Login Succeeded docker push remote.repository.com:5000/alpine:latest The push refers to repository [remote.repository.com:5000/alpine] 73046094a9b8: Pushed latest: digest: sha256:0873c923e00e0fd2ba78041bfb64a105e1ecb7678916d1f7776311e45bf5634b size: 528
Um sich einmal mit Docker vertraut zu machen, mag diese Lösung auch genügen. Meistens will man aber noch etwas darüber hinausgehende Fähigkeiten wie etwa die oben angesprochene Autorisierung hinsichtlich einzelner Ressourcen. Konkret kann das etwa bedeuten, dass ein Benutzer uneingeschränkte Lese- und Schreibrechte für seinen Namespace ("remote.repository.com/User") erhält, aber nur Leserechte für andere Images.
Dieses lässt sich über die Identity-Management-API der Docker Registry realisieren, die tokenbasiert funktioniert. Will ein Anwender auf die Registry zugreifen, leitet der Server den Client auf einen Authentifizierungsserver um, der Login/Passwort überprüft und ein Token ausstellt (Bild 1). Darüber hinaus ist in dem Scope-Feld des Tokens festgelegt, welche Berechtigungen der Benutzer im Einzelnen besitzt.
Implementiert wird der beschriebene Ablauf beispielsweise von der freien Software "docker_auth", die von der Firma Cesanta entwickelt wurde und auf GitHub [1] wie auch als Image im Docker Hub zu finden ist. docker_auth bietet zur Authentifizierung die folgenden Methoden: eine statische Benutzerliste, ein Login mittels Google oder GitHub, eine LDAP-Anbindung, MongoDB oder ein externes Programm. Zur Autorisierung können statische ACLs, MongoDB oder ein externes Programm verwendet werden.
Zur Konfiguration dient eine YAML-Datei, die Sie dem Container wie gewohnt per Bind-Mount als Volume unterschieben. Dies gilt auch für die TLS-Zertifikate, die docker_auth verwendet. Beispiele für die Konfiguration finden Sie im GitHub-Repository im Verzeichnis "examples", wo auch die Datei "reference.yml" liegt, die sämtliche verfügbaren Optionen mit Kommentaren enthält. Ein Beispiel für die Konfiguration, die auf Backends wie MongoDB verzichtet und sowohl die User-Accounts wie auch die ACLs bereits enthält, ist in Listing 2 zu sehen.
Listing 2: Auth-Server-Konfiguration
server: addr: ":5001" certificate: "/le/live/remote.repository.com/cert.pem" key: "/le/live/remote.repository.com/privkey.pem" token: issuer: "MyRepository auth server" # muss mit Registry-Config übereinstimmen! expiration: 900 users: # Password is specified as a BCrypt hash. Use `htpasswd -nB USERNAME` to generate. "oliver": password: "$2y$05$4dIrCZLpgSYDClrS6pN2BOxVm.rkPy/4IgnurlHbukOxOJldlhJM." acl: - match: {account: "admin"} actions: ["*"] comment: "Admin has full access to everything." - match: {account: "user"} actions: ["pull"] comment: "User \"user\" can pull stuff."
Wieder werden die Hashes der Passwörter abgelegt, die sich mit Htpasswd erzeugen lassen. Die ACLs bestehen aus drei Elementen. "match" gibt an, für wen oder was die Regel gilt, "actions" spezifiziert, was damit gemacht werden darf, dann folgt noch ein Kommentar. Hier gibt es viele Möglichkeiten, den Zugriff zu regeln, bei denen man zum Beispiel Regular Expressions verwenden kann. Richtige Gruppen gibt es nicht, aber sie lassen sich mit den sogenannten Labeln simulieren. Auch eine Einschränkung auf IP-Adressen ist möglich. Die Dokumentation enthält eine ganze Reihe von Beispielen für ACLs, die sich auch per Copy-and-paste übernehmen lassen. Den Auth-Server starten Sie nun so:
$ docker run --name docker_auth -p 5001:5001 -v `pwd`:/config:ro \-v /var/log/docker_auth:/logs \-v /etc/letsencrypt:/le \cesanta/docker_auth:1 /config/config.yml
Auch die Docker-Registry, die wir oben mit Umgebungsvariablen gestartet haben, lässt sich mit einer YAML-Datei konfigurieren. Jede Einstellung in der Datei entspricht einer Umgebungsvariablen, die den YAML-Pfad durch Unterstriche trennt. Der erste Wert "REGISTRY" fällt dabei weg, da er nur den Container beziehungsweise die Anwendung kennzeichnet. Für "REGISTRY_AUTH_HTPASSWD_PATH" würde also folgende Struktur in der YAML-Datei entsprechen:
auth:
htpasswd:
path: "/auth/htpasswd"
Der konkrete Wert kommt allerdings im Folgenden nicht zum Einsatz, da wir ja statt der Htpasswd-Authentifizierung den Auth-Server verwenden möchten. Die entsprechende Konfiguration ist in Listing 3 zu sehen. Neben dem Speicherort der Registry-Daten ("storage/filesystem/rootdirectory"), der sich natürlich nicht im Container, sondern auf dem Host befindet, ist vor allem das Authentifizierungs-Realm interessant, hinter dem sich der Auth-Server befindet. Außdem verwenden wir Let's-Encrypt-Zertifikate, die ebenfalls auf dem Host installiert sind.
Listing 3: Registry-Konfiguration
version: 0.1 log: fields: service: registry storage: filesystem: rootdirectory: /var/lib/registry http: addr: :5000 tls: certificate: "/le/live/remote.repository.com/cert.pem" key: "/le/live/remote.repository.com/privkey.pem" auth: token: realm: "https://remote.repository.com:5001/auth" service: "Docker registry" issuer: "MyRepository auth server" rootcertbundle: "/le/live/remote.repository.com/fullchain.pem"
Ist die Konfigurationsdatei unter dem Namen "config.yml" im aktuellen Verzeichnis gespeichert, starten Sie die Registry mit dem folgenden Aufruf:
docker run -p 5000:5000 --name registry \
-v `pwd`/config.yml:/etc/docker/registry/config.yml \
-v /var/docker-registry:/var/lib/registry \
-v /etc/letsencrypt:/le registry:2
Nun können Sie die Registry wie oben gezeigt verwenden. User-Accounts und ACLs legen Sie wie beschrieben in der Konfigurationsdatei von docker_auth ab und starten den Container neu. In der Log-Ausgabe der Registry können Sie sich die einzelnen Requests ansehen, was auch bei der Fehlersuche hilft (Bild 2). Etwas dynamischer als mit der Konfigurationsdatei wird das Setup mit MongoDB als Datenbank. Hier kann die Konfiguration geändert werden, ohne den Container immer neu starten zu müssen. Komfortabler wird die Administration allerdings nicht unbedingt, denn die ACLs werden in der gleichen Syntax wie in der Konfigurationsdatei in der Datenbank gespeichert (Bild 3). Zur Authentifizierung ist, wie erwähnt, für viele Anwender sicher auch LDAP interessant, aber die ACLs müssen dabei trotzdem auf einem anderen Weg hinterlegt werden, sei es im File oder in MongoDB.