Saturday, 9 May 2020

Getting with the Webauthn flow

Following on from my last post, we now have a Keycloak instance up, running in a Docker container. The persistence is provided by a very basic H2 database inside the container but it works just fine. 

My objective now is to configure Keycloak to test a 2-factor authentication scenario with a Yubikey. 
The token I've got is a Yubikey 4 , previously known as a FIDO U2F token, and since the publication of the FIDO2 specification is now known as Webauthn CTAP1 (Client to Authenticator Protocol 1) Authenticator.
This is an older token but it will do the job. Part of my objective with this mini-project to is to determine if it's worth spending money on a stack of FIDO2 hardware tokens. 

So let's go through the steps. My sources for this are the Keycloak documentation and this video for an older version of Keycloak by Stian Thorgersen the project lead. Blink and you'll miss it.

You'll find my own video of the process at the end.

1) Create a realm

Keycloak is deceptively simple when you first discover it. And then you start delving into the options and quickly realise you have no idea what half the stuff does.

2) Copy browser authentication flow
We then go to the Authentication menu when the authentication Flows are selected by default. 
Make sure the "Browser" flow is selected, and click the copy button. Give it a name.


3) Authentication flows are the control logic for determining how any particular authentication context is treated. We need to leave Cookie and Identity Provider Redirector alone because they will allow us to authenticate a user who has already authenticated with some other method. 

Out of the box the Browser flow is configured with conditional 2FA if the user has already configured a TOTP in their account. For our demo purposes we're going to disable that and force them to register a token. 

  • Choose the action to delete "Webauthn Browser - Conditional OTP"
  • "Add execution" on the line "Browser Webauthn Forms"
  • Choose "WebAuthn Authenticator" and save
  • Change the Requirement on the new WebAuthn Authenticator line to "Required" 


4) Next we need to make users register their shiny token so they can log in. 
  • Select the "Required Actions" menu
  • Click "Register" and then add the "Webauthn Register" option.
  • In this case I am a FIDO2 CATP1 extremist and will not tolerate any other kind of 2FA so I disable the "Configure OTP" option.
  • Move the Webauthn Register option up to the top and select as the "Default Action"

5) Next I need to configure the WebAuthn Policy. Lucky me.
  • Go to the Webauthn Policy menu. There are a few critical options here I need to configure.
  • I specify the hostname I put in my container's TLS certificate in the "Relying Party ID" option. I also added this hostname to my hosts file (last blog post updated...)
  • "Attestation Conveyance Preference" changes the behaviour of the webauthn client during registration and how Keycloak obtains the public key used for verifying authentication assertions, and essentially determines how the "Relying Party", that's Keycloak" can trust the crypto being used. I'll set this to "direct" which increases the cost/time of the registration in exchange for a stricter set of specs. I'm going to investigate Attestation Statements at a later date.
  • Some functionality is only available with FIDO2 specification authenticators, for example it's possible to specify AAGUIDs and so limit what kind of tokens users are allowed to register. I'm using an older token so I'll leave the other settings on default.
  • Save


6) Now we have Webauthn configured we need to bind our authentication flow to Browser logins.
  • Go to the "Bindings" menu
  • Change the Browser Flow binding to "Browser webauthn" or whatever you specified in step 2
  • Save

7) In this demo I'm going to allow users to register to the realm and create an account. These could be existing accounts mapped to LDAP, AD or just created locally within the Keycloak realm.
  • Go to "Realm Settings" and select the "Login" menu
  • Turn User Registration on and save

8) Now we're going to try and use it. 
  • Go to the "Clients" menu and copy the Base URL for the "account" client ID.
  • The aim is to open this link in an unauthenticated context, so you can either open it in a private browsing window or use another browser or just logout as admin.
  • I register a new user bar@foo, username barf
    • Upon clicking register, my browser prompts me to provide a FIDO authenticator

       
    • Then it asks me to touch the authenticator to confirm I want to register it

       
    • Then the browser webauthn client asks to directly access the authenticator. This is to generate the Attestation Statement.

       
    • Give it a friendly name for Keycloak to register it under

  • It's done!
  • Logging out and then back into the realm account setting page with the account we just created should request another touch on the authenticator after the Keycloak username/password.
  • Please feel free to message me with any corrections or improvements. I'm still just figuring all this stuff out. 





Friday, 8 May 2020

Testing Keycloak 10 in a docker container

I first came across Keycloak around 2014 when it was still in version 0.9. A colleague introduced me and I've been a fan ever since. It's always had the backing of Red Hat but since about 2016 it is also the upstream project for their Red Hat Single Sign On product. 


