SUSE / Portus

Authorization service and frontend for Docker registry (v2)
http://port.us.org/
Apache License 2.0
3k stars 471 forks source link

Cannot login to registry (401 error) #2206

Closed Lelenaic closed 5 years ago

Lelenaic commented 5 years ago

Description

I cannot login to the docker registry with the admin user, or any non-admin user. I get a 401 error.

Steps to reproduce

  1. Clone repositoty
  2. Modify .env FQDN, KEY and PORTUS PASSWORD
  3. Place valid certificates (generated with letsencrypt) in the secrets folder
  4. Place the letsencrypt root certificate un the secrets folder
  5. Start docker-compose.yml
  6. Create default admin account, and a user account
  7. Configure registry in portus
  8. Try docker login your.domain.name or docker login your.domain.name:5000 with admin or user credentials

Here are the logs with the debug level for the portus container just after doing the docker login command:

portus_1      | Started GET "/v2/token?account=test&client_id=docker&offline_token=true&service=registry.weview.fr" for 109.190.218.93 at 2019-06-05 15:16:00 +0000
portus_1      | Processing by Api::V2::TokensController#show as JSON
portus_1      |   Parameters: {"account"=>"test", "client_id"=>"docker", "offline_token"=>"true", "service"=>"registry.weview.fr"}
portus_1      |   User Load (1.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'test' LIMIT 1
portus_1      |   ApplicationToken Load (0.8ms)  SELECT `application_tokens`.* FROM `application_tokens` WHERE `application_tokens`.`user_id` = 3
portus_1      |   User Load (1.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'test' ORDER BY `users`.`id` ASC LIMIT 1
portus_1      |    (0.7ms)  BEGIN
portus_1      |   User Update (1.2ms)  UPDATE `users` SET `current_sign_in_at` = '2019-06-05 15:16:00', `last_sign_in_at` = '2019-06-05 15:15:31', `sign_in_count` = 12, `updated_at` = '2019-06-05 15:16:00' WHERE `users`.`id` = 3
portus_1      |    (1.1ms)  COMMIT
portus_1      |   Registry Load (2.0ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`hostname` = 'registry.weview.fr' LIMIT 1
portus_1      | No hostname matching `registry.weview.fr', testing external_hostname
portus_1      |   Registry Load (3.6ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`external_hostname` = 'registry.weview.fr' LIMIT 1
portus_1      | [jwt_token] [claim] {:iss=>"registry.weview.fr", :sub=>"test", :aud=>"registry.weview.fr", :iat=>1559747760, :nbf=>1559747755, :exp=>1559748660, :jti=>"RKnoKZi2twd87NWbkjT5GGHcBa3fx722jt6FiDzNqU"}
portus_1      | Completed 200 OK in 150ms (Views: 1.4ms | ActiveRecord: 11.9ms)

Registry container logs:

registry_1    | time="2019-06-05T15:32:31Z" level=warning msg="error authorizing context: authorization token required" go.version=go1.7.6 http.request.host=registry.weview.fr http.request.id=5388514f-bb8a-41c9-b1d6-44f02caa6de5 http.request.method=GET http.request.remoteaddr=109.190.218.93 http.request.uri="/v2/" http.request.useragent="docker/18.09.2 go/go1.10.6 git-commit/6247962 kernel/4.9.125-linuxkit os/linux arch/amd64 UpstreamClient(Docker-Client/18.09.2 \\(darwin\\))" instance.id=b1564f65-7193-4ac8-9d21-fbe40fb2cd61 version=v2.6.2-14-ga66a4c3 
registry_1    | 172.25.0.6 - - [05/Jun/2019:15:32:31 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/18.09.2 go/go1.10.6 git-commit/6247962 kernel/4.9.125-linuxkit os/linux arch/amd64 UpstreamClient(Docker-Client/18.09.2 \\(darwin\\))"
registry_1    | time="2019-06-05T15:32:33Z" level=info msg="token signed by untrusted key with ID: \"6EB2:LFM4:2NTZ:W3IR:I4LW:ZFVP:Z56O:WWM3:36L2:2RQN:7C5F:63HZ\"" 
registry_1    | time="2019-06-05T15:32:33Z" level=warning msg="error authorizing context: invalid token" go.version=go1.7.6 http.request.host=registry.weview.fr http.request.id=91200248-4efc-423c-9a64-a30f8f710eab http.request.method=GET http.request.remoteaddr=109.190.218.93 http.request.uri="/v2/" http.request.useragent="docker/18.09.2 go/go1.10.6 git-commit/6247962 kernel/4.9.125-linuxkit os/linux arch/amd64 UpstreamClient(Docker-Client/18.09.2 \\(darwin\\))" instance.id=b1564f65-7193-4ac8-9d21-fbe40fb2cd61 version=v2.6.2-14-ga66a4c3 
registry_1    | 172.25.0.6 - - [05/Jun/2019:15:32:33 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/18.09.2 go/go1.10.6 git-commit/6247962 kernel/4.9.125-linuxkit os/linux arch/amd64 UpstreamClient(Docker-Client/18.09.2 \\(darwin\\))"

Background logs (background service always crashes at start btw):

 background_1  | /usr/bin/bundle:23:in `load': cannot load such file -- /usr/lib64/ruby/gems/2.6.0/gems/bundler-1.16.4/exe/bundle (LoadError)
background_1  |         from /usr/bin/bundle:23:in `<main>'
background_1  | Database ready
background_1  | [schema] Selected the schema for mysql
background_1  | [Mailer config] Host:     registry.weview.fr
background_1  | [Mailer config] Protocol: https://
background_1  | Creating scope :owner. Overwriting existing method TeamUser.owner.
background_1  | Creating scope :contributor. Overwriting existing method TeamUser.contributor.
background_1  | Creating scope :viewer. Overwriting existing method TeamUser.viewer.
background_1  | /usr/bin/bundle:23:in `load': cannot load such file -- /usr/lib64/ruby/gems/2.6.0/gems/bundler-1.16.4/exe/bundle (LoadError)
background_1  |         from /usr/bin/bundle:23:in `<main>'
background_1  | /srv/Portus/lib/portus/registry_client.rb:197:in `get_page': Something went wrong while fetching the catalog Response: [401] - {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]} (Portus::RegistryClient::RegistryError)
background_1  |         from /srv/Portus/lib/portus/registry_client.rb:174:in `paged_response'
background_1  |         from /srv/Portus/lib/portus/registry_client.rb:121:in `catalog'
background_1  |         from /srv/Portus/lib/portus/background/sync.rb:63:in `block in execute!'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:70:in `block (2 levels) in find_each'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:70:in `each'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:70:in `block in find_each'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:136:in `block in find_in_batches'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:238:in `block in in_batches'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:222:in `loop'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:222:in `in_batches'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:135:in `find_in_batches'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/batches.rb:69:in `find_each'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/querying.rb:11:in `find_each'
background_1  |         from /srv/Portus/lib/portus/background/sync.rb:62:in `execute!'
background_1  |         from /srv/Portus/bin/background.rb:61:in `block (2 levels) in <top (required)>'
background_1  |         from /srv/Portus/bin/background.rb:58:in `each'
background_1  |         from /srv/Portus/bin/background.rb:58:in `each_with_index'
background_1  |         from /srv/Portus/bin/background.rb:58:in `block in <top (required)>'
background_1  |         from /srv/Portus/bin/background.rb:57:in `loop'
background_1  |         from /srv/Portus/bin/background.rb:57:in `<top (required)>'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/commands/runner/runner_command.rb:38:in `load'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/commands/runner/runner_command.rb:38:in `perform'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/command/base.rb:65:in `perform'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/command.rb:46:in `invoke'
background_1  |         from /srv/Portus/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/commands.rb:18:in `<top (required)>'
background_1  |         from bin/rails:12:in `require'
background_1  |         from bin/rails:12:in `<main>'
background_1  | [Initialization] Running: 'Registry events', 'Registry synchronization'
background_1  | exit status 1
docker-registry_background_1 exited with code 1

Deployment information

Deployment method: I'm using the docker-compose.yml file. Here it is:

version: "2"

services:
  portus:
    image: opensuse/portus:head
    environment:
      - PORTUS_MACHINE_FQDN_VALUE=${MACHINE_FQDN}

      # DB. The password for the database should definitely not be here. You are
      # probably better off with Docker Swarm secrets.
      - PORTUS_DB_HOST=db
      - PORTUS_DB_DATABASE=portus_production
      - PORTUS_DB_PASSWORD=${DATABASE_PASSWORD}
      - PORTUS_DB_POOL=5
      - PORTUS_LOG_LEVEL=debug

      # Secrets. It can possibly be handled better with Swarm's secrets.
      - PORTUS_SECRET_KEY_BASE=${SECRET_KEY_BASE}
      - PORTUS_KEY_PATH=/certificates/privkey.pem
      - PORTUS_PASSWORD=${PORTUS_PASSWORD}

      # SSL
      - PORTUS_PUMA_TLS_KEY=/certificates/privkey.pem
      - PORTUS_PUMA_TLS_CERT=/certificates/fullchain.pem

      # NGinx is serving the assets instead of Puma. If you want to change this,
      # uncomment this line.
      #- RAILS_SERVE_STATIC_FILES='true'
    ports:
      - 3000:3000
    links:
      - db
    volumes:
      - ./secrets:/certificates:ro
      - static:/srv/Portus/public

  background:
    image: opensuse/portus:head
    depends_on:
      - portus
      - db
    environment:
      # Theoretically not needed, but cconfig's been buggy on this...
      - CCONFIG_PREFIX=PORTUS
      - PORTUS_MACHINE_FQDN_VALUE=${MACHINE_FQDN}

      # DB. The password for the database should definitely not be here. You are
      # probably better off with Docker Swarm secrets.
      - PORTUS_DB_HOST=db
      - PORTUS_DB_DATABASE=portus_production
      - PORTUS_DB_PASSWORD=${DATABASE_PASSWORD}
      - PORTUS_DB_POOL=5

      # Secrets. It can possibly be handled better with Swarm's secrets.
      - PORTUS_SECRET_KEY_BASE=${SECRET_KEY_BASE}
      - PORTUS_KEY_PATH=/certificates/privkey.pem
      - PORTUS_PASSWORD=${PORTUS_PASSWORD}

      - PORTUS_BACKGROUND=true
    links:
      - db
    volumes:
      - ./secrets:/certificates:ro

  db:
    image: library/mariadb:10.0.23
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --init-connect='SET NAMES UTF8;' --innodb-flush-log-at-trx-commit=0
    environment:
      - MYSQL_DATABASE=portus_production

      # Again, the password shouldn't be handled like this.
      - MYSQL_ROOT_PASSWORD=${DATABASE_PASSWORD}
    volumes:
      - ./volumes/mariadb:/var/lib/mysql

  registry:
    image: library/registry:2.6
    command: ["/bin/sh", "/etc/docker/registry/init"]
    environment:
      # Authentication
      REGISTRY_AUTH_TOKEN_REALM: https://${MACHINE_FQDN}/v2/token
      REGISTRY_AUTH_TOKEN_SERVICE: ${MACHINE_FQDN}
      REGISTRY_AUTH_TOKEN_ISSUER: ${MACHINE_FQDN}
      REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE: /secrets/cabundle.cert

      # SSL
      REGISTRY_HTTP_TLS_CERTIFICATE: /secrets/fullchain.pem
      REGISTRY_HTTP_TLS_KEY: /secrets/privkey.pem
      # Portus endpoint
      REGISTRY_NOTIFICATIONS_ENDPOINTS: >
        - name: portus
          url: https://${MACHINE_FQDN}/v2/webhooks/events
          timeout: 2000ms
          threshold: 5
          backoff: 1s
    volumes:
      - ./volumes/registry:/var/lib/registry
      - ./secrets:/secrets:ro
      - ./registry/config.yml:/etc/docker/registry/config.yml:ro
      - ./registry/init:/etc/docker/registry/init:ro
    ports:
      - 5000:5000
      - 5001:5001 # required to access debug service
    links:
      - portus:portus

  nginx:
    image: library/nginx:alpine
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./secrets:/secrets:ro
      - static:/srv/Portus/public:ro
    ports:
      - 80:80
      - 443:443
    links:
      - registry:registry
      - portus:portus

volumes:
  static:
    driver: local

Configuration:

Evaluated configuration:
---
email:
  from: portus@example.com
  name: Portus
  reply_to: ''
  smtp:
    enabled: false
    address: smtp.example.com
    port: 587
    domain: example.com
    ssl_tls: ''
    enable_starttls_auto: false
    openssl_verify_mode: none
    ca_path: ''
    ca_file: ''
    user_name: ''
    password: "****"
    authentication: login
gravatar:
  enabled: true
delete:
  enabled: true
  contributors: false
  garbage_collector:
    enabled: false
    older_than: 30
    keep_latest: 5
    tag: ''
ldap:
  enabled: false
  hostname: ldap_hostname
  port: 389
  timeout: 5
  encryption:
    method: ''
    options:
      ca_file: ''
      ssl_version: TLSv1_2
  base: ''
  admin_base: ''
  group_base: ''
  filter: ''
  uid: uid
  authentication:
    enabled: false
    bind_dn: ''
    password: "****"
  group_sync:
    enabled: true
    default_role: viewer
  guess_email:
    enabled: false
    attr: ''
oauth:
  local_login:
    enabled: true
  google_oauth2:
    enabled: false
    id: ''
    secret: ''
    domain: ''
    options:
      hd: ''
  open_id:
    enabled: false
    identifier: ''
    domain: ''
  openid_connect:
    enabled: false
    issuer: ''
    identifier: ''
    secret: ''
  github:
    enabled: false
    client_id: ''
    client_secret: ''
    organization: ''
    team: ''
    domain: ''
  gitlab:
    enabled: false
    application_id: ''
    secret: ''
    group: ''
    domain: ''
    server: ''
  bitbucket:
    enabled: false
    key: ''
    secret: ''
    domain: ''
    options:
      team: ''
first_user_admin:
  enabled: true
signup:
  enabled: true
check_ssl_usage:
  enabled: true
registry:
  jwt_expiration_time:
    value: 15
  catalog_page:
    value: 100
  timeout:
    value: 2
  read_timeout:
    value: 120
machine_fqdn:
  value: registry.weview.fr
display_name:
  enabled: false
user_permission:
  change_visibility:
    enabled: true
  create_team:
    enabled: true
  manage_team:
    enabled: true
  create_namespace:
    enabled: true
  manage_namespace:
    enabled: true
  create_webhook:
    enabled: true
  manage_webhook:
    enabled: true
  push_images:
    policy: allow-teams
security:
  clair:
    server: ''
    health_port: 6061
    timeout: 900
  zypper:
    server: ''
  dummy:
    server: ''
anonymous_browsing:
  enabled: true
background:
  registry:
    enabled: true
  sync:
    enabled: true
    strategy: initial
pagination:
  per_page: 10
  before_after: 2

Portus version: 2.5.0-dev@a1b9f2ebfeb84680a9dcd5629195e4c52815735c
version: 0.1
storage:
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
http:
  addr: 0.0.0.0:5000
  debug:
    addr: 0.0.0.0:5001

Portus version: 2.5.0-dev@a1b9f2ebfeb84680a9dcd5629195e4c52815735c

Thanks for your help!

Lelenaic commented 5 years ago

I found the issue. With letsencrypt, I must use "cert.pem" instead of "fullchain.pem".