Traefik, docker and docker registry

Under the cut you will see:

  1. Using Traefik as a reverse proxy to route traffic inside docker containers.





  2. Using Traefik to Automatically Obtain Let's Encrypt Certificates





  3. Using Traefik to differentiate access to the docker registry using basic auth





  4. All of the above will be configured exclusively within docker-compose.yml and will not require separate configuration files to be passed into containers.





Relevance of the issue

Almost all instructions on the Internet use several additional configuration files that will need to be copied to the container at startup. We found a way to make all the necessary settings exclusively inside the compose file.





In addition, there is little information on the Internet on the use of traefik to control access to the docker registry. The technique described below can be used to control access to any application that implements the Rest API.





Finding a solution

Here is a link to the official docker registry deployment article. Scroll down the page and see an example of deployment via docker-compose. I'll reprint the example below:





registry:
  restart: always
  image: registry:2
  ports:
    - 5000:5000
  environment:
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
    REGISTRY_HTTP_TLS_KEY: /certs/domain.key
    REGISTRY_AUTH: htpasswd
    REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
    REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
    - /path/data:/var/lib/registry
    - /path/certs:/certs
    - /path/auth:/auth
      
      



https registry, . . https , , , let's encrypt - traefik.





registry, . ,    . nginx, .





nginx . , traefik.





, .





Registry

registry compose . β€œregistry", compose :





mkdir registry
cd registry
nano docker-compose.yml
      
      



:





version: '2.4'
services:
  registry:
    restart: always
    image: registry:2
    ports:
      - 5000:5000
      
      







docker-compose up -d
      
      



http://<IP>:5000/v2/_catalog , <IP> - ip . 

:





{"repositories":[]}
      
      



, . - firewall.





Traefik

traefik . 

SSL, .





registry compose . β€œregistry", compose :





mkdir traefik
cd traefik
nano docker-compose.yml
      
      



:





version: "2.4"
 
services:
 
  traefik:
    image: "traefik:v2.4"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
    ports:
      - "8080:8080"
      
      



()
command:
- "--api.insecure=true"
      
      



command .

dashboard insecure . , dashboard traefik. traefik , 8080.





      - "8080:8080"
      
      



8080 docker traefik. , dashboard traefik





:





docker-compose up -d
      
      



IP 8080:





Registry Traefik ( )

, compose , , . 

.





compose Traefik:





Version: "2.4"
 
services:
 
  traefik:
    image: "traefik:v2.4"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
    ports:
      - "80:80"
      - "8080:8080"
    networks:
      - registry_default
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
  registry_default:
    external: true
      
      



()
- "--providers.docker=true"
      
      



. traefik .





- "--providers.docker.exposedbydefault=false"
      
      



HTTP HTTP traefik. , traefik docker , expose . . 





, ! , - ! : hosts β€œIP__ _”, β€œhttp://_" .





- "80:80"
      
      



80 (http) docker traefik. .





    networks:
      - registry_default
networks:
  registry_default:
    external: true
      
      



, compose . , compose β€œ___default, compose .





volumes:
  - "/var/run/docker.sock:/var/run/docker.sock:ro"
      
      



docker.sock traefik. , traefik . , , .





compose Registry:





version: '2.4'
services:
  registry:
    restart: always
    image: registry:2
    ports:
      - 5000:5000
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry.rule=Host(`<REGISTRY.FQDN>`)"
      
      



()
- "traefik.enable=true"
      
      



traefik,





- "traefik.http.routers.registry.rule=Host(`<REGISTRY.FQDN>`)"
      
      



traefik, <REGISTRY.FQDN> . , , .





- "traefik.http.services.registry.loadbalancer.server.port=5000"
      
      



, , . docker 1 , . 





:





docker-compose up -d
      
      



http://<REGISTRY.FQDN>:5000/v2/_catalog , <REGISTRY.FQDN> - , compose .





:





{"repositories":[]}
      
      



.





SSL ( https)

SSL Let's Encrypt.





compose Traefik:





version: "2.4"
 
services:
 
  traefik:
    image: "traefik:v2.4"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=<EMAIL>"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    networks:
      - registry_default
    volumes:
      - "letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
volumes:
  letsencrypt:
networks:
  registry_default:
    external: true
      
      



()
- "--entrypoints.web.address=:80"
      
      



entrypoint http web .





- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      
      



entrypoint web websecure. HTTP HTTPS





- "--entrypoints.websecure.address=:443"
      
      



entrypoint 443 websecure





- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      
      



Let’s Encrypt http challenge





- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      
      



entrypoint http challenge





- "--certificatesresolvers.myresolver.acme.email=<EMAIL>"
      
      



<email>





- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      
      



acme.json. . , β€œ/acme.json" .





- "443:443"
      
      



443 (https) docker traefik. .





    volumes:

      - "letsencrypt:/letsencrypt"

volumes:

  letsencrypt:
      
      



SSL . .

: /var/lib/docker/volumes/< >





compose Registry:





version: '2.4'
services:
  registry:
    restart: always
    image: registry:2
    ports:
      - 5000:5000
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry.rule=Host(`<REGISTRY.FQDN>`)"
      - "traefik.http.routers.registry.entrypoints=websecure"
      - "traefik.http.routers.registry.tls.certresolver=myresolver"
      
      



()
- "traefik.http.routers.registry.entrypoints=websecure"
      
      



entrypoint http (web) websecure





- "traefik.http.routers.registry.tls.certresolver=myresolver"
      
      



SSL





:





docker-compose up -d
      
      



http://<REGISTRY.FQDN>:5000/v2/_catalog , <REGISTRY.FQDN> - , compose .





