Eigene Registry für Docker-Images - Ablage D

Lesezeit
7 Minuten
Bis jetzt gelesen

Eigene Registry für Docker-Images - Ablage D

01.12.2018 - 12:00
Veröffentlicht in:

Wer selber Docker-Images herstellt, braucht auch eine eigene Registry. Diese gibt es ebenfalls als Docker-Image, aber nur mit eingeschränkter Funktionalität. Mit einem Auth-Server wird daraus ein brauchbares Repository für Images.

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.

Zugangsbeschränkung und Rechtekontrolle

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.

docker_auth als Auth-Server

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."
Bild 1: Damit die Docker-Registry Authentifizierung und Autorisierung unterstützt, benötigt sie einen separaten Auth-Server, der Token ausstellt.
Bild 1: Damit die Docker-Registry Authentifizierung und Autorisierung unterstützt, benötigt sie einen separaten Auth-Server, der Token ausstellt.

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.

Bild 2: Die Docker-Registry protokolliert jeden einzelnen Request.
Bild 2: Die Docker-Registry protokolliert jeden einzelnen Request.

Probleme mit Zertifikaten umgehen

Wenn Sie eine Fehlermeldung über ein unsicheres Zertifikat erhalten, besteht die einfachste Lösung darin, den Docker-Daemon mit der Option "--insecure-registry" zu starten, gefolgt von einer Liste der Hostnamen ihrer Registries inklusive Port. Dies müssen Sie auf jedem Client tun, der die Registry verwenden möchte.

Der Grund für die Fehlermeldung liegt darin, dass beispielsweise aktuelle Linux-Distributionen das Root-Zertifikat von Let's Encrypt nicht in ihrer Keychain installiert haben, auf die Standard-Tools wie der Docker-Daemon (über die Krypto-Libraries) zurückgreifen – während etwa die Webbrowser eigene Keychains mitbringen. Listing 4 zeigt, wie Sie die Root-Zertifikate auf einem Ubuntu-System installieren und aktivieren. Dies müssen Sie nun allerdings auf allen Rechnern tun, von denen Sie oder Ihre Kollegen Docker verwenden, also möglicherweise auch auf Rechnern mit Windows oder macOS – da ist die Option mit den "--insecure-registries" sicher die einfachere.

Listing 4: Zertifikate von Let's Encrypt installieren



curl -O https://letsencrypt.org/certs/isrgrootx1.pem
curl -O https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
openssl x509 -in isrgrootx1.pem -inform PEM -out isrgrootx1.crt
openssl x509 -in lets-encrypt-x3-cross-signed.pem -inform PEM -out lets-encrypt-x3-cross-signed.crt
sudo cp *.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates –verbose
I already trust 148, your new list has 150
Certificate added: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
1 new root certificates were added to your trust store.
...
sudo systemctl restart docker
Bild 3: Die ACLs können auch in einer MongoDB-Datenbank gespeichert werden.
Bild 3: Die ACLs können auch in einer MongoDB-Datenbank gespeichert werden.

Registry-Betrieb als Proxy-Cache

Interessant dürfte für viele auch der Betrieb einer eigenen Registry als Proxy sein, von der Docker-Dokumentation auch "pull through cache" genannt. Dies ist kein großes Problem. Es genügt, in der Konfigurationsdatei die Anweisung "proxy" hinzuzufügen und darunter die URL der Upstream-Registry einzutragen:

proxy:remoteurl: https://registry-1.docker.io

Eine weitere Option ist es, die Registry wie auch den Auth-Server hinter einem Proxy wie Apache, Nginx oder HAProxy zu betreiben. Dann fällt die Portnummer aus den Registry-URLs heraus und Sie haben auf dem Server zwei offene Ports weniger. Typischerweise erledigen Sie dann die TLS-Terminierung auch über den Proxy, dann kann die Verschlüsselung bei den Servern wegfallen. Sie müssen nur bei der Proxy-Konfiguration anhand der aufgerufenen URLs zwischen den Backends differenzieren. Eine solche Konfiguration für HAProxy können Sie in Listing 5 sehen.

