SUSE / Portus

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

Portus LDAP auth does not work #2262

Closed djechelon closed 4 years ago

djechelon commented 4 years ago

Description

I am configuring Portus to use LDAP authentication to our organization's AD server. Every time I try to authenticate for the first time I get an error "Operations error (code 1)" without furhter information.

Portus is deployed as a Docker stack comprehensive with portus, nginx, registry, postgres. LDAP is provided by an external source dc01.acme.com

Steps to reproduce

  1. Start Portus clean (make sure permanent volumes are clear)
  2. Attempt log in using first.lastname user and password
  3. See error popping up

Logs follow (I have redacted few information about my name and company)

portus_1    | [41] * Listening on ssl://0.0.0.0:3000?cert=/certificates/acme.com.crt&key=/certificates/acme.com.key&verify_mode=none
portus_1    | [41] Use Ctrl-C to stop
portus_1    | [41] - Worker 0 (pid: 44) booted, phase: 0
portus_1    | [41] - Worker 1 (pid: 48) booted, phase: 0
portus_1    | [41] - Worker 2 (pid: 53) booted, phase: 0
portus_1    | [41] - Worker 3 (pid: 68) booted, phase: 0
portus_1    | Started GET "/users/sign_in" for 172.18.0.5 at 2019-11-29 15:22:44 +0000
portus_1    | Processing by Auth::SessionsController#new as HTML
portus_1    |    (1.1ms)  SELECT COUNT(*) FROM "users" WHERE ("users"."username" != $1)  [["username", "portus"]]
portus_1    |   Rendered shared/_notification.html.slim (13.1ms)
portus_1    |   Rendered shared/_notifications.html.slim (25.0ms)
portus_1    |   User Load (0.5ms)  SELECT "users".* FROM "users" WHERE ("users"."username" != $1)  [["username", "portus"]]
portus_1    |   Rendered devise/sessions/new.html.slim within layouts/authentication (115.2ms)
portus_1    | Completed 200 OK in 235ms (Views: 189.4ms | ActiveRecord: 2.1ms)
nginx_1     | 192.168.0.139 - - [29/Nov/2019:15:22:46 +0000] "GET /users/sign_in HTTP/2.0" 200 1603 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0"
portus_1    | Started POST "/users/sign_in" for 172.18.0.5 at 2019-11-29 15:22:52 +0000
portus_1    | Processing by Auth::SessionsController#create as HTML
portus_1    |   Parameters: {"utf8"=>"✓", "authenticity_token"=>"Q2uFnPDp206NlfOOUFz+PhfbLBBzXxzT4vhDM60HGVqK/hLq97wVO+QOVGkPsTqS7CkHJETBcE2gW/fv491Jwg==", "user"=>{"username"=>"user.name", "password"=>"[FILTERED]"}, "button"=>""}
portus_1    |   User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE "users"."username" = $1 LIMIT 1  [["username", "user.name"]]
portus_1    | [ldap] Operations Error (code 1).
portus_1    | Completed 401 Unauthorized in 20ms (ActiveRecord: 0.7ms)
portus_1    | Processing by Auth::SessionsController#new as HTML
portus_1    |   Parameters: {"utf8"=>"✓", "authenticity_token"=>"Q2uFnPDp206NlfOOUFz+PhfbLBBzXxzT4vhDM60HGVqK/hLq97wVO+QOVGkPsTqS7CkHJETBcE2gW/fv491Jwg==", "user"=>{"username"=>"user.name", "password"=>"[FILTERED]"}, "button"=>""}
portus_1    |    (0.8ms)  SELECT COUNT(*) FROM "users" WHERE ("users"."username" != $1)  [["username", "portus"]]
portus_1    |   Rendered shared/_notification.html.slim (0.1ms)
portus_1    |   Rendered shared/_notification.html.slim (0.0ms)
portus_1    |   Rendered shared/_notifications.html.slim (1.5ms)
portus_1    |   User Load (0.4ms)  SELECT "users".* FROM "users" WHERE ("users"."username" != $1)  [["username", "portus"]]
portus_1    |   Rendered devise/sessions/new.html.slim within layouts/authentication (15.0ms)
portus_1    | Completed 200 OK in 171ms (Views: 16.0ms | ActiveRecord: 1.2ms)
nginx_1     | 192.168.0.139 - - [29/Nov/2019:15:22:53 +0000] "POST /users/sign_in HTTP/2.0" 200 4593 "https://docker01.acme.com/users/sign_in" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0"