, :





  • http https





  • Let's Encrypt





, traefik - . , docker logs traefik



.





SSL dashboard

, dashboard , . , , .





compose Traefik:





version: "2.4"
 
services:
 
  traefik:
    image: "traefik:v2.4"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=<EMAIL>"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    networks:
      - registry_default
    volumes:
      - "letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`<TRAEFIK.FQDN>`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=myresolver"
      - "traefik.http.routers.traefik.service=api@internal"
volumes:
  letsencrypt:
networks:
  registry_default:
    external: true
      
      



()
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.traefik.rule=Host(`<TRAEFIK.FQDN>`)"
  - "traefik.http.routers.traefik.entrypoints=websecure"
  - "traefik.http.routers.traefik.tls.certresolver=myresolver"
      
      



<traefik.fqdn> dashboard.





- "traefik.http.routers.traefik.service=api@internal"
      
      



.





api@internal - . dashboard .





registry, .





traefik:





docker-compose up -d
      
      



http://<TRAEFIK.FQDN>, <TRAEFIK.FQDN> - traefik dashboard, compose .





. .





compose Traefik:





version: "2.4"
 
services:
 
  traefik:
    image: "traefik:v2.4"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=<email>"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    networks:
      - registry_default
    volumes:
      - "letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - "traefik.http.middlewares.traefik-compress.compress=true"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`<TRAEFIK.FQDN>`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=myresolver"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=traefik-compress"
volumes:
  letsencrypt:
networks:
  registry_default:
    external: true
      
      



()
- "traefik.http.middlewares.traefik-compress.compress=true"
      
      



middleware traefik-compress . middleware .





- "traefik.http.routers.traefik.middlewares=traefik-compress"
      
      



middleware traefik-compress traefik





compose Registry:





version: '2.4'
services:
  registry:
    restart: always
    image: registry:2
    ports:
      - 5000:5000
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry.rule=Host(`<REGISTRY.FQDN>`)"
      - "traefik.http.routers.registry.entrypoints=websecure"
      - "traefik.http.routers.registry.tls.certresolver=myresolver"
      - "traefik.http.routers.registry.middlewares=traefik-compress"
      
      



()
- "traefik.http.routers.registry.middlewares=traefik-compress"
      
      



middleware traefik-compress registry





basic Dashboard

htpasswd. , (, Ubuntu):





apt-get install apache2-utils
      
      



β€œ$” ( $ $$), docker-compose.yml





echo $(htpasswd -nbB USER "PASS") | sed -e s/\\$/\\$\\$/g
      
      



( ):





USER:$$2y$$05$$iPGcI0PwxkDoOZUlGPkIFe31e47F5vewcjlhzhgf0EHo45H.dFyKW
      
      



docker-compose.yml traefik ,   <USER-PASSWORD-OUTPUT>



.





compose Registry:





version: "2.4"
 
services:
 
  traefik:
    image: "traefik:v2.4"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=<EMAIL>"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    networks:
      - registry_default
    volumes:
      - "letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - "traefik.http.middlewares.traefik-compress.compress=true"
      - "traefik.http.middlewares.auth.basicauth.users=<USER-PASSWORD-OUTPUT>"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`<TRAEFIK.FQDN>`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=myresolver"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=traefik-compress,auth"
volumes:
  letsencrypt:
networks:
  registry_default:
    external: true
      
      



()
- "traefik.http.middlewares.auth.basicauth.users=<USER-PASSWORD-OUTPUT>"
      
      



middleware auth . middleware .





- "traefik.http.routers.traefik.middlewares=traefik-compress,auth"
      
      



middleware auth traefik





: ( .env ) docker-compose.yml <USER-PASSWORD-OUTPUT>,   $.  : 





echo $(htpasswd -nbB <USER> "<PASS>")
      
      



docker (docker-compose up -d) , dashboard traefik .





Registry

compose Registry:





version: '2.4'
services:
  registry:
    restart: always
    image: registry:2
    ports:
      - 5000:5000
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry.rule=Host(`REGISTRY.FQDN`) && Method(`POST`, `PUT`, `DELETE`, `PATCH`)"
      - "traefik.http.routers.registry.entrypoints=websecure"
      - "traefik.http.routers.registry.tls.certresolver=myresolver"
      - "traefik.http.routers.registry.service=registry"
      - "traefik.http.services.registry.loadbalancer.server.port=5000"
      - "traefik.http.routers.registry.middlewares=auth-registry,traefik-compress"
      - "traefik.http.middlewares.auth-registry.basicauth.users=<ADMIN-PASSWORD-OUTPUT>"
      - "traefik.http.routers.guest-registry.rule=Host(`REGISTRY.FQDN`) && Method(`GET`, `HEAD`)"
      - "traefik.http.routers.guest-registry.entrypoints=websecure"
      - "traefik.http.routers.guest-registry.tls.certresolver=myresolver"
      - "traefik.http.routers.guest-registry.service=guest-registry"
      - "traefik.http.services.guest-registry.loadbalancer.server.port=5000"
      - "traefik.http.routers.guest-registry.middlewares=aguest-registry,traefik-compress"
      - "traefik.http.middlewares.aguest-registry.basicauth.users=<USER-PASSWORD-OUTPUT>"

      
      



2 :





  • registry



    - (\)





  • guest-registry



    - ()





middleware basic .





, . , . - .





registry:





docker-compose up -d
      
      



Postman

.





Get - .





Post - 401.





.





Get - .





Post - . , registry, . .





traefik nginx, docker .





The only problem we faced when migrating to traefik is the inability to use negatives in the routing rules. You can read more about the problem here .








All Articles