Using Yubikey/PKCS11 for Upstream Client Certificates

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Using Yubikey/PKCS11 for Upstream Client Certificates

teward
Hi there,

I'm building a reverse proxy that needs to use TLS client certificates for
authentication to its proxy_pass location.

The documentation at
https://docs.nginx.com/nginx/admin-guide/security-controls/securing-http-traffic-upstream/
is pretty clear in how to point Nginx to the signed certificate and private
key file, but my cert and key are in hardware (YubiKey in PIV mode).

I have pkcs11 support through OpenSC, but I'm wondering if Nginx can work
with that. Is there a way to have it use the yubikey through pkcs11?

Cheers,
Erik

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286922,286922#msg-286922

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Using Yubikey/PKCS11 for Upstream Client Certificates

teward
Specifically, I'd like to know if the proxy_ssl_certificate and
proxy_ssl_certificate_key directives can support RFC-7512 PKCS#11 URIs, or
whether they're hardwired to be just local file paths.

With my private key in hardware, I'm looking for the ability to point nginx
to something like:

location /upstream {
    proxy_pass                https://backend.example.com;
    proxy_ssl_certificate     /etc/nginx/client.pem;
    proxy_ssl_certificate_key
'pkcs11:type=private;token=some_token;object=username%40example.org';
}

Cheers,
Erik van Zijst

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286922,286930#msg-286930

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Using Yubikey/PKCS11 for Upstream Client Certificates

Konstantin Pavlov
Hi Erik,

I've been enable to use an yubikey neo to store a server key and utilize
them via pkcs11 engine in nginx some time ago.  I didnt check the
upstream connection, since I only cared about front-end.
And as I only had a yubikey neo instead of a proper HSM, it turned out
to be a crypto deccelerator. :-)

I've took some notes on implementing it at http://thre.sh/yub.txt, hope
this helps.

04.02.2020 20:14, erik wrote:

> Specifically, I'd like to know if the proxy_ssl_certificate and
> proxy_ssl_certificate_key directives can support RFC-7512 PKCS#11 URIs, or
> whether they're hardwired to be just local file paths.
>
> With my private key in hardware, I'm looking for the ability to point nginx
> to something like:
>
> location /upstream {
>     proxy_pass                https://backend.example.com;
>     proxy_ssl_certificate     /etc/nginx/client.pem;
>     proxy_ssl_certificate_key
> 'pkcs11:type=private;token=some_token;object=username%40example.org';
> }
>
> Cheers,
> Erik van Zijst
>
> Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286922,286930#msg-286930
>
> _______________________________________________
> nginx mailing list
> [hidden email]
> http://mailman.nginx.org/mailman/listinfo/nginx
>

--
Konstantin Pavlov
https://www.nginx.com/
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Using Yubikey/PKCS11 for Upstream Client Certificates

teward
In reply to this post by teward
According to the documentation
(http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key),
proxy_ssl_certificate_key supports syntax for ssl-engine specific backends:

> The value engine:name:id can be specified instead of the file (1.7.9),
which loads a secret key with a specified id from
> the OpenSSL engine name.

which implies that at least for the private key we should be able to
configure a pluggable ssl engine backend.

I've got my private key loaded in aYubikey and have the pkcs11 engine loaded
in openssl:

    $ openssl engine -t pkcs11
    (pkcs11) pkcs11 engine
         [ available ]

However, when I specify:

    location /upstream {
        proxy_pass https://10.16.1.21:443/;
        proxy_ssl_certificate /etc/nginx/ssl/cert.pem;
        proxy_ssl_certificate_key
"engine:pkcs11:pkcs11:id=%01;type=private";
    }

and hit the endpoint with debug error logging turned on, it fails during the
upstream TLS handshake:

    2020/02/05 07:40:28 [debug] 25199#25199: *1 http upstream request:
"/upstream?"
    2020/02/05 07:40:28 [debug] 25199#25199: *1 http upstream send request
handler
    2020/02/05 07:40:28 [debug] 25199#25199: *1 malloc: 000055AB2AB745C0:72
    2020/02/05 07:40:28 [debug] 25199#25199: *1 set session:
0000000000000000
    2020/02/05 07:40:28 [debug] 25199#25199: *1 tcp_nodelay
    2020/02/05 07:40:28 [debug] 25199#25199: *1 SSL_do_handshake: -1
    2020/02/05 07:40:28 [debug] 25199#25199: *1 SSL_get_error: 2
    2020/02/05 07:40:28 [debug] 25199#25199: *1 SSL handshake handler: 0
    2020/02/05 07:40:28 [debug] 25199#25199: *1 SSL_do_handshake: -1
    2020/02/05 07:40:28 [debug] 25199#25199: *1 SSL_get_error: 5
    2020/02/05 07:40:28 [error] 25199#25199: *1 peer closed connection in
SSL handshake (104: Connection reset by peer) while SSL handshaking to
upstream, client: ::1, server: _, request: "GET /upstream HTTP/1.1",
upstream: "https://10.16.1.21:443/", host: "localhost"


Cheers,
Erik van Zijst

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286922,286957#msg-286957

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Using Yubikey/PKCS11 for Upstream Client Certificates

teward
I figured it out and thought I'd post back for anyone else looking at this
post in the future.

My problem had nothing to do with the PKCS#11 engine. It persisted when I
pointed proxy_ssl_certificate_key directly at the non-encrypted,
password-less rsa key file.

Instead, the problem was SNI. By default, Nginx uses the inbound request's
Host header as the upstream SNI name. Since I was hitting Nginx with curl on
localhost, it ended up sending "localhost" as the upstream virtual host.
It's even in the debug error log:

2020/02/05 07:40:28 [error] 25199#25199: *1 peer closed connection in SSL
handshake (104: Connection reset by peer) while SSL handshaking to upstream,
client: ::1, server: _, request: "GET /upstream HTTP/1.1", upstream:
"https://10.16.1.21:443/", host: "localhost"

Since the upstream server does not have localhost as its SNI name, the TLS
connection failed to get established. By fixing the value for SNI it went
through:

        proxy_ssl_server_name on;
        proxy_ssl_name upstream.example.org:443;

I had to do a similar thing for the upstream HTTP Host header, which was
also being set to the value of the incoming request (again, localhost for
me):

        proxy_set_header Host upstream.example.org:443;

Now to get the full PKCS#11 uri for the Yubikey I ran:

  $ p11tool --provider /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
--list-privkeys  --login
    Token 'PIV Card Holder pin (PIV_II)' with URL
'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=00000000;token=PIV%20Card%20Holder%20pin%20%28PIV_II%29'
requires user PIN
  Enter PIN:
  Object 0:
    URL:
pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=00000000;token=PIV%20Card%20Holder%20pin%20%28PIV_II%29;id=%01;object=PIV%20AUTH%20key;type=private
    Type: Private key
    Label: PIV AUTH key
    Flags: CKA_WRAP/UNWRAP; CKA_PRIVATE; CKA_NEVER_EXTRACTABLE;
CKA_SENSITIVE;
    ID: 01

Prepending that with "engine:pkcs11:" and plugging that into
proxy_ssl_certificate_key:

    proxy_ssl_certificate /etc/nginx/ssl/cert.pem;
    proxy_ssl_certificate_key
"engine:pkcs11:pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=00000000;token=PIV%20Card%20Holder%20pin%20%28PIV_II%29;id=%01;object=PIV%20AUTH%20key;type=private;pin-value=123456";

And that made the whole thing work. Note that the client certificate itself
is still read from a file as proxy_ssl_certificate does not support pkcs11
uri's.

I can now access the remote TLS server through the local proxy:

    $ curl http://localhost/foo/bar


Erik van Zijst

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286922,286966#msg-286966

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Using Yubikey/PKCS11 for Upstream Client Certificates

teward
In reply to this post by Konstantin Pavlov
Thanks, I got it working in the end though. I realize a Yubikey isn't
terribly performant but for my particular use case I don't expect that to be
a problem.

Cheers,
Erik

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286922,286967#msg-286967

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx