Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!


Need help with Docker Registry Authorization, nginx and tokens
New on LowEndTalk? Please Register and read our Community Rules.

All new Registrations are manually reviewed and approved, so a short delay after registration may occur before your account becomes active.

Need help with Docker Registry Authorization, nginx and tokens

Disclaimer:
I do not know GO (at all) javascript (only basics) and i do not wanna use htpasswd/apache utility but my own API i already have up and running (because it's 100x easier to add/change IP whitelist + more usernames/passwords + more features/expandability

I tried googling all my issues + asking on discords + made a thread on official docker forum + asked on github (5 year old unclosed issue)

Here is my issue:

i used this tutorial to create a private docker repository (with slight changes)

this is what i tried:

version: '3'

services:
  registry:
    restart: always
    image: registry:2
    ports:
    - "5000:5000"
    environment:
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
      REGISTRY_HTTP_ADDR: 127.0.0.1:5000
    volumes:
      - ./data:/data
    network_mode: host

basically im creating a normal registry but exposing it on localhost:5000

now to convert from localhost to SSL im using nginx

server {
    client_max_body_size 2000m;
    proxy_pass_request_headers      on;
    server_name dockerhub.thechemicalworkshop.com;
    auth_request /auth;

    location / {
    # Do not allow connections from docker 1.5 and earlier
    # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
    if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
      return 404;
    }

    proxy_pass                          http://127.0.0.1:5000;
    proxy_set_header  Host              $http_host;   # required for docker client's sake
    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_read_timeout                  900;
    proxy_pass_request_headers      on;
    }

        location = /auth {
                proxy_pass_request_headers      on;
                proxy_pass http://localhost:8080/api/authorize_docker;
                proxy_pass_header token;
                proxy_set_header Authorization $http_authorization;
                proxy_pass_header  Authorization;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header  X-Forwarded-Proto $scheme;
}

listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/dockerhub.thechemicalworkshop.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/dockerhub.thechemicalworkshop.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

the auth_request /auth; and /auth do work and call my API on http://localhost:8080/api/authorize_docker

the authorize_docker part is running this script

@app.route("/api/authorize_docker")
async def authorize_docker():
    data = await request.data
    data2 = await request.get_data()
    js = await request.json
    js2 = await request.get_json()
    form = await request.form
    files = await request.files
    values = await request.values
    # stream = await request.stream
    print(request.authorization)
    print(await request.get_data())
    print(request.headers)
    if matti_check(request.headers["X-Real-Ip"]):
        return "Welcome_Matti"
    else:
        print(request.headers)
        return "ERROR", 500

im basically printing a bunch of stuff (for testing) and returning 200 if the request comes from my IP (it does work !)

however when i inspect the data on my API or wireshark there is no user/password being passed anywhere (??)

i basically run this to log in

Powershell Windows 10
docker login https://dockerhub.thechemicalworkshop.com/
Username: user
Password:
Login Succeeded

the python logs give me this:

4|TCW-API  | None
4|TCW-API  | b''
4|TCW-API  | Remote-Addr: 127.0.0.1
4|TCW-API  | X-Real-Ip: REDACTED
4|TCW-API  | X-Forwarded-Proto: https
4|TCW-API  | Host: localhost:8080
4|TCW-API  | Connection: close
4|TCW-API  | User-Agent: docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.14 \(windows\))
4|TCW-API  | Accept-Encoding: gzip
4|TCW-API  |
4|TCW-API  | [2022-04-29 22:28:14,748] 127.0.0.1:39722 GET /api/authorize_docker 1.0 200 13 32601
4|TCW-API  | None
4|TCW-API  | b''
4|TCW-API  | Remote-Addr: 127.0.0.1
4|TCW-API  | X-Real-Ip: REDACTED
4|TCW-API  | X-Forwarded-Proto: https
4|TCW-API  | Host: localhost:8080
4|TCW-API  | Connection: close
4|TCW-API  | User-Agent: docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.14 \(windows\))
4|TCW-API  | Accept-Encoding: gzip
4|TCW-API  |
4|TCW-API  | [2022-04-29 22:28:15,865] 127.0.0.1:39730 GET /api/authorize_docker 1.0 200 13 21546