The early logo was fun. Then it grew up and went all corporate. 

Keycloak is a versatile open source identity and access management project with an API-first approach which makes it highly suited to modern application environments. I used it to build the identity management back-end for an entreprise cloud-service catalogue back in 2016 and it did everything we asked of it... more or less. 

If I remember it did get a bit messy when we were trying to do Kerberos authentication from two different Active Directory domains within the same realm, although the account federation part worked fine the SSO only worked on one domain due to field mapping limitations. I also cobbled together a Keycloak module for bit.ly's OAuth reverse proxy but I was too ashamed to tweet about it as I didn't have time to write the test module in Go.

Anyway, this post is about getting Keycloak 10 up and running in a docker container so we can mess around with the latest features. So let's get into it. I'm using a Mac for this but well obviously, it should be the same on any other OS - that's kind of the point of Docker. 

As I write this a new version of Keycloak (10.1) has been released so that's what I'll be using.

First of all get the latest container image with 


~ % docker pull jboss/keycloak

To get Keycloak up and running with the configuration I want I need to assemble a fairly long docker run command. 

The options I use are : 
  • -p 8080:8080 for the http port forwarding
  • -p 8443:8443 for https
  • -e KEYCLOAK_USER=<adminuser> -e KEYCLOAK_PASSWORD=<password>
    to create the initial user. This can be done in other ways as described on the Keycloak dockerhub readme and of course you can be more secure than sticking your password on the command line. This is just for fun. Also, don't quote your user and password or things get weird.
  • --mount type=bind,source=./<tls.key>,target=/etc/x509/https/tls.key --mount type=bind,source=./<tls.crt>,target=/etc/x509/https/tls.crt
    I'm going to be accessing this Keycloak instance from other systems and vms, so I need a hostname other than localhost, and preferably a TLS certificate to match. At this point I add the CN of the certificate to my hosts file so that we can access a URL with a matching certificate.
    The bind mount replaces a file in the container image with a file on your host OS. Super useful for fiddling with configuration files in the image, but in this case the easiest way to push the tls key and cert into the container. Generate your key pair with whatever tool or quantum-resistant black magic you prefer. 
  • -e KEYCLOAK_LOGLEVEL='DEBUG'Finally, debug logs. If we're going to be messing about with authentication protocols then this is a must.


The final command looks something like this  : 
~ % docker run \
-e KEYCLOAK_USER=<adminuser> \
-e KEYCLOAK_PASSWORD=<password> \
-e KEYCLOAK_LOGLEVEL='DEBUG' \
-p 8080:8080 -p 8443:8443 \
--mount type=bind,source="/<path>/<tls.key>",target=/etc/x509/https/tls.key \
--mount type=bind,source="/<path>/<tls.crt>",target=/etc/x509/https/tls.crt \
jboss/keycloak


When Keycloak has finished starting then you should see this in your console : 
21:03:36,552 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
21:03:36,552 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
21:03:36,552 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Keycloak 10.0.1 (WildFly Core 11.1.1.Final) started in 33748ms - Started 692 of 997 services (708 services are lazy, passive or on-demand)


Keycloak is up. TLS error because my cert isn't signed. I imported it into the OS trusted certs list to make Chrome stop complaining. 

Log in with the username and password defined above. 

And we're in. Next post, creating a client and Webauthn authentication flow.


The Precious


In September 2018 I started a new job. Of all the things that attracted my attention when I explored the cupboards in my new office, one item stood out in particular - or rather two things because the carefully curated wine selection also grabbed my attention -  but the blister pack of unused Yubikeys glinted gold and looked shiny and precious.

I could do something with those I thought; maybe in a week or two when I get settled in. Turns out I had a lot to learn. Things got intense.

I became the head of the team and was learning new things every day for over a year. I came home from work exhausted from managing human beings. I went to bed early and slept like a baby every night. When I very occasionally dreamt, I dreamt of doing something technical; something involving Yubikeys.

Months past.

When the COVID-19 crisis was kicking off, the government announced on a Monday evening that movement restrictions would apply from lunch-time the following day. As I was going to be working from home for months to come I made up a list of things I needed to get from the office - and I did honestly need them (comfortable headset, USB smartcard reader, 4G wifi router for backup connectivity, ...). But when I dashed into the office on that Tuesday morning there was only one critical item on my list - the Precious.




It's been 19 months and the shiny objects have finally had their way. I'm going to publish a series of posts over the coming days about things I've been doing with the Yubikeys. It's not going to be deep crypto and development level, but more how to get up and running with open-source off the shelf tools. It's my token offering to the cybers.