SSL client certificate authentication returns "21:unable to verify the first certificate"

I'm in the process of transferring some of my old-and-busted apache2 web servers to newer and more resilient nginx containers.

On my old web server, I have an apache2 server that hosts secretbackdoor.example.com, which authenticates "users" (only me) using a signed certificate. The CA is setup in the following way:

> ROOT CA (self-signed, obviously)
|--> Intermediate CA (Signed by ROOT CA)
  |--> Client Certificate (Signed by Intermediate CA)

This stack overflow question showed that I can't do direct intermediate CA verification. Aside: I agree technically that nginx is right but practically, it's useful to have certificates scoped to a specific Intermediate CA....I digress.

That being said, I've followed the instructions on the question but have hit a wall. I've set ssl_client_certificate to be the CA certificate. When I try to connect with my browser that has a client certificate, I get the following error in my error.log file:

client SSL certificate verify error: (21:unable to verify the first certificate) while reading client request headers, client: 1.1.1.1, server: secretbackdoor.example.com, request: "GET / HTTP/2.0", host: "secretbackdoor.example.com"

I've tried using the intermediary CA in addition to setting ssl_client_certificate to the chained certificates to no avail.

I'm running nginx using docker with letsencrypt certificates securing the traffic. I'm not sure if that's what's causing the issues, but thought it shouldn't be a problem since client certificates can and often are signed by different CA authorities.

Here is my site setup:

server {
    listen       443;
    server_name  secretbackdoor.example.com;

    ssl_certificate /etc/letsencrypt/live/secretbackdoor.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/secretbackdoor.example.com/privkey.pem;
    ssl_client_certificate /etc/nginx/conf.d/certs/root_ca.pem;

    ssl_verify_client on;
    ssl_verify_depth 2;

    # https://github.com/certbot/certbot/blob/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
    include /etc/letsencrypt/options-ssl-nginx.conf;
    # https://github.com/certbot/certbot/blob/master/certbot/certbot/ssl-dhparams.pem
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Any assistance would be greatly appreciated!


TLDR; include the entire certificate chain in the ssl_client_certificate file.

I'm not sure if this will help you now, but I am able to verify client certificates issued by a tertiary CA. Here is how it is laid out:

> Root CA (self-signed, obviously)
|--> Intermediate CA (Signed by Root CA)
  |--> Department CA (Signed by Intermediate CA)
    |--> Client Certificate (Signed by Department CA)

The file that I use for ssl_client_certificate has all 3 CA certs in it. I also have ssl_verify_depth set to 3. The other important step I took was to ensure that error_log was set to info and was isolated to the specific server section I was testing. This is the error message nginx was producing when I only had part of the certificate chain in the configuration:

[info] 20996#20996: *1 client SSL certificate verify error: (21:unable to verify the first certificate) while reading client request headers, client: 10.0.1.33, server: , request: "GET / HTTP/1.1", host: "dept-connector"