How can I check the client certificate using Snap
Asked Answered
G

1

26

I know it's rarely used, but is it possible to access the client certificate in Snap?

If not, is it possible using a different web stack?

Grau answered 19/3, 2013 at 2:43 Comment(2)
More generally, I would like to know whether any Haskell web framework provides support for certificate-based client authentication.Smudge
This is sort of a non-answer, but as a workaround, you could use nginx to verify certs and pass the DN through a header.Replication
Z
2

This is not available to you in Snap's snap-server package, which I'm assuming is how you're running your server.

Buuuuut it's not difficult to build, either by forking or as a separate module (you'll have to copy some code over, though, since some internal values you'll need aren't exported). bindHttps, located in Snap.Internal.Http.Server.TLS, is what you want to target. This function is largely a wrapper around calls to OpenSSL.Session from the HsOpenSSL library, which itself is a loose wrapper around the OpenSSL library.

Lucky for us OpenSSL has full support for client certificates. You simply have to set the verification mode to SSL_VERIFY_PEER. There are other knobs you can fiddle with too. You also have to make sure you install a certificate chain to actually verify the client certificate against. Chain of trust and all that jazz. For reference, see how nginx does it.

Even better, this function is exposed in HsOpenSSL as the function contextSetVerificationMode :: SSLContext -> VerificationMode -> IO (). You'll notice that ctx :: SSLContext exists in the definition of Snap's bindHttps. All you'll have to do is either copy or fork that module and introduce your calls.

It would look something like this (unverified code alert):

± % diff -u /tmp/{old,new}
--- /tmp/old    2016-04-11 11:02:42.000000000 -0400
+++ /tmp/new    2016-04-11 11:02:56.000000000 -0400
@@ -19,6 +19,7 @@

      ctx <- SSL.context
      SSL.contextSetPrivateKeyFile ctx key
+     SSL.contextSetVerificationMode ctx (SSL.VerifyPeer True True (Just (\_ _ -> return True)))
      if chainCert
        then SSL.contextSetCertificateChainFile ctx cert
        else SSL.contextSetCertificateFile ctx cert

The first boolean tells OpenSSL to fail if no client certificate is present. The second boolean tells OpenSSL that the client certificate is only needed on the first request and no longer needed on renegotiations. The third value is a callback. I think the right thing to do is to just return True in the callback. It's what nginx does, anyway.

Zea answered 11/4, 2016 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.