Deployment information

I have amended the docker-compose.yml in the example repository. The final file looks as follows

version: "3.7"

services:
  portus:
    restart: always
    image: opensuse/portus:2.4
    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_ADAPTER=postgresql
      - PORTUS_DB_USERNAME=postgres
      - 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/acme.com.key
      - PORTUS_PASSWORD=${PORTUS_PASSWORD}
      - PORTUS_LOG_LEVEL=debug
      # SSL
      - PORTUS_PUMA_TLS_KEY=/certificates/acme.com.key
      - PORTUS_PUMA_TLS_CERT=/certificates/acme.com.crt

      # NGinx is serving the assets instead of Puma. If you want to change this,
      # uncomment this line.
      #- RAILS_SERVE_STATIC_FILES='true'
      - PORTUS_PUMA_HOST=0.0.0.0:3000
    ports:
      - 3000:3000
    links:
      - db
    volumes:
      - ./portus/config.yml:/srv/Portus/config/config-local.yml:ro
      - ${CERTIFICATES_DIR}:/certificates:ro
      - static:/srv/Portus/public

  db:
    restart: always
    image: library/postgres:10-alpine
    environment:
      POSTGRES_PASSWORD: portus
    volumes:
      - /var/lib/portus/db:/var/lib/postgresql/data

  registry:
    restart: always
    image: library/registry:2.7.1
    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: /certificates/acme.com.crt

      # SSL
      REGISTRY_HTTP_TLS_CERTIFICATE: /certificates/acme.com.crt
      REGISTRY_HTTP_TLS_KEY: /certificates/acme.com.key

      # Portus endpoint
      REGISTRY_NOTIFICATIONS_ENDPOINTS: >
        - name: portus
          url: https://${MACHINE_FQDN}/v2/webhooks/events
          timeout: 2000ms
          threshold: 5
          backoff: 1s
    volumes:
      - /var/lib/portus/registry:/var/lib/registry
      - ${CERTIFICATES_DIR}:/certificates: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:
    restart: always
    image: library/nginx:alpine
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ${CERTIFICATES_DIR}:/certificates:ro
      - static:/srv/Portus/public:ro
    ports:
      - 80:80
      - 443:443
    links:
      - registry:registry
      - portus:portus

volumes:
  static:
    driver: local

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
    tag: ''
ldap:
  enabled: true
  hostname: dc01.acme.com
  port: 389
  timeout: 5
  encryption:
    method: ''
    options:
      ca_file: ''
      ssl_version: TLSv1_2
  base: DC=acme,DC=com
  filter: ''
  uid: sAMAccountName
  authentication:
    enabled: true
    bind_dn: CN=Docker,OU=Services,OU=acme,DC=acme,DC=com
    password: "****"
  guess_email:
    enabled: true
    attr: mail
oauth:
  local_login:
    enabled: true
  google_oauth2:
    enabled: false
    id: ''
    secret: ''
    domain: ''
    options:
      hd: ''
  open_id:
    enabled: false
    identifier: ''
    domain: ''
  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: false
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: docker01.acme.com
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:
  limit: 10
  per_page: 10
  before_after: 2

Portus version: 2.4.3@5a616c0ef860567df5700708256f42505cdb9952
djechelon commented 4 years ago

Related #798 but was closed long ago apparently providing no solution

djechelon commented 4 years ago

API health returns...

{"database":{"msg":"database is up-to-date","success":true},"ldap":{"msg":"LDAP server is reachable","success":true},"registry":{"msg":"no registry configured","success":false}}

ArcticSnowman commented 4 years ago

Is this LDAP or AD (active directoy)?

I suspect your ldap.base: is wrong. Typically it should point to an OU object. For example: OU=Users,OU=acme,DC=acme,DC=com

stale[bot] commented 4 years ago

Thanks for all your contributions! This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.