Open archont94 opened 1 month ago
The RFC specifies this endpoint as a POST https://datatracker.ietf.org/doc/html/rfc6749#section-6. Seems like distribution/distribution is not following the spec
Is there any chance that authentik will support this custom docker implementation (similar to how GitHub is supported: https://docs.goauthentik.io/docs/add-secure-apps/providers/oauth2/#github-compatibility)?
I have this simple python proxy to translate GET requests to POST ones and move params from url query to body, but it isn't great (some images aren't loading on authentik site etc). If this would be part of authentik (similar to github custom compability layer), this issue would be solved. Its not even close to being safe, I just wanted quickly confirm that docker client can actually use authentik oauth. There should be a lot of extra safe checks etc.
import aiohttp
import base64
from aiohttp import web
import json
UPSTREAM_URL = "http://authentik-server:9000" # docker service name
async def handle(request):
request_method = request.method
request_url = f"{UPSTREAM_URL}{request.path}"
data = await request.read()
params = request.query
headers = request.headers
docker_token_query = False
if "/application/o/token" in request.path and request_method == "GET":
docker_token_query = True
request_url = "https://authentik.example.com/application/o/token/" # to avoid wrong `iss` value in token we can use external API address, as this will be POST anyway (so its not recurrence)
data = aiohttp.FormData()
data.add_field('grant_type', 'client_credentials') # value expected by authentik
client_id = params.get('service') # query param 'client_id' is hardcoded to 'docker' by client, but we can set 'service' field on docker registry config to any value
data.add_field('client_id', client_id)
auth_header = headers.get('Authorization')
if auth_header is None:
raise web.HTTPUnauthorized()
base64_credentials = auth_header.split(' ')[1] # TODO - its unsafe, there should be checks if this is Basic auth etc
decoded_credentials = base64.b64decode(base64_credentials).decode('utf-8')
username, password = decoded_credentials.split(':', 1)
data.add_field('username', username)
data.add_field('password', password)
request_method = "POST"
params = None
headers = None
async with aiohttp.ClientSession() as session:
async with session.request(request_method, request_url,
data=data,
params=params,
headers=headers,
max_redirects=50) as resp:
resp_body = await resp.read()
if docker_token_query:
if resp.status == 200:
resp_body = json.loads(resp_body.decode('utf-8'))
resp_body['token'] = resp_body['access_token'] # compability with older docker clients
resp_body = json.dumps(resp_body).encode('utf-8')
print(f"response status {resp.status}, body {resp_body}")
return web.Response(status=resp.status, body=resp_body, content_type=resp.content_type)
async def main():
app = web.Application()
app.router.add_route('*', '/{tail:.*}', handle)
return app
if __name__ == '__main__':
web.run_app(main(), port=8080)
Example config.yml
for docker registry:
version: 0.1
log:
level: debug
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
auth:
token:
realm: https://authentik.example.com/application/o/token/
service: CLIENT_ID_FROM_AUTHENTIK
issuer: http://authentik.example.com/application/o/SLUG_NAME/ # OpenID Configuration Issuer from Authentik
rootcertbundle: /certs/token.crt # optional, same as one used by authentik to sign oauth2
jwks: /certs/jwks.json # value obtained from https://authentik.example.com/application/o/docker-dev-registry/jwks/ saved as json on disk
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
Is your feature request related to a problem? Please describe. I want to use authentik as oauth2 for docker registry (distribution/distribution on GitHub). Unfortunately it uses GET requests for
/application/o/token
endpoint instead of POST (see https://github.com/distribution/distribution/blob/main/docs/content/spec/auth/token.md for details, TLDR:To respond to this challenge, the client will need to make a GET request to the URL
).Describe the solution you'd like It would be great if authentik could support both POST and GET requests on this endpoint.
Additional context I'm not sure why they decided to use GET and if there is any RFC which covers which version should be used, but I assume that adding support for both in authentik wouldn't be problematic.
This is the request docker client does to authentik, in order to obtain token:
"GET /application/o/token?account=USERNAME&client_id=docker&offline_token=true&service=SERVICE_VALUE_FROM_REGISTRY_CONFIG HTTP/1.1" 400 140 "-" "docker/24.0.7 go/go1.20.10 git-commit/311b9ff kernel/5.15.153.1-microsoft-standard-WSL2 os/linux arch/amd64 UpstreamClient(Docker-Client/24.0.7 \x5C(linux\x5C))"
. Client ID seems to be hardcoded, but it can be set to any value in authentik so that should work.SERVICE_VALUE_FROM_REGISTRY_CONFIG
is value fromconfig.yml
registry file, sectionauth.token.service
.