Migrating from OAUTH to HTTP client cert authentication

61 views
Skip to first unread message

Stefan Ott

unread,
May 11, 2023, 10:30:21 AM5/11/23
to Repo and Gerrit Discussion
Hi there.

I recently had the urge to migrate our Gerrit installation away from using github OAUTH and chose to use HTTP client certificates instead. Since this required a bit more thinking (and bugging people on IRC) than I'd expected, I would like to share my experience with the community; maybe somebody finds this useful.

Feel free to skip to the bottom if you're the impatient type.

Initial state of things

As mentioned, we had been using OAUTH with github for a while, our users' primary identity was github-oauth:<id> (with id being a number). The Gerrit installation was already running behind an nginx reverse-proxy that was dealing with HTTPS termination.

Client certificates

We started by setting up a custom CA and issuing a bunch of client certificates. I won't go into too much detail, this is pretty straight-forward. To allow Gerrit to match the certificate to a user, we put the Gerrit user ID in the certificates' Common Name (CN) field. This was wrong, but quite useful for debugging later on.

Nginx and Gerrit

In order to keep things simple, we wanted nginx to validate the client certificates, extract the user ID from the CN field and pass it to Gerrit. So we enabled certificate authentication:

    ssl_client_certificate /etc/ssl/certs/http-client-cacert.pem;
    ssl_verify_client on;

Then, we added a custom header field to pass the CN to Gerrit:

    location / {
        ...
        proxy_set_header CertCN $ssl_client_s_dn_cn;
    }

Which needs this little helper:

    map $ssl_client_s_dn $ssl_client_s_dn_cn {
        default "";
        ~,CN=(?<CN>[^,]+) $CN;
    }

And finally we changed Gerrit's configuration:

    [auth]
        type = HTTP
        httpHeader = CertCN


So far, so simple. Unfortunately it didn't quite work.

Matching IDs

To be more precise, it did actually work. I could connect to Gerrit, nginx would ask me for the certificate, but when I logged in I was not using my own account, instead I was using a fresh account with my Gerrit user ID (1000000) as the username.

So, apparently Gerrit wanted to use the value from the CN fields as the user name, not the user ID. No problem, I made myself a new certificate with myname in the CN field. This made things a bit worse: When trying to log in, I got this type of error in the logs instead:

    com.google.gerrit.server.account.AccountException: Cannot assign external ID "username:myname" to account 1000030; external ID already in use.

Clearly, Gerrit was reading the information from the CN field just fine, but instead of using it to authenticate my user ("myname"), it kept creating new users and trying to associate my existing username with them. This is where I ultimately jumped on IRC to get some help.

As it turns out, (I'm sure most of you already know this) Gerrit uses a special git repo to manage its users. Since it had already created a new user with the name 1000000 during my previous login, at this point it made sense to get a copy of that repo and have a look at what exactly Gerrit was doing / trying to do:

    git fetch origin refs/meta/external-ids:external-ids

Which is what the docs and various people on the Internet told me to do. Unfortunately I got an error message, saying that there was no such thing as refs/meta/external-ids on the remote repo. As it turns out, that message was a bit misleading -- I just didn't have the right permissions. Granting myself "Access Database" permissions fixed that (of course I had to undo the configuration changes first to log in).

Now I was able to fetch external-ids and look into the issue. What I saw was that the most recent commit said:

    Create Account on First Login

Which happened right around the time when I was first trying to use certificate authentication. Bingo. Looking at the commit with git show, it created two new files. The first one saying:

    [externalId "gerrit:1000000"]
        accountId = 1000029


And the second one:

    [externalId "username:1000000"]
         accountId = 1000029


Which explained the error message I was getting: Gerrit uses these files to map identities (such as external IDs, but also Gerrit usernames) to account IDs. Creating a new account means that two files are added to the repo -- one for the external ID (in this case "gerrit:1000000", where 1000000 was the value from the CN field) and one for the username.

As I was trying to log in with my new certificate, using "myname" as its CN, Gerrit tried to find a mapping for "gerrit:myname", found none and proceeded to create new user. Then, as it tried assign the username "myname" to that new account, it noticed that there was already such a user and gave up.

Since it was now quite clear how Gerrit matches the HTTP authenticated user names to its accountId, we were able to add such as mapping ourselves:

    [externalId "gerrit:myname"]
           accountId = 1000000

The file name, as described in the docs, is simply a hash of the external ID:

    $ echo -n "gerrit:myname" | sha1sum
    d919131386679458b4d7a5f47dc23164616664ac  -

So we put the mapping in a file called d919131386679458b4d7a5f47dc23164616664ac, added it to the repo, committed and pushed the change:

    $ git push origin HEAD:refs/meta/external-ids

This last step needs Push access on refs/meta/external-ids.

And that's it. We can now use our little CA to create client certificates to authenticate Gerrit users. Wonderful.

Thank you for listening and thanks to Paul Fertser on IRC for pointing me in the right direction.

TL;DR
  • Configure Nginx to use client certificate authentication and to forward the CN to Gerrit
  • Configure Gerrit to use HTTP authentication
  • Create client certificate with your Gerrit username in the CN field
  • Add a mapping for gerrit:yourname to All-Users repo
  • Profit

Luca Milanesio

unread,
May 11, 2023, 4:21:46 PM5/11/23
to Repo and Gerrit Discussion, Luca Milanesio, Stefan Ott
Hi Stefan,

On 6 May 2023, at 01:50, 'Stefan Ott' via Repo and Gerrit Discussion <repo-d...@googlegroups.com> wrote:

Hi there.

I recently had the urge to migrate our Gerrit installation away from using github OAUTH and chose to use HTTP client certificates instead. Since this required a bit more thinking (and bugging people on IRC) than I'd expected, I would like to share my experience with the community; maybe somebody finds this useful.

Thanks for sharing your experience: have you thought about contributing this to the Gerrit documentation?

Luca.

--
--
To unsubscribe, email repo-discuss...@googlegroups.com
More info at http://groups.google.com/group/repo-discuss?hl=en

---
You received this message because you are subscribed to the Google Groups "Repo and Gerrit Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to repo-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/repo-discuss/b35bdef3-227b-4c49-ba4b-992dd5675504n%40googlegroups.com.

Dzintars Klavins

unread,
May 11, 2023, 4:44:17 PM5/11/23
to Luca Milanesio, Repo and Gerrit Discussion, Stefan Ott
I have no use case for this ATM, but I just wanted to say BIG BIG Thank You for taking Your time and sharing such gems! I wish more people would  do so. :)



--

WBR,

Dzintars Kļaviņš

CONFIDENTIALITY NOTICE: The contents of this email message and any attachments are intended solely for the addressee(s) and may contain confidential and/or privileged information and may be legally protected from disclosure. If you are not the intended recipient of this message or their agent, or if this message has been addressed to you in error, please immediately alert the sender by reply email and then delete this message and any attachments. If you are not the intended recipient, you are hereby notified that any use, dissemination, copying, or storage of this message or its attachments is strictly prohibited. 
Reply all
Reply to author
Forward
0 new messages