failure to limit access to a secure area with self-signed client SSL

P
  • 20 Mar '23
i run

    nginx -v
        nginx version: nginx/1.23.3 (COPR Build)

the server's setup to use LE certs

    server {

    ...
     ssl_trusted_certificate   "/www/sec/le/deploy/otherexample.com/intermediate_ca.ec.crt.pem";
     ssl_certificate           "/www/sec/le/deploy/otherexample.com/fullchain.ec.crt.pem";
     ssl_certificate_key       "/www/sec/le/deploy/otherexample.com/priv.ec.key";
    ...

i've a secure area that i want to limit access to clients only with exact-matching ssl cert fingerprints

i've added

     map $ssl_client_fingerprint $test_ssl_fp_reject {
        default 1;
        # cert's SHA1 FP
        01234567890ABCDEFGHIJK1234567890ABCDEFGH 0;
     }
    ...
    log_format ssl_client
        '"Client fingerprint" $ssl_client_fingerprint '
        '"Client DN" $ssl_client_s_dn ';
    ...

    server {
        ...
        # attempt the verify, to populate $ssl_client_fingerprint
        ssl_verify_client optional;
        ssl_verify_depth 2;
        ssl_client_certificate "/etc/ssl/cert.pem";
        ...
        location /sec/test {
            if ($test_ssl_fp_reject) {return 403; }

            root /www/sec/test;
            try_files /test.php =444;
            fastcgi_pass   phpfpm;
            fastcgi_index  test.php;
            fastcgi_param  PATH_INFO $fastcgi_script_name;
            include fastcgi.conf;
        }
        ...
        access_log  /var/log/nginx/ssl.log ssl_client;

the client cert's self-signed with my own CA, and usage's config'd for Client auth,

    openssl x509 -in desktop.example.com.client.ec.crt.pem -text -noout
        Certificate:
            Data:
                Version: 3 (0x2)
                Serial Number: 4859 (0x12fb)
                Signature Algorithm: ecdsa-with-SHA256
                Issuer: C = US, ST = NY, O = example.com, OU = example.com_CA, CN = example.com_CA_INT, emailAddress = ssl at example.com
                Validity
                    Not Before: Mar 20 11:17:47 2023 GMT
                    Not After : Mar 17 11:17:47 2024 GMT
                Subject: C = US, ST = NY, L = New_York, O = example.com, OU = example.com_CA, CN = desktop.example.com, emailAddress = ssl at example.com
                Subject Public Key Info:
                    Public Key Algorithm: id-ecPublicKey
                        Public-Key: (384 bit)
                        pub:
                            04:...:e5
                        ASN1 OID: secp384r1
                        NIST CURVE: P-384
                X509v3 extensions:
                    X509v3 Basic Constraints:
                        CA:FALSE
                    Netscape Cert Type:
                        SSL Client, S/MIME
                    Netscape Comment:
                        example.com CLIENT Certificate
                    X509v3 Subject Key Identifier:
                        CC:...:06
                    X509v3 Authority Key Identifier:
                        D0:...:CD
                    X509v3 Key Usage: critical
                        Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Key Agreement
                    X509v3 Extended Key Usage:
                        TLS Web Client Authentication, E-mail Protection
                    X509v3 Subject Alternative Name:
                        DNS:desktop.example.com, DNS:www.desktop.example.com
            Signature Algorithm: ecdsa-with-SHA256
            Signature Value:
                30:...:6f

i've imported the cert as .pfx into Firefox & Chrome.

i can access

    https://otherexample.com

as usual.

now, on access to EITHER of

    https://otherexample.com
    https://otherexample.com/sec/test

in browser i get

    400 Bad Request
    The SSL certificate error
    nginx

while in log, i _do_ see the captured FP & DN,

    tail -f /var/log/nginx/ssl.log

        "Client fingerprint" 01234567890ABCDEFGHIJK1234567890ABCDEFGH "Client DN" emailAddress=ssl at example.com,CN=desktop.example.com,OU=example.com_CA,O=example.com,L=New_York,ST=NY,C=US

if i toggle

    -   ssl_verify_client optional;
    +   ssl_verify_client off;

now, access to

    https://otherexample.com

works. but

    https://otherexample.com/sec/test

returns

    403 Forbidden
    nginx

since the $ssl_client_fingerprint doesn't populate

    tail -f /var/log/nginx/ssl.log

        "Client fingerprint" - "Client DN" -

and, if I turn off ALL client verification, then access to frontend and by secure area works as expected.

what config change's needed to

    (1) keep the site publicly accessible using the LE certs"
    (2) lock down to secure area for exact FP-match access only?
F
  • 21 Mar '23
On Mon, Mar 20, 2023 at 01:51:47PM -0400, PGNet Dev wrote:

Hi there,

> now, on access to EITHER of
> 
>   https://otherexample.com
>   https://otherexample.com/sec/test
> 
> in browser i get
> 
>   400 Bad Request
>   The SSL certificate error
>   nginx

What does the error_log say about this request and response?

It looks like some part of your nginx/tls setup fails to verify the
client certificate; maybe the debug or info log will hint at why.

Good luck with it,

    f
-- 
Francis Daly        francis at daoine.org
P
  • 21 Mar '23
> What does the error_log say about this request and response?

nothing that's giving me a hint i recognize,

    ...
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 header: "cache-control: no-cache"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 encoded string, len:2
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 encoded string, len:6
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 table add: "te: trailers"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 table account: 42 free:2775
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 header: "te: trailers"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 request line: "GET / HTTP/2.0"
    2023/03/21 18:52:14 [info] 4955#4955: *7 client SSL certificate verify error: certificate status request failed while reading client request headers, client: 2401::...::1, server: example.com, request: "GET / HTTP/2.0", host: "example.com"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http finalize request: 495, "/?" a:1, c:1
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http special response: 495, "/?"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 headers more header filter, uri "/"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 xslt filter header
    2023/03/21 18:52:14 [debug] 4955#4955: *7 charset: "" > "utf-8"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 header filter
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 push resources
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 table size update: 0
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: ":status: 400"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "date: Tue, 21 Mar 2023 22:52:14 GMT"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "content-type: text/html; charset=utf-8"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "content-length: 208"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "secure: Server"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "x-robots-tag: noindex, nofollow, nosnippet, noarchive"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "x-download-options: noopen"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "x-permitted-cross-domain-policies: none"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "permissions-policy: interest-cohort=()"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "x-xss-protection: 1; mode=block"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "strict-transport-security: max-age=63072000; includeSubDomains; preload"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "x-frame-options: SAMEORIGIN"
    2023/03/21 18:52:14 [debug] 4955#4955: *7 http2 output header: "referrer-policy: strict-origin-when-cross-origin"
    ...
F
  • 22 Mar '23
On Tue, Mar 21, 2023 at 07:02:23PM -0400, PGNet Dev wrote:
> > What does the error_log say about this request and response?

>   2023/03/21 18:52:14 [info] 4955#4955: *7 client SSL certificate verify error: certificate status request failed while reading client request headers, client: 2401::...::1, server: example.com, request: "GET / HTTP/2.0", host: "example.com"

That'll be why nginx blocks the access, at least -- the client cert is
not verified as good.

You have indicated that the client cert has:

Issuer: C = US, ST = NY, O = example.com, OU = example.com_CA, CN = example.com_CA_INT, emailAddress = ssl at example.com

Do you have the certificate that has that value as the Subject? What
is that certificate's Issuer? And repeat until you get to the root
certificate.

And which of the ssl*certificate files named in your config holds those certificates?

    f
-- 
Francis Daly        francis at daoine.org
P
  • 22 Mar '23
> Do you have the certificate that has that value as the Subject? What
> is that certificate's Issuer? And repeat until you get to the root
> certificate.
> 
> And which of the ssl*certificate files named in your config holds those certificates?

i verified all my certs/chains. all good.

with my orig conf, it appears i can't manage to grab/verify ssl client FP's for other-than-primary domains

this fails to work, errors as reported above,

    server {
        ...
        servername example.com;

        ssl_verify_client optional;
        ssl_verify_depth 2;
        ssl_client_certificate    "/www/ssl/self-signed/myCA.CHAIN.crt.pem";

        ssl_trusted_certificate   "/www/ssl/le/deploy/example.com/intermediate_ca.ec.crt.pem";
        ssl_certificate           "/www/ssl/le/deploy/example.com/fullchain.ec.crt.pem";
        ssl_certificate_key       "/www/ssl/le/deploy/example.com/priv.ec.key";

        location /test {
            if ($ssl_client_verify != SUCCESS) { return 403; }
            if ($test_ssl_fp_reject) {return 403; }
        ...
    }

OTOH simply splitting the secure subdir out into a separate server{}/subdomain, with separate, self-signed cert

    server {
        ...
        servername example.com;

        ssl_verify_client off;
        ssl_trusted_certificate   "/www/ssl/le/deploy/example.com/intermediate_ca.ec.crt.pem";
        ssl_certificate           "/www/ssl/le/deploy/example.com/fullchain.ec.crt.pem";
        ssl_certificate_key       "/www/ssl/le/deploy/example.com/priv.ec.key";
    }

    server {

        servername test.example.com;

        ssl_verify_client on;
        ssl_client_certificate  "/www/ssl/self-signed/myCA.CHAIN.crt.pem";
        ssl_verify_depth 2;
        ssl_certificate         "/www/ssl/self-signed/test.example.com.server.ec.crt.pem";
        ssl_certificate_key     "/www/ssl/self-signed/test.example.com.ec.key.pem";

        location / {
            if ($ssl_client_verify != SUCCESS) { return 403; }
            if ($test_ssl_fp_reject) {return 403; }
            ...
        }
        ...
    }

achieves the intended result -- just not in the same server{} block
F
  • 23 Mar '23
On Wed, Mar 22, 2023 at 08:48:50AM -0400, PGNet Dev wrote:

Hi there,

> > Do you have the certificate that has that value as the Subject? What
> > is that certificate's Issuer? And repeat until you get to the root
> > certificate.
> > 
> > And which of the ssl*certificate files named in your config holds those certificates?
> 
> i verified all my certs/chains. all good.

You verified things in your way, and saw they were good.

The nginx logs you provided indicated that nginx verified things in its
way, and saw they were not good.

It seems like you have a system that works for you now, and that is
good.

If you want to keep testing for another system, then based on what
you reported, and what you provided here, my guess is that your client
certificate does verify against whatever is in myCA.CHAIN.crt.pem,
and does not verify against whatever is in intermediate_ca.ec.crt.pem.

So I suspect that if you put the contents of those two files into a
single file, and then refer to that either as ssl_client_certificate or
as ssl_trusted_certificate, and do not use the other directive at all;
then things might work more like you want.

Good luck with it,

    f
-- 
Francis Daly        francis at daoine.org