(why are there 2 calls and not 1?)

anyways, there is NO user, password, authentication bearer token or anything (it should be login, password and hashed bearer token)

so i looked for other ways to authorize


i found this project Authorization for Private Docker Registry | by Thilina Manamgoda | Medium

and adjusted my configs:

docker

version: '3'

services:
  registry:
    restart: always
    image: registry:2
    ports:
    - "5000:5000"
    environment:
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
      REGISTRY_HTTP_ADDR: 127.0.0.1:5000
      REGISTRY_AUTH: token
      REGISTRY_AUTH_TOKEN_REALM: http://api.thechemicalworkshop.com/api/authorize_docker
      REGISTRY_AUTH_TOKEN_ISSUER: "The Chemical Workshop"
      REGISTRY_AUTH_TOKEN_SERVICE: "dockerhub.thechemicalworkshop.com"
      REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE: /ssl/fullchain.pem
    volumes:
      - ./data:/data
      - ./ssl:/ssl
    network_mode: host

and it does work ! python receives user and password + bearer hashed
the problem is docker now wants a authorization token, so to generate/return it i'm using this:

python API

raw_token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1ZNFQ6S05SVTpHSlNXOkVOUlU6R0ZSVzpDWVpXOkdOUlc6Q01MQzpHVlRHOktNM0Q6TVJTVzpDWlREIn0.eyJpc3MiOiJUaGUgQ2hlbWljYWwgV29ya3Nob3AiLCJhdWQiOiJkb2NrZXJodWIudGhlY2hlbWljYWx3b3Jrc2hvcC5jb20iLCJleHAiOjE2ODc5MjczMjAwMDB9.wHUtXjyROxSJ4eXlT4VpCaMFczCYnmwqHXpc_G9aAJzTCfhaXTP7ORkWGGWgilDhF4yFOZHSCEhPxWvG7cosCEqeQA1VTX3qVJGEXITvjqkV_QO-oYWlg6VeWib5ex7C8GVCpquOVlnD_bqJ-H_mxFBmoSEtdkBV2_AcXaZYopHVXsGnuWurAPzeNOs0cc2k2Mt9LaveBrQVrrKRSma-5mtoyjfu_HaSqYz6Zv8G1zxN7PZ064nq1XKp1LYt6dF4ShcxQRVMx69H6iefc0-h4ZSRIu_sWSChLr96tYJIestfwHsi_83A-YUcWhYQGQQAfRHuchTJ7BoybmzRzhJhHg'

    if matti_check(request.headers["X-Real-Ip"]):
        payload = {"expires_in": 360000,"issued_at": "2022-04-30T15:33:00Z"}

        payload['token'] = raw_token

        response = await make_response(payload)

        return response
    else:
        print(request.headers)
        return "ERROR", 500

the longass token gets created using this python script

import jwt

private_key = b'''-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOlBy6Ru+8d5mO
-----END PRIVATE KEY-----'''

public_key = b'''-----BEGIN CERTIFICATE-----
MIIDbTCCAlUCFEAIdgSu+9F+y39e4uqNZLBX02ThMA0GCSqGSIb3DQEBCwUAMHMx
-----END CERTIFICATE-----'''

encoded = jwt.encode(\
    {\
        "iss": "The Chemical Workshop", \
        "aud": "dockerhub.thechemicalworkshop.com",\
        "exp": 1687927320000\
        }, \
        private_key, algorithm="RS256", headers={"kid": "MY4T:KNRU:GJSW:ENRU:GFRW:CYZW:GNRW:CMLC:GVTG:KM3D:MRSW:CZTD"})
print(encoded)

