Web GUI Smart Card Authentication Howto
EDIT 1: I suspect many of you may own Yubikeys. As you probably are aware, Yubikeys >=4 can behave as a PIV smart card and will work work with the below configuration in place of a plastic smart card or other token. The only downside is that the pin entry will take place on the computer than 'out of band' on a smart card reader.
EDIT 2: I incorrectly asserted below that NIST curve P384 (secp384r1) is the strongest crypto key/key exchange (private key & ephemeral)- actually Firefox 77 supports P521- the strongest NIST curve and equivalent to a 256bit symmetric key).
I did a bit of hacking away at my pfSense box this week and managed to get smart card authentication working for the webGUI. I searched the forums and found no other posts setting out how to do this- apologies if this has already been covered elsewhere. I have a very verbose writing style, so the post is probably a lot longer than it ought to be but I prefer to break everything down as much as possible.
Firstly- a bit of a mild warning- this makes use of editing PFsense config files via SSH or direct console access. Furthermore, it does not survive (yet) reboots. That being said, the step to get it working again after a reboot is extremely simple, albeit where I store the certificate to save it between reboots is not aesthetically pleasing.
Before I get into the detail, the smart card does not replace the username/password authentication or interact with user accounts at all, it simply blocks any interaction with pfSense's webGUI (including login) unless a valid smartcard (or X509 certificate) is present- it is an extremely strong second authentication factor. Lastly, I have a strong preference for EC crypto, but there is nothing stopping you from using RSA keys in the below process as long as your key size is between 2048-4096bits. Beware- most smart cards (other than the one I use) only support RSA up to 2048bits
What you need:
- Either a smart card with both PKCS11 middleware, the ability to import signed X509 certificates (FYI, I used the excellent smartcard-hsm card- it really is the highest spec smart card I've come across and has great cross-platform support- buy it from cardcontact.de- I have no financial or other connection to them)
- If your smart card is not in usb token form, then you will also need a smart card reader (I used an Identiv SPR332) - a card with a pinpad reader is the most secure configuration.
- If not a smart card, then simply an X509 certificate + private key imported into your browser (I didn't do this for my own setup)
- A root certificate that you either own/control (no reason why you couldn't do this with a cert signed by a commercial CA, but that is not what I used)
- Linux or FreeBSD to set up and initialize card (its possible that you can do it on Mac using Brew or other package managers- you will need to install OpenSC, PCSClite, pkcs11-tool, pkcs15-tool. It is also possible that you can do this on Windows using CYGWIN, but I did not explore that idea)
Step1: Initialize smart card and ensure you generate a new strong management and user pins- for smartcard-hsm- this is done using pkcs15-tool, is well documented online and requires one command
Step 2: Generate private key on your smart card using either pkcs11-tool or XCA (Good Linux PKI GUI). I chose the EC:secp384r1 curve as it simply offers the highest level of cryptographic security possible for current web browsers. Going for secp521r1 (i.e. P-521) will break on all current browsers as it is not part of NSA suite B and the respective browser vendors have decided to only implement suite B when it comes to NIST curves.
Step 3: Generate CSR using previously created private key
Step 4: Sign CSR and generate pem certificate using openssl or XCA and your trusted root ca key (for me, that ca key is stored on a separate smart card)
Step 5: Import pem certificate into card using pkcs11-tool or XCA
Step 6: ssh into pfSense box and edit /etc/inc/system.inc. This is one of several major config files/scripts that go on to generate the working system during boot. Search fo something pertaining to nginx - for example "\t\tssl_session_timeout", excluding quotes- this will take you to the section of the configuration file that you will need to edit- the section that has all of the nginx stuff
Step 7 (optional): Improve the nginx crypto settings - specifically to include $nginx_config .= "\t\tssl_ecdh_curve secp384r1;\n" - I know there is suspicion around the NIST curves, but if the NSA wants to root my router, I'm not going to be able to stop them. Apart from an NSA backdoor, secp384r1 is the strongest asymmetric crypto that is supported by Chrome(ium)/Firefox/Edge and is the weakest link for many people (they set their cipher to aes256 but leave the ecdh curve at p256 , which only offers 128bits of security- RSA is even worse with near universal use of RSA 2048 (c.112bits symmetric crypto equivalence) somewhere in the CA chain, assuming you don't use your own PKI- for aes256, you should theoretically be using a c.15384bit RSA key.
Step 8: In the line talking about ssl_protocols- change the one in the final else statement to just TLSv1.2 - as of pfSense 2.4.5, tls 1.3 is not supported or at least breaks if you change the setting here.
Step 9: Either scp or paste contents of a pem encoded certificate into a file called myrootca.crt into /var/etc/ in the pfSense folder structure
Step 10: Add a config line to /etc/inc/system.inc: $nginx_config .= "\t\tssl_client_certificate /var/etc/myrootca.crt;\n";
Step 11: Add another config line to /etc/inc/system.inc: $nginx_config .= "\t\tssl_verify_client on;\n"; - this setting enforces client tls authentication and will prohibit access to the webconfigurator unless the client browser presents a certificate signed by myrootca
Step 12: Restart webconfigurator with /etc/rc.restart_webgui or via option 11 on the initial pfSense shell welcome screen.
On reboot /var/etc/ is wiped and rebuilt, so I ended up storing my root ca crt file in /etc/inc (as this folder persisted across reboots). I then copy the crt file to /var/etc/ using ssh after a reboot to get the smart card functionality up and running again.
I will not go into the details of how to set up your web browser to work with smart cards via pkcs11, suffice to say I think Firefox has a far better implementation than Chrome(ium)- probably because that's what US DoD encourages its remote workers to use and they must have "provided support" to Mozilla to have a solid, working smart card functionality.
The above steps will lead to it being impossible to access the webconfigurator (regardless of firewall settings) unless a valid smart card is present and unlocked (with pin). You can, of course simply not have a smart card but apply the same approach as the above but simply replace all the steps involving smart cards to private/public key pairs stored on your machine and imported into the browser's certificate store.