Listing 5: HAProxy-Konfiguration



use_backend registry if { hdr_end(host) -i remote.repository.com } { path_beg /v2 }
use_backend registry_auth if { hdr_end(host) -i remote.repository.com } { path_beg /auth }
…
backend registry server registry 127.0.0.1:5000
backend registry_auth server registry_auth 127.0.0.1:5001

Andere Docker-Registries

Eine Alternative zum Einsatz von docker_auth als Authentifizierungsbackend ist beispielsweise der Keycloak-Server von Red Hat [2]. Er steht ebenfalls als freie Software zur Verfügung, bietet zahlreiche weitere Features für Single Sign-on und auch ein webbasiertes Frontend – allerdings von Haus aus keine Möglichkeit zur Regelung der Autorisierung. Ein weiteres Projekt aus dem Hause Red Hat ist der Pulp-Repository-Server [3], der mittlerweile auch Docker unterstützt, aber wiederum nur rudimentär.

Wer gleich die komplette Registry inklusive Zugangskontrolle ersetzen möchte, kann sich die beiden Open-Source-Projekte Portus [4] von Suse und Harbor [5] von VMware anschauen. Beide sind aber in aktiver Entwicklung und nicht ganz einfach zu installieren und zu konfigurieren. Leichter geht es mit kommerziellen Produkten wie der Artifactory [6] von JFrog oder Sonatype Nexus, die neben Docker-Images auch zahlreiche weitere "Artefakte" hosten können, etwa Pakete für Apache Maven, RubyGems, RPMs und so weiter. Hier empfiehlt sich insbesondere ein Blick auf Sonatype Nexus [7], dessen Open-Source-Variante beinahe den kompletten Funktionsumfang der Enterprise-Ausführung bietet – abgesehen von Features wie Hochverfügbarkeit und dem Support durch den Hersteller.

Fazit

Eine eigene Registry für Docker-Images zu betreiben, ist nicht schwierig. Etwas aufwendiger wird es, wenn zur dateibasierten Authentifizierung noch eine Autorisierung für einzelne Ressourcen oder eine Anbindung an ein LDAP-Backend dazukommen soll. Beides lässt sich aber mit der freien Software "docker_auth" lösen. Als Alternativen bieten sich kommerzielle Docker-Registries an, die es zum Teil auch als leicht abgespeckte Open-Source-Varianten gibt.

[1] GitHub-Repository docker_auth
iap61

[2] Keycloak
iap62

[3] Pulp
iap63

[4] Portus
iap64

[5] Harbor
iap65

[6] JFrog Artifactory
iap66

[7] Sonatype Nexus OSS
iap67

Link-Codes

 

Aus IT-Administrator Magazin Ausgabe 12/2018: Datenspeicherung für KMUs Seite 44-47

Ähnliche Beiträge

IT-Tage 2023 – Konferenz für Entwicklung, Datenbanken, DevOps, Security und KI

„Alles unter einem Dach!“ ist das Motto der IT-Tage, der Jahreskonferenz des Fachmagazins Informatik Aktuell. Unter anderem werden Subkonferenzen zu den Themen Software-Entwicklung und -Architektur, Datenbanken, DevOps, Cloud & Serverless, IT-Security, Künstliche Intelligenz, Java, Python oder .NET angeboten.

Azure mit lokalen Netzen verbinden (3)

Azure bietet vielfältige Möglichkeiten, um Ressourcen in der Cloud mit lokalen Netzwerken zu verbinden. Dazu gehören auch Sicherheitsmechanismen und Loadbalancer, die den Datenverkehr zwischen Cloud und lokalem Rechenzentrum nicht nur zur Verfügung stellen, sondern absichern und hochverfügbar konfigurieren. IT-Administrator zeigt die Wege auf, um die Cloudumgebung mit dem lokalen Netzwerk zu verbinden. Im dritten und letzten Teil der Workshopserie zeigen wir, wie Sie virtuelle Firewalls hochziehen.