I removed most of the key for space reasons removed most of the key for space reasons

anyways the KID key instructions on docker documentation are:

1 Take the DER encoded public key which the JWT token was signed against.

2 Create a SHA256 hash out of it and truncate to 240bits.

3 Split the result into 12 base32 encoded groups with : as delimiter.

so to get 1. i used openssl x509 -in domain.crt -noout -outform der -pubkey (i found it online)

which gives me following key

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzpQcukbvvHeZjpsRmR1T
u27d/Ep1DYvqLwfVsfoxXIU5lUrQ53J3K89Z/hrb8J0hF0ewaaqfzLE5khBY7E/v
10bqiAr9oNTu+3bKM+z9YRaJ4mh1atE3IFsW7uYBAJ8KYykxzUsJ8nJHyqFbJ0nH
pvdlRgoD/+CTn6uIGIrhMx8kgDbXGwX+t/OhsG87jVwfsr5Sa7lVilnMR6HJ/5F8
rAJ/O8sGfBGlbL62qUdbnLxrwxANiyA1p9F3UEoo2GhA9rkD0oileUVeYtVJ9zIu
NARfrProR7X6i8ZSCjF9FyhasyEl/rgNv69CPEFj1HCVYb/CHkCVj1L3Zi1v8K1K
PwIDAQAB
-----END PUBLIC KEY-----

now my question is, do i convert the WHOLE output (including -----BEGIN PUBLIC KEY-----) or just content?

by Create a SHA256 hash out of it and truncate to 240bits i understand SHA256 and keep the first 30 characters ? (is this correct?)

basically i get

hash content
f95642eb641cac63ca1b5fe3cdeafcf9ecb576f7f60c321aa8583e1d86d4cb7f
or
hash everything
75e190d3c15134507fbb352fd4d87301248544a418caf09bd8176c401dc55071

converting first 30 characters into base32 and doing step 3
gives me:

MY4T:KNRU:GJSW:ENRU:GFRW:CYZW:GNRW:CMLC:GVTG:KM3D:MRSW:CZTD
or
G42W:KMJZ:GBSD:GYZR:GUYT:GNBV:GA3W:MYTC:GM2T:EZTE:GRSD:QNZT

the problem is, it does not work

docker-registry-registry-1 | time="2022-04-30T17:10:56.438672709Z" level=info msg="token signed by untrusted key with ID: "MY4T:KNRU:GJSW:ENRU:GFRW:CYZW:GNRW:CMLC:GVTG:KM3D:MRSW:CZTD""

I am super hard stuck and would appreciate any help/tips/lead

there is more info on the docker thread and i will be updating BOTH once i get some lead/updates

Comments

  • I got more lead but no idea what that means
    https://github.com/distribution/distribution/issues/2223#issuecomment-1115381920

    anyone clues?

  • I made a fiverr request and said im giving 5$ bounty whoever can fix this bug
    bunch of morons applied that had no idea what docker is, anyone willing to fix it/help fix it?

  • Honestly you need to narrow down the issue before someone can help you out.

    Also, looking for someone to do what is essentially devops/SRE work for "5$" is a bit of a joke, and I'm not at all surprised at the outcome.

    Thanked by 2devp yoursunny
  • @stevewatson301 said:
    Honestly you need to narrow down the issue before someone can help you out.

    Also, looking for someone to do what is essentially devops/SRE work for "5$" is a bit of a joke, and I'm not at all surprised at the outcome.

    well, im pretty sure there are people that can read thru the source go code and find a working token in minutes

    The 5$ is well, all i can afford at the moment lol but yeah it's not much

    as for narrowing down, am am unsure, whatever i try it comes down to same error message every time (tried different tokens, generating them differently, etc) i can't even find lead how to debug this further

    a step could be installing go and trying other project but that would lead to no-where as i dont understand go code so would be back here asking what does this code mean/do / how is the token generated

Sign In or Register to comment.