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.
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
solved it, there will be a writeup/link to writeup on https://forums.docker.com/t/need-help-with-nginx-as-authentication-for-private-docker-repository/124018