After configuring HAProxy on this server I've been trying out a few things that have been in the back of my mind for some time. Here I document configuring a client certificate to mutually authenticate a web browser to a subdomain and limit access based on its presence.
This was motivated by the experience of trying to guard access to a web application with just basic authentication. Client TLS certificates are such an obvious improvement in the security of the system that I wanted to understand what downsides I might be misunderstanding. In order to test it out I decided to try exposing the Cockpit instance on this server.
The problem I've had with running Cockpit was the idea of relying on the current user-password system on the server in an internet-facing environment. One of the first thing I did when I configured this server was to disable password authentication over SSH and this is basically an identical level of access. Instead I wanted the same kinds of guarantees I get with an SSH key - so I'll use an HTTPS client certificate.
This was all nicely documented by HAProxy. There are a number of other scenarios for certificate generation in the Redhat manual; I'm capturing it here for my own notes.
I am just going to self-sign a certificate because I don't care
about the longevity of this experiment (also why days
is 30!):
openssl req -newkey rsa:2048 -nodes -x509 -days 30 -keyout privateCA.pem -out cacert.crt
Generate a certificate signing request for my browser (user):
openssl req -newkey rsa:2048 -nodes -subj "/CN=nolan/O=noprescoOrg" -keyout clientKey.pem -out client.csr
Create a certificate extension configuration file:
$ cat >certificate-extensions.conf <<EOF
basicConstraints = CA:FALSE
keyUsage = digitalSignature
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
EOF
And then generate the certificate:
openssl x509 -req -in client.csr -out client.crt -CA cacert.crt -CAkey privateCA.pem -CAcreateserial -days 30 -extfile certificate-extensions.conf
On the one hand, I remember being entirely nonplussed the first time I had to do the above sort of thing with openssl. On the other hand, I've done it enough times that I really appreciate that there is nothing very interesting about the process; apart from the certificate extension selection this is nearly indistinguishable from creating a self-signed certificate for a web server. This makes sense but I had expected more steps to create a client certificate.
The last piece necessary in my case was to export the certificate in a format that Firefox can use, which meant converting to pkcs12:
openssl pkcs12 -export -out cert.pkcs12 -inkey clientKey.pem -in client.crt
With that done it is possible to copy the client certificate to my laptop and add it to Firefox from the preferences settings menu using the "Certificates → View Certificates → Import" dialog.
One thing that I hadn't really thought about was that TLS
negotiation happens before application protocol layer handling such
as HTTP so there's no way to configure a route that
requires a client side TLS certificate. There is the possibility of
making it optional, which I chose to do but I was reluctant to try
it on the top-level domain in my case. Instead I carved out a
temporary subdomain and accepted that I will receive warnings in the
browser because it isn't listed in the certificate's Subject
Alternative Names. My self-signed CA certificate is located
at /etc/ssl/auth-test/cacert.crt
so the configuration
necessary to turn on certificate auth
for cockpit.nprescott.com
is then:
frontend https-in
bind :::443 ssl crt /etc/ssl/nprescott.com/nprescott.com.pem alpn h2,http/1.1 verify optional ca-file /etc/ssl/auth-test/cacert.crt
acl is_cockpit hdr_dom(host) -i cockpit.nprescott.com
http-request deny if is_cockpit !{ ssl_c_used }
use_backend be_cockpit if is_cockpit
default_backend lighttpd-server
backend be_cockpit
server c1 localhost:9090
Cockpit is already running on the server
at localhost:9090
(and in reality it is systemd socket
activated so it isn't really running, more like waiting to run). A
quick reload of HAProxy and access attempts to the subdomain trigger
my browser to pop-up a dialog telling me the domain has requested I
identify myself with a certificate along with a selection of those
certificates matching this domain. It is nice that there is an
option to "Remember this decision" but I will say the browser
interface leaves something to be desired in terms of user
experience.
It seems like there is some work being done in the browser-space to improve the usability of client certificates. My impression is that this is probably most useful for things like hardware security tokens, which I haven't got to make an assessment of. The current "scary looking dialogs" are probably sufficient to deter most people from using this sort of thing. On the side of administering certificates the process of creating a single certificate for a single user for a single application is easy enough but I think there is not insignificant overhead to doing real certificates management. My impression is that the real thing involves something like FreeIPA or Dogtag PKI (I can't immediately tell!). I will admit I feel a sense of fatigue just looking at the number of acronyms and jargon surrounding PKI so I think I'll leave this here for now — the simple case is simple and then thing seem to rapidly spiral out to require a lot of supporting infrastructure.
I think there may be some interesting potential for access controls built on top of what I've found here, leveraging HAProxy in ways I've investigated before but I will either need to spend more time digging into certificate management or admit I don't know how it'd work in practice. I'm presently more interested in application-level uses but the breadth of PKI management leaves me a little flummoxed.