NGINX Unit

SSL/TLS certificates§

The /certificates section of the control API handles TLS certificates that are used with Unit’s listeners.

To set up SSL/TLS for a listener, upload a .pem file with your certificate chain and private key to Unit, and name the uploaded bundle in the listener’s configuration; next, the listener can be accessed via SSL/TLS.

Note

For the details of certificate issuance and renewal in Unit, see an example in TLS with Certbot.

First, create a .pem file with your certificate chain and private key:

$ cat cert.pem ca.pem key.pem > bundle.pem

Usually, your website’s certificate (optionally followed by the intermediate CA certificate) is enough to build a certificate chain. If you add more certificates to your chain, order them leaf to root.

Upload the resulting bundle file to Unit’s certificate storage under a suitable name (in this case, bundle):

# curl -X PUT --data-binary @bundle.pem --unix-socket \
       /path/to/control.unit.sock http://localhost/certificates/bundle

    {
        "success": "Certificate chain uploaded."
    }

Warning

Don’t use -d for file upload with curl; this option damages .pem files. Use the --data-binary option when uploading file-based data to avoid data corruption.

Internally, Unit stores the uploaded certificate bundles along with other configuration data in its state subdirectory; the control API exposes some of their properties as GET-table JSON using /certificates:

{
    "certificates": {
        "bundle": {
            "key": "RSA (4096 bits)",
            "chain": [
                {
                    "subject": {
                        "common_name": "example.com",
                        "alt_names": [
                            "example.com",
                            "www.example.com"
                        ],

                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme, Inc."
                    },

                    "issuer": {
                        "common_name": "intermediate.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Certification Authority"
                    },

                    "validity": {
                        "since": "Sep 18 19:46:19 2022 GMT",
                        "until": "Jun 15 19:46:19 2025 GMT"
                    }
                },
                {
                    "subject": {
                        "common_name": "intermediate.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Certification Authority"
                    },

                    "issuer": {
                        "common_name": "root.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Root Certification Authority"
                    },

                    "validity": {
                        "since": "Feb 22 22:45:55 2023 GMT",
                        "until": "Feb 21 22:45:55 2026 GMT"
                    }
                }
            ]
        }
    }
}

Note

Access array items, such as individual certificates in a chain, and their properties by indexing:

# curl -X GET --unix-socket /path/to/control.unit.sock \
       http://localhost/certificates/bundle/chain/0/
# curl -X GET --unix-socket /path/to/control.unit.sock \
       http://localhost/certificates/bundle/chain/0/subject/alt_names/0/

Next, add the uploaded bundle to a listener; the resulting control API configuration may look like this:

{
    "certificates": {
        "bundle": {
            "key": "<key type>",
            "chain": [
                "<certificate chain, omitted for brevity>"
            ]
        }
    },

    "config": {
        "listeners": {
            "*:443": {
                "pass": "applications/wsgi-app",
                "tls": {
                    "certificate": "bundle"
                }
            }
        },

        "applications": {
            "wsgi-app": {
                "type": "python",
                "module": "wsgi",
                "path": "/usr/www/wsgi-app/"
            }
        }
    }
}

All done; the application is now accessible via SSL/TLS:

$ curl -v https://127.0.0.1
    ...
    * TLSv1.2 (OUT), TLS handshake, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Server hello (2):
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    * TLSv1.2 (IN), TLS handshake, Server finished (14):
    * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
    * TLSv1.2 (OUT), TLS change cipher, Client hello (1):
    * TLSv1.2 (OUT), TLS handshake, Finished (20):
    * TLSv1.2 (IN), TLS change cipher, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Finished (20):
    * SSL connection using TLSv1.2 / AES256-GCM-SHA384
    ...

Finally, you can delete a certificate bundle that you don’t need anymore from the storage:

# curl -X DELETE --unix-socket /path/to/control.unit.sock \
       http://localhost/certificates/bundle

    {
        "success": "Certificate deleted."
    }

Note

You can’t delete certificate bundles still referenced in your configuration, overwrite existing bundles using put, or delete non-existent ones.