Skip to content
Advertisement

Python Uvicorn – obtain SSL certificate information

I have a gunicorn + uvicorn + fastApi stack. (Basically, I am using https://hub.docker.com/r/tiangolo/uvicorn-gunicorn-fastapi docker image).

I’ve already implemented SSL based authentication by providing appropriate gunicorn configuration options: certfile, keyfile, ca_certs, cert_reqs. And it works fine: user have to provide a client SSL certificate in order to be able to make an API calls.

What I need to do now is to obtain client certificate data and pass it further (add it to request headers) into my application, since it contains some client credentials. For example, I’ve found a way to do it using gunicorn worker by overrding gunicorn.workers.sync.SyncWorker: https://gist.github.com/jmvrbanac/089540b255d6b40ca555c8e7ee484c13.

But is there a way to do the same thing using UvicornWorker? I’ve tried to look through the UvicornWorker‘s source code, but didn’t find a way to do it.

I went deeper into the Uvicorn source code, and as far as I understand, in order to access the client TLS certificate data, I need to do some tricks with python asyncio library (https://docs.python.org/3/library/asyncio-eventloop.html), possibly with Server (https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server) class and override some of the UvicornWorker‘s methods. I am still not quite sure if it is possible to achieve the desired result though.

Advertisement

Answer

I ended up setting the nginx (Openresty) in front of my server and added a script to get a client certificate and put it into header.

Here is a part of my nginx config:

set_by_lua_block $client_cert {
    local client_certificate = ngx.var.ssl_client_raw_cert
    if (client_certificate ~= nil) then
        client_certificate = string.gsub(client_certificate, "n", "")
        ngx.req.set_header("X-CLIENT-ID", client_certificate)
    end
    return client_certificate
}

It is also possible to extract some specific field from a client certificate (like CN, serial number etc.) directly inside nginx configuration, but I decided to pass the whole certificate further.

My problem is solved without using gunicorn as I originally wanted though, but this is the only good solution I’ve found so far.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement