Certgrinder

The certgrinder client is responsible for generating a keypair and a CSR, which it uses to contact the Certgrinder server certgrinderd over SSH to get a signed certificate. The following section explains how to install and configure it to get certificates from the Certgrinder server.

Installation

This section explains the steps to install a Certgrinder client. Repeat these steps on each server in need of certificates!

Install certgrinder

You can install certgrinder from pip with pip install certgrinder. This will install the latest certgrinder release. It will pull in the dependencies it needs automatically.

You can also checkout the Github repo and install the deps from requirements.txt by hand if you prefer. If you want to install with pip directly from Git the following may help: pip install "git+https://github.com/tykling/certgrinder/#egg=certgrinder&subdirectory=client"

Create Certgrinder User

Since certgrinder is designed to be run under a separate system user one should be created. The user needs sudo access if (and only if) it is to be able to reload/restart services after renewing certificates. Sometimes it is also necessary to add some system users to the certgrinder group so they can read certificates. More on that later.

The user also needs to run ssh-keygen and the SSH key needs to be added to the authorized_keys file on the Certgrinder server. Make sure to test the SSH access works (hint: check firewalls, v4 vs v6 etc).

Configuration

Configuration of certgrinder can be done using command-line options, or a configuration file, or a combination of the two.

The certgrinder configuration file is in YAML format. An example config named certgrinder.conf.dist can be found in the distribution. use --config-file or -f to specify the config file location.

Each config item can be specified either in the YAML config file as a key: value pair, or on the commandline as --key value - the latter overriding the former if both are present. For example, if the configfile has log-level: INFO and the command-line has --log-level: DEBUG then the effective log-level would be DEBUG.

This is an alphabetical list of the configurable options:

alternate-chain

Instruct certgrinder to request the alternate chain for signing.

For production this means using the short chain with 1 intermediate signed by ISRG Root X1 instead of using the default long chain with 2 intermediates signed by DST Root CA X3.

For staging it means using Fake LE Root X2 (1 intermediate) instead of the usual Fake LE Root X1 (2 intermediates).

Default: False

certgrinderd

The command to run as certgrinderd. Usually this will be something like ssh certgrinderd@certgrinder.example.com -T, possibly also with a --config-file for certgrinderd if needed.

Default: None

cert-renew-threshold-days

A certificate will be renewed when it has less than this many days of lifetime left.

Default: 30

domain-list

Comma-separated lists of domains for the certificates. Can be specified multiple times on the command-line, --domain-list example.com,www.example.com --domain-list example.net means two certificates, the first with two names, the second with one name.

Default: None

invalid-ca-cn-list

List of CommonName of certificate issuers to consider invalid. This is not a regular CA certificate validity check, it is used to detect certificates issued by LetsEncrypt staging servers as invalid.

Default: ["Fake LE Intermediate X1", "Fake LE Intermediate X2"]

log-level

Sets the verbosity level for console and syslog logging. One of DEBUG, INFO, WARNING, ERROR, CRITICAL.

Default: INFO

key-type-list

List of key types to enable. Supported choices are rsa and ecdsa. Files for each keytype will be suffixed with .rsa.ext and ecdsa.ext, respectively.

Default: ["rsa", "ecdsa"]

name-server

Set this to a DNS server IP (v4 or v6, no hostnames) to use that DNS server instead of the system resolver.

Default: None

ocsp-renew-threshold-percent

The amount of time in percent between produced_at and next_update that must have passed before an OCSP response is considered too old. As of January 2021 LetsEncrypt has 7 days between produced_at and next_update in OCSP responses, so the default of 50% means OCSP responses will be renewed after 3.5 days (half of the validity period) has passed.

As of January 2021 LetsEncrypt produces new OCSP responses after half of the validity period has passed, so any setting lower than that will be pointless. Setting this lower than 50 will just result in Certgrinder fetching the same OCSP response over and over.

Set to 0 to always renew OCSP responses regardless of their age.

Default: 50

path

The directory used for keys, CSRs and certificates. Must exist and be writable by the user running Certgrinder.

Default: None

periodic-sleep-minutes

Certgrinder will pick a random number of minutes between 0 and this number and sleep for that long before doing periodic actions. Set to 0 to disable sleeping.

Default: 60

pid-dir

The directory to place the certgrinderd PID file in.

Default: /tmp

post-renew-hooks

A list of commands which certgrinder must run after renewing one or more certificates or OCSP responses. Use this to reload/restart services which need to be poked after the certificate changes. Can be specified multiple times on the command-line. Remember to include sudo or whatever if needed. Wrap complex commands in a small shell script to avoid quoting issues.

Default: None

post-renew-hooks-dir

A path to a hooks.d style directory containing files to be executed after renewing one or more certificates or OCSP responses. Each executable file in this path will be run in the order returned by os.listdir(). Set post-renew-hooks-dir-runner if something like sudo is needed to elevate privileges before running the hooks.

Default: None

post-renew-hooks-dir-runner

When this is set it will be executed in place of each executable in post-renew-hooks-dir with the executable as argument.

Example: If post-renew-hooks-dir contains two executable files hook1 and hook2 and post-renew-hooks-dir-runner is set to /usr/local/bin/sudo then certgrinder will execute /usr/local/bin/sudo /path/to/hooks/dir/hook1 and then /usr/local/bin/sudo /path/to/hooks/dir/hook2 instead of executing the two hooks directly.

Default: None

staging

Enable staging mode. Adds --staging to the certgrinderd command, and considers certificates issued by LE staging servers as valid.

Default: False

syslog-facility

Set this and syslog-socket to enable logging to syslog. Must be a value supported by logging.handlers.SysLogHandler like LOG_USER or LOG_LOCAL0.

Default: None

syslog-socket

Set this and syslog-facility to enable logging to syslog.

Default: None

tlsa-port

Set this to the port (like 443) when using show tlsa or check tlsa subcommands.

Default: None

tlsa-protocol

Set this to the protocol (like tcp) when using show tlsa or check tlsa subcommands.

Default: None

tlsa-type-list

Set this to enable a TLSA type (can be specified multiple times). The TLSA type must be specified as three integers, one of: 310, 311 or 312. Default: is all three pubkey types.

Default: ["310", "311", "312"]

ACME Challenges

Finally you need to choose which challenge type to use for this certgrinder client. If DNS-01 you need to create one or more CNAME record pointing somewhere. If HTTP-01 you need to create an HTTP redirect. See the section on challenges for more info.

Testing

At this point you should be ready to test! Start by checking with SSH manually to see that the SSH key is installed properly on the Certgrinder server, and firewalls are open. Certgrinder has a --staging switch which makes certgrinderd use the LetsEncrypt staging environment. Use this until everything works! certgrinder outputs some info on what happens, and can output more with -d / --debug, but sometimes you need to check syslog on the Certgrinder server.

Crontab job

I run Certgrinder daily, although by default it only attempts certificate renewal when less than 30 days validity remains, and OCSP response renewal when half the validity period has passed.

When everything above works it is time to automate it by adding it to crontab. The following line works for me (the periodic command sleeps a random number of minutes before doing its thing, so all the clients don’t contact the Certgrinder server at once):

0 2 * * * certgrinder /usr/home/certgrinder/virtualenv/bin/certgrinder -c /usr/home/certgrinder/certgrinder.conf periodic

Client Commands

All the functionality in Certgrinder can be accessed by using commands and subcommands. The following commands are available:

check commands

All the subcommands for the check commands return exit code 1 if a problem is found and 0 if everything is fine.

check certificate command

The check certificate subcommand loops over the configured domainsets and checks the validity of the certificate for each. If a problem is found certgrinder will exit with exit code 1, if all is well the exit code will be 0.

check connection command

The check connection subcommand simply checks that the connection to the certgrinderd server works as expected. It calls the ping command on the certgrinderd server and expects to see the string pong on stdout. If the expected string is found the exit code will be 0, if a problem is found the exit code will be 1.

check ocsp command

The check ocsp subcommand loops over the configured domainsets and checks for the existance of an OCSP response for each. If an OCSP response for a certificate is missing or too old certgrinder will exit with exit code 1, if all is well the exit code will be 0.

An OCSP response is considered too old when more than ocsp-renew-threshold-percent percent of the time between producedAt and nextUpdate has passed. As of January 2021 LetsEncrypt has 7 days (one week) between producedAt and nextUpdate which means OCSP responses will be renewed after 3.5 days with the default ocsp-renew-threshold-percent setting of 50.

check tlsa command

The check tlsa subcommand is like the show tlsa subcommand but it goes one step further and actually checks in the DNS if the records could be found, and prints some output accordingly. The following example shows two runs of check tlsa mode. The first run finds no TLSA records and outputs what needs to be added:

[certgrinder@znc ~]$ ./virtualenv/bin/certgrinder -f certgrinder.conf check tlsa 443 tcp
2018-02-16 08:59:39 +0000 INFO: Processing domains: znc.tyknet.dk
2018-02-16 08:59:39 +0000 INFO: Looking up TLSA records for _443._tcp.znc.tyknet.dk
2018-02-16 08:59:39 +0000 WARNING: No TLSA records for name _443._tcp.znc.tyknet.dk of type 3 1 0 was found in DNS. This record needs to be added:
2018-02-16 08:59:39 +0000 WARNING: _443._tcp.znc.tyknet.dk 3 1 0 30820222300d06092a864886f70d01010105000382020f003082020a0282020100bb852c1035ee7ce08d69a13f5cca95374dc872b2028e65ee34600478076c9185e79ff373d3acfc4aa29f152b9abcb515e449417ce7768f7f91915ff2d6e75d732e863021240ce4b24475220306e6ffd3f963dc4a8eafb4077f635d8a0d655b5921df2bcb2e6e610aa8db1d79b6da14d1fc7d842c1e5d4cbfa6697617aa9d2251be1a386fd7c14eccef21151c35d336ebba8f97d3160b35775c57079d2594b1d2a9d593bc408ccf2a01b171f4a3e65005b07df7efd77bac3d5f430b0aab5f161b7d7ebc40b600064ec3a4c59d64a1ec1f27c234a08a473aa0fcdf6008492161af6a1d9179a432622776e675f4d3dafb3d1d00b3189c4cdcd6de250721f012fc5f34426d06cb4b045b04ba2bd7ac2fcedce429dfde3dffcbb8b2df50cade99458c954de157b88751c26b79413d6eef5e26ab008e7aa7c69be3d6163f80f5d565b87f9030b54a23cf4c704e509cc84e618a446c75684893d65bd5fd38ef6b839d316b5616b06bbafbb7c2aa6f3db217b4df6e5f02b85d8685be14a9d480ee56c1b4454a88fc01a4532a55e926929fea70822088054f5ddf957e8c5ca2c3808c8a09b70c7eeda4883aaf6f1092033beeb0ff5621a8b8ddf3455f1d30d2398fe786038a39e0825bb6bac9865500de33eff67e3984a73b7592bde5897681b52da06c93447a0efa4d1fb52bc151811776ef501ca818c68fd1d4fe3d73c5e5526b4bf47f0203010001
2018-02-16 08:59:39 +0000 WARNING: No TLSA records for name _443._tcp.znc.tyknet.dk of type 3 1 1 was found in DNS. This record needs to be added:
2018-02-16 08:59:39 +0000 WARNING: _443._tcp.znc.tyknet.dk 3 1 1 5b95cb6ea387570f1f3dc4508794ca13a17a665733bab5f76b1e330f2fa13361
2018-02-16 08:59:39 +0000 WARNING: No TLSA records for name _443._tcp.znc.tyknet.dk of type 3 1 2 was found in DNS. This record needs to be added:
2018-02-16 08:59:39 +0000 WARNING: _443._tcp.znc.tyknet.dk 3 1 2 24d49f3c974129b9c28b5e6213892a404d8e9777c5a2e977333b88442d4e16ac0bc732001ec783df795c194704149bd18bbca21087111b33fa79e84dab05e760
2018-02-16 08:59:39 +0000 INFO: Done processing domains: znc.tyknet.dk
[certgrinder@znc ~]$

The second run is after adding the suggested records to DNS:

[certgrinder@znc ~]$ ./virtualenv/bin/certgrinder -f certgrinder.conf check tlsa 443 tcp
2018-02-16 09:16:27 +0000 INFO: Processing domains: znc.tyknet.dk
2018-02-16 09:16:27 +0000 INFO: Looking up TLSA records for _443._tcp.znc.tyknet.dk
2018-02-16 09:16:27 +0000 INFO: TLSA record for name _443._tcp.znc.tyknet.dk type 3 1 0 found in DNS matches the local key, good.
2018-02-16 09:16:27 +0000 INFO: TLSA record for name _443._tcp.znc.tyknet.dk type 3 1 1 found in DNS matches the local key, good.
2018-02-16 09:16:27 +0000 INFO: TLSA record for name _443._tcp.znc.tyknet.dk type 3 1 2 found in DNS matches the local key, good.
2018-02-16 09:16:27 +0000 INFO: Done processing domains: znc.tyknet.dk
[certgrinder@znc ~]$

All TLSA records for this public key can now be found in the DNS.

NOTE: As there might be additional records for the same name which do not belong to this server/key (for example in a loadbalanced or anycast setup), no attempts are made to warn about wrong/old/superfluous TLSA records. This might be added in a future version as a switch to tell Certgrinder that the local public key is the only one in existence for this service.

get commands

The get subcommands do all the real work.

get certificate command

The get certificate subcommand loops over the configured domainsets and gets a new certificate for each, regardless of the current status of existing certificates. Use with care, only for troubleshooting. Do not use from cron. Use the periodic command instead.

get ocsp command

The get ocsp subcommand loops over the configured domainsets and gets a new OCSP response for each, regardless of the current status of existing OCSP responses. Do not use from cron. Use the periodic command instead.

help command

The help command is just a shortcut for -h which shows commandline usage and help.

periodic command

The periodic command sleeps for a random number of minutes between 0 and the config setting periodic-sleep-minutes before doing anything. Set this setting to 0 to disable sleeping.

After sleeping the certificates and OCSP responses are checked and renewed as needed. This command is meant to be run daily from cron or similar.

show commands

The show subcommands show information but never change anything.

show certificate command

The show certificate subcommand loops over configured domainsets and outputs information about each certificate (if any).

show configuration command

The show configuration subcommand just dumps the active configuration as a pretty printed JSON object and exits. Useful for testing or debugging configuration issues.

show ocsp command

The show ocsp subcommand loops over the configured domainsets and shows info about each OCSP response.

show paths command

The show paths subcommand loops over the configured domainsets and outputs the paths used for keys, certificates and OCSP responses.

show spki command

The show spki subcommand outputs pin-sha256 spki pins for the public keys. The HPKP standard https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning defined the pin-sha256 format for public key pins. While the HPKP standard didn’t get much traction the pinning format is used in various places now, so certgrinder can generate them.

The operation is pretty simple:

[certgrinder@znc ~]$ ./virtualenv/bin/certgrinder -f certgrinder.conf show spki
2018-02-16 09:28:37 +0000 INFO: Processing domains: znc.tyknet.dk
2018-02-16 09:28:37 +0000 INFO: pin-sha256="W5XLbqOHVw8fPcRQh5TKE6F6ZlczurX3ax4zDy+hM2E="
2018-02-16 09:28:37 +0000 INFO: Done processing domains: znc.tyknet.dk
[certgrinder@znc ~]$

show tlsa command

The show tlsa subcommand loops over the configured domainsets and generates TLSA records for the public keys. The result is printed to the terminal in a format suitable for putting in the DNS. It looks something like this:

[certgrinder@znc ~]$ ./virtualenv/bin/certgrinder -f certgrinder.conf show tlsa 443 tcp
2018-02-16 08:42:18 +0000 INFO: Processing domains: znc.tyknet.dk
2018-02-16 08:42:18 +0000 INFO: TLSA records for _443._tcp.znc.tyknet.dk:
2018-02-16 08:42:18 +0000 INFO: _443._tcp.znc.tyknet.dk 3 1 0 30820222300d06092a864886f70d01010105000382020f003082020a0282020100bb852c1035ee7ce08d69a13f5cca95374dc872b2028e65ee34600478076c9185e79ff373d3acfc4aa29f152b9abcb515e449417ce7768f7f91915ff2d6e75d732e863021240ce4b24475220306e6ffd3f963dc4a8eafb4077f635d8a0d655b5921df2bcb2e6e610aa8db1d79b6da14d1fc7d842c1e5d4cbfa6697617aa9d2251be1a386fd7c14eccef21151c35d336ebba8f97d3160b35775c57079d2594b1d2a9d593bc408ccf2a01b171f4a3e65005b07df7efd77bac3d5f430b0aab5f161b7d7ebc40b600064ec3a4c59d64a1ec1f27c234a08a473aa0fcdf6008492161af6a1d9179a432622776e675f4d3dafb3d1d00b3189c4cdcd6de250721f012fc5f34426d06cb4b045b04ba2bd7ac2fcedce429dfde3dffcbb8b2df50cade99458c954de157b88751c26b79413d6eef5e26ab008e7aa7c69be3d6163f80f5d565b87f9030b54a23cf4c704e509cc84e618a446c75684893d65bd5fd38ef6b839d316b5616b06bbafbb7c2aa6f3db217b4df6e5f02b85d8685be14a9d480ee56c1b4454a88fc01a4532a55e926929fea70822088054f5ddf957e8c5ca2c3808c8a09b70c7eeda4883aaf6f1092033beeb0ff5621a8b8ddf3455f1d30d2398fe786038a39e0825bb6bac9865500de33eff67e3984a73b7592bde5897681b52da06c93447a0efa4d1fb52bc151811776ef501ca818c68fd1d4fe3d73c5e5526b4bf47f0203010001
2018-02-16 08:42:18 +0000 INFO: _443._tcp.znc.tyknet.dk 3 1 1 5b95cb6ea387570f1f3dc4508794ca13a17a665733bab5f76b1e330f2fa13361
2018-02-16 08:42:18 +0000 INFO: _443._tcp.znc.tyknet.dk 3 1 2 24d49f3c974129b9c28b5e6213892a404d8e9777c5a2e977333b88442d4e16ac0bc732001ec783df795c194704149bd18bbca21087111b33fa79e84dab05e760
[certgrinder@znc ~]$

Shown above is the show tlsa subcommand in action. The value supplied should be the port and protocol of the service, in the example above it is a HTTPS service, so the TLSA record is the service hostname prefixed with _443._tcp.

version command

The version command is just a shortcut for -v which shows the Certgrinder version and exits.

Command Line Usage

Certgrinder version 0.18.0-dev. See the manpage or ReadTheDocs for more info.

usage: certgrinder [-h] [-a] [--certgrinderd CERTGRINDERD]
                   [--cert-renew-threshold-days CERT-RENEW-THRESHOLD-DAYS]
                   [-c CONFIG-FILE] [-d] [-D DOMAIN-LIST]
                   [--invalid-ca-cn-list INVALID-CA-CN-LIST]
                   [-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [-k {rsa,ecdsa}]
                   [-n NAME-SERVER] [--now] [-o OCSP-RENEW-THRESHOLD-PERCENT]
                   [--path PATH]
                   [--periodic-sleep-minutes PERIODIC-SLEEP-MINUTES]
                   [-p PID-DIR] [--post-renew-hooks POST-RENEW-HOOKS]
                   [--post-renew-hooks-dir POST-RENEW-HOOKS-DIR]
                   [--post-renew-hooks-dir-runner POST-RENEW-HOOKS-DIR-RUNNER]
                   [-q] [-s] [--syslog-facility SYSLOG-FACILITY]
                   [--syslog-socket SYSLOG-SOCKET] [--tlsa-port TLSA-PORT]
                   [--tlsa-protocol TLSA-PROTOCOL]
                   [--tlsa-type-list {310,311,312}] [-v]
                   {check,get,help,periodic,show,version} ...

Positional Arguments

command

Possible choices: check, get, help, periodic, show, version

Command (required)

Named Arguments

-a, --alternate-chain

Use alternate chain. For production this means using the short chain with 1 intermediate signed by ‘ISRG Root X1’ instead of using the long chain with 2 intermediates signed by ‘DST Root CA X3’. For staging it means using ‘Fake LE Root X2’ (1 intermediate) instead of the usual ‘Fake LE Root X1’ (2 intermediates).

--certgrinderd

The command to reach the certgrinderd server, will get the input (CSR or cert chain) on stdin. Usually something like ‘ssh certgrinderd@server -T’

--cert-renew-threshold-days

A certificate is renewed when it has less than this many days of lifetime left. Default: 30

-c, --config-file

The path to the certgrinder.yml config file to use

-d, --debug

Debug mode. Equal to setting –log-level=DEBUG.

-D, --domain-list

Comma separated list of domains for a certificate. Can be specified multiple times.

--invalid-ca-cn-list

The CommonName of an issuer (CA intermediate) to consider invalid. Can be specified multiple times.

-l, --log-level

Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL

Logging level. One of DEBUG, INFO, WARNING, ERROR, CRITICAL. Defaults to INFO.

-k, --key-type-list

Possible choices: rsa, ecdsa

The keytypes to enable. Valid values are ‘rsa’ and ‘ecdsa’. Can be specified multiple times. Defaults to both rsa and ecdsa.

-n, --name-server

Tell certgrinder to use this DNS server IP to lookup TLSA records. Only relevant with -c / –checktlsa. Only v4/v6 IPs, no hostnames.

--now

Run periodic command without delay. Equal to setting –periodic-sleep-minutes 0.

-o, --ocsp-renew-threshold-percent

Possible choices: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100

An integer between 0 and 100 specifying the amount of time in percent between produced_at and next_update which must have passed before an OCSP response is considered too old. Defaults to 50.

--path

Tell certgrinder to use the specified directory for keys, CSRs and certificates. The directory must exist and be writeable by the user running certgrinder.

--periodic-sleep-minutes

Tell certgrinder to sleep for a random number of minutes between 0 and this number before doing anything when the periodic command is used. Set to 0 to disable sleeping.

-p, --pid-dir

The directory to store the PID file in

--post-renew-hooks

The list of commands to run after one or more certificates are renewed. Most such commands will need root access to run, remember to prefix the command with ‘sudo’ as needed. Can be specified multiple times. Default: None

--post-renew-hooks-dir

Path to a folder containing executables to run after one or more certificates or OCSP responses are renewed. These will execute under the regular certgrinder user uid, so make sure to use sudo/doas in scripts or suid executables as needed. Default: None

--post-renew-hooks-dir-runner

Path to an executable like sudo to be used to run each of the executables in the post renew hooks dir. Default: None

-q, --quiet

Quiet mode. No output at all if there is nothing to do, and no errors are encountered. Equal to setting –log-level=WARNING.

-s, --staging

Staging mode. Sets –acme-server-url https://acme-staging-v02.api.letsencrypt.org/directory and –invalid-ca-cn-list empty. Use this while playing around to avoid hitting rate limits!

--syslog-facility

The syslog facility to use. Set this and syslog-socket to enable logging to syslog.

--syslog-socket

The syslog socket to connect to. Set this and syslog-facility to enable logging to syslog.

--tlsa-port

The service port number (like 443) for TLSA operations.

--tlsa-protocol

The service protocol (like tcp) for TLSA operations.

--tlsa-type-list

Possible choices: 310, 311, 312

Enables a TLSA type for TLSA operations. Can be specified multiple times.

-v, --version

Show version and exit.

Sub-commands:

check

Use the “check” command to check certificates, OCSP responses and TLSA records. Returns exit code 0 if all is well, and 1 if something needs attention.

certgrinder check [-h] {certificate,connection,ocsp,tlsa} ...
Positional Arguments
subcommand

Possible choices: certificate, connection, ocsp, tlsa

Specify what to check using one of the available check sub-commands.

Sub-commands:
certificate

Tell certgrinder to check certificate validity for all configured domainsets. Returns exit code 1 if any problem is found, exit code 0 if all is well.

certgrinder check certificate [-h]
connection

Tell certgrinder to check the connection to the certgrinderd server by calling the certgrinderd ‘ping’ command which should return the string ‘pong’ if all is well.

certgrinder check connection [-h]
ocsp

Tell certgrinder to check the OCSP response validity for certificates for all configured domainsets. Returns exit code 1 if any problem is found, exit code 0 if all is well.

certgrinder check ocsp [-h]
tlsa

Tell certgrinder to lookup TLSA records for the given port and protocol in the DNS and compare with what we have locally, for example: ‘certgrinder check tlsa 853 tcp’

certgrinder check tlsa [-h] tlsa-port tlsa-protocol
Positional Arguments
tlsa-port

The port of the service, for example 443

tlsa-protocol

The protocol of the service, for example tcp

get

Use the “get” command to get certificates and OCSP responses

certgrinder get [-h] {certificate,ocsp} ...
Positional Arguments
subcommand

Possible choices: certificate, ocsp

Specify what to get using one of the available get sub-commands

Sub-commands:
certificate

Tell certgrinder to get new certificate(s), regardless of their current state. Rarely needed, use ‘periodic’ command instead.

certgrinder get certificate [-h]
ocsp

Tell certgrinder to get OCSP responses for the configured domainset(s). Rarely needed, use ‘periodic’ command instead.

certgrinder get ocsp [-h]

help

The “help” command just outputs the usage help

certgrinder help [-h]

periodic

The “periodic” command checks certificates and renews them as needed. Meant to be run from cron or similar daily.

certgrinder periodic [-h]

show

Use the “show” command to show certificates, TLSA records, SPKI pins or configuration.

certgrinder show [-h] {certificate,configuration,paths,ocsp,spki,tlsa} ...
Positional Arguments
subcommand

Possible choices: certificate, configuration, paths, ocsp, spki, tlsa

Specify what to show using one of the available show sub-commands

Sub-commands:
certificate

Tell certgrinder to output information about certificates.

certgrinder show certificate [-h]
configuration

Tell certgrinder to output the current configuration

certgrinder show configuration [-h]
paths

Tell certgrinder to output the paths used

certgrinder show paths [-h]
ocsp

Tell certgrinder to output information about OCSP responses.

certgrinder show ocsp [-h]
spki

Tell certgrinder to generate and print the pin-sha256 spki pins for the public keys it manages.

certgrinder show spki [-h]
tlsa

Use the ‘show tlsa’ sub-command to tell certgrinder to generate and print TLSA records for the given service, for example: ‘certgrinder show tlsa 443 tcp’

certgrinder show tlsa [-h] tlsa-port tlsa-protocol
Positional Arguments
tlsa-port

The port of the service, for example 443

tlsa-protocol

The protocol of the service, for example tcp

version

The “version” command just outputs the version of Certgrinder

certgrinder version [-h]

Class Methods

class certgrinder.Certgrinder

The Certgrinder client class.

__init__() None

Define the default config.

check_certificate(certificate: Optional[cryptography.x509.base.Certificate] = None, public_key: Optional[Union[cryptography.hazmat.backends.openssl.rsa._RSAPublicKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey]] = None) bool

Check certificate validity and returns True or False.

This method is called by self.grind() once per domainset when the “check certificate” subcommand is invoked. It reads the certificate from self.certificate_path if there is no certificate arg

Parameters
  • certificate – The certificate to be checked

  • public_key – The keypair the certificate is based on

Returns

True if everything is OK, False otherwise

static check_certificate_expiry(certificate: cryptography.x509.base.Certificate, threshold_days: int) bool

Check the remaining validity of the certificate.

Parameters
  • certificate – The certificate to check

  • threshold_days – The lowest number of remaining days of validity that is considered valid

Returns

True if remaining certificate lifetime is >= threshold_days, False if not

static check_certificate_issuer(certificate: cryptography.x509.base.Certificate, invalid_ca_cn_list: List[str]) bool

Check the issuer of the certificate.

Parameters
  • certificate – The certificate to check

  • invalid_ca_cn_list – The list of CA CommonName strings to consider invalid

Returns

True if the certificate issuer CN is not in invalid_ca_cn_list

static check_certificate_public_key(certificate: cryptography.x509.base.Certificate, public_key: Union[cryptography.hazmat.backends.openssl.rsa._RSAPublicKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey]) bool

Make sure certificate has the specified public key.

Parameters
  • certificate – The certificate to check

  • public_key – The public key

Returns

True if the public key matches, False if not

static check_certificate_san_names(certificate: cryptography.x509.base.Certificate, san_names: List[str]) bool

Make sure the certificate has the provided list of names as SAN.

Parameters
  • certificate – The certificate to check

  • san_names – A list of the names to expect

Returns

True if all san_names were found in the cert, and no others.

static check_certificate_subject(certificate: cryptography.x509.base.Certificate, subject: str) bool

Make sure the certificate has the specified subject.

Parameters
  • certificate – The certificate to check

  • subject – The subject to expect

Returns

True if the subject matches the cert, False if not

classmethod check_certificate_validity(certificate: cryptography.x509.base.Certificate, invalid_ca_cn_list: List[str], threshold_days: int, san_names: List[str], public_key: Optional[Union[cryptography.hazmat.backends.openssl.rsa._RSAPublicKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey]] = None, subject: str = '') bool

Perform a few sanity checks of the certificate.

  • Check that the issuer is valid

  • Check that the certificate expiry is not exceeded

  • Check that the public key is correct

  • Check that the subject is correct

  • Check that the SubjectAltName data is correct

Parameters
  • certificate – The certificate to check

  • invalid_ca_cn_list – A list of CA CommonNames to consider invalid

  • threshold_days – The minimum number of remaining days lifetime to considered valid.

  • san_names – A list of domain names to expect in SubjectAltName of the certificate.

  • keypair – The keypair the certificate is for.

Returns

False if a problem is found, True if all is well.

check_connection(stdout: Optional[bytes] = None) bool

The check connection subcommand method.

Parameters

stdout – The certgrinderd response to use instead of calling certgrinderd (optional)

Returns

None

check_ocsp() bool

The check ocsp subcommand method, called for each domainset by self.grind().

Returns

True if the OCSP response was found and is not too old, False otherwise

check_tlsa() None

The ‘check tlsa’ subcommand method, called for each domainset by self.grind().

Loops over the configured TLSA types and calls self.verify_tlsa_record() which does the heavy lifting.

Returns

None

configure(userconfig: Dict[str, Union[str, int, bool, List[str]]]) None

Merge and check configuration and configure logging.

Merge the supplied userconfig dict with the default config, checks for missing required settings, and configures logging and syslog.

Parameters

userconfig – dict of the config to be merged with the default config

Returns

None

static generate_csr(keypair: Union[cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey], domains: List[str]) cryptography.hazmat.backends.openssl.x509._CertificateSigningRequest

Generate and return a new CSR based on the public key and list of domains.

Only set CN since everything else is removed by LetsEncrypt in the certificate anyway. Add all domains in subjectAltName, including the one put into CN.

Parameters
  • keypair – The keypair to base the CSR on

  • domains – A list of domains to put in the CSR. First in the list will be cert CN.

Returns

The CSR object

static generate_private_key(keytype: str) Union[cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey, cryptography.hazmat.backends.openssl.ec._EllipticCurvePrivateKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey]

Generate and returns a private key.

Parameters

keytype – “rsa” for RSA key, “ecdsa” for ECDSA and “ed25519” for ed25519

Returns

The keypair object

Raises

ValueError – For unsupported keytypes

static generate_spki(derkey: bytes) str

Generate and return a pin-sha256 spki hpkp style pin for the provided public key.

OpenSSL equivalent command is:

openssl x509 -in example.com.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl base64

Parameters

derkey – The bytes representing the public key in DER format

Returns

A string of the SPKI pin

static generate_tlsa_record(derkey: bytes, tlsatype: str) str

Generate and return the data part of a TLSA record of the requested type.

TLSA record is generated from the DER formatted public key supplied. Returns an uppercase hex string.

Parameters
  • derkey – The bytes representing the public key in DER format

  • tlsatype – The TLSA type (like “310”)

Returns

String of the TLSA data

Raises

ValueError – If an unknown TLSA type is passed

get_certgrinderd_command(subcommand: List[str]) List[str]

Return the certgrinderd command to run.

Adds --log-level with the current self.conf["log-level"]. Also adds –acme-server-url if configured, and –preferred-chain.

Parameters

subcommand – The certgrinderd subcommand to run as a list, like [“get”, “ocsp”]

Returns

A list of the elements which make up the certgrinderd command

get_certificate(csr: Optional[cryptography.hazmat.backends.openssl.x509._CertificateSigningRequest] = None, stdout: Optional[bytes] = None) bool

Get a new certificate for self.domainset.

This methods gets a new certificate regardless of the status of any existing certificate. It is called by self.periodic() as needed. It can also be called by the get certificate subcommand.

Parameters
  • csr – The CSR to use instead of generating one

  • stdout – The stdout bytes to use instead of calling self.run_certgrinderd(csr)

Returns

False something goes wrong, True if all is well

static get_der_pubkey(keypair: Union[cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey]) bytes

Return the DER formatted publickey.

Parameters

keypair – The keypair which contains the public key

Returns

The bytes representing the DER formatted public key

get_filename(hostname: str) str

Calculate the hostname string to be used for filenames.

Files are named after the ascii idna representation of the first hostname in the list (which is also the CN in the subject of the CSR and certificate).

Max filename length on some platforms is 255 bytes, but a hostname could be up to 253 bytes (RFC 1035 section 2.3.4), and we need some room for the usage and keytype and extension, so we only use the last 230 bytes of the ascii idna representation of the hostname for the filename, leaving 25 bytes for metadata.

Parameters

domainset – The list of hostnames

Returns

The string to use in filenames

get_ocsp(certificate: Optional[cryptography.x509.base.Certificate] = None, issuers: List[cryptography.x509.base.Certificate] = [], stdout: Optional[bytes] = None) bool

The get ocsp subcommand method, called for each domainset by self.grind().

Parameters
  • certificate – The certificate to get OCSP response for (optional)

  • issuers – The list of issuer(s) of the certificate to get OCSP response for (optional)

  • stdout – The mock OCSP response to return instead of calling certgrinderd (optional, used for unit tests)

Returns

None

grind(args: argparse.Namespace) None

Loop over enabled keytypes and domainsets in self.conf["domain-list"] and call args.method for each.

load_certificates(path: str) List[cryptography.x509.base.Certificate]

Reads PEM certificate data from the path, parses the certificate(s), and returns them in a list.

Parameters

path – The path to read the PEM certificate(s) from

Returns

A list of cryptography.x509.Certificate objects

load_domainset(domainset: List[str], keytype: str) None

Prepare paths and create/load private key.

Parameters
  • domainset – The list of hostnames to load

  • keytype – The keytype to use, “rsa” or “ecdsa”.

Returns

None

static load_keypair(path: str) Union[cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey]

Load keypair bytes from disk, load key and return the object.

Fixes keypair permissions to 640 if they are not 640.

Parameters

path – The path to load the keypair from

Returns

The keypair object

static load_ocsp_response(path: str) cryptography.hazmat.backends.openssl.ocsp._OCSPResponse

Reads OCSP response in DER format from the path and returns the object.

Parameters

path – The path to read the OCSP response from

Returns

The OCSP response object

static lookup_tlsa_record(domain: str, port: int, protocol: str, tlsatype: Optional[str] = None, nameserver: str = '') Optional[List[str]]

Lookup TLSA records in DNS for the configured domain, port, and protocol.

Loop over any responses and look for the requested tlsatype. Return a list of results, optionally limited to the specified tlsatype, or None. Use system resolver unless nameserver is specified.

Parameters
  • domain – The service domain name (like mail.example.com)

  • port – The service port (like 443)

  • protocol – The service protocol (like tcp)

  • tlsatype – The TLSA type (like 312)

  • nameserver – The DNS server IP to use instead of system resolver (optional)

Returns

A list of records or None

classmethod output_spki(derkey: bytes) None

Get and print the spki pin for the supplied DER public key.

Parameters

derkey – The bytes representation of the DER formatted public key

Returns

None

classmethod output_tlsa_record(derkey: bytes, domain: str, port: int, protocol: str, tlsatype: str, warning: bool = False) None

Output the TLSA record for the given DER key, domain, port, protocol and tlsatype.

Call self.generate_tlsa() and output the result formatted as a DNS record

Parameters
  • derkey – The bytes representation the public key in DER format

  • domain – The service domain name (like mail.example.com)

  • port – The service port (like 443)

  • protocol – The service protocol (like tcp)

  • tlsatype – The TLSA type (like 312)

  • warning – Set True to output at level WARNING (default INFO)

Returns

None

static parse_certgrinderd_ocsp_output(certgrinderd_stdout: bytes) Optional[cryptography.hazmat.backends.openssl.ocsp._OCSPResponse]

Parse a DER encoded binary OCSP response as returned by Certgrinderd.

Parameters

certgrinderd_output – The bytes representing the OCSP response in DER format

Returns

cryptography.hazmat.backends.openssl.ocsp._OCSPResponse

static parse_certificate(certificate_bytes: bytes) Optional[cryptography.x509.base.Certificate]

Parse a bunch of bytes representing a PEM certificate and return.

Parameters

certificate_bytes – The PEM certificate

Returns

The parsed cryptography.x509.Certificate object or None

parse_certificate_chain(certificate_chain: bytes, csr: cryptography.hazmat.backends.openssl.x509._CertificateSigningRequest) Optional[List[cryptography.x509.base.Certificate]]

Split a PEM chain into a list of certificates.

Parameters
  • certificate_chain – The bytes representing the PEM formatted certificate chain

  • csr – The CSR this certificate was issued from

Returns

A list of certificates with the leaf certificate first, or None if an error happens

periodic() bool

The periodic method performs periodic maintenance tasks.

This method is called by the ‘periodic’ command, from cron or similar. It starts out by sleeping for a random period and then checks certificates and renews as needed.

run_certgrinderd(stdin: bytes, command: List[str], certgrinderd_stdout: Optional[bytes] = None, certgrinderd_stderr: Optional[bytes] = None) Optional[bytes]

Run the configured self.conf["certgrinderd"] command.

The stdin argument will be passed to stdin of the command. A CSR is needed for the “get certificate” certgrinderd command, and a certificate chain is needed for the “get ocsp” command.

Parameters
  • stdin – bytes representing CSR or cert chain to pass to the certgrinderd command

  • command – The certgrinderd command and subcommand to call

  • certgrinderd_stdout – Mocked certgrinderd stdout to use instead of calling the command

  • certgrinderd_stderr – Mocked certgrinderd stderr to use instead of calling the command

Returns

The bytes representing the stdout from the subprocess call

static run_post_renew_hook(hook: List[str]) bool

Run a specific post renew hook.

Parameters

hook – A list of string components of the command and arguments

Returns: True if exit code was 0, False otherwise.

run_post_renew_hooks() bool

Loops over configured post_renew_hooks and executables in post_renew_hooks_dir and runs them.

Returns

None

static save_certificate(certificate: cryptography.x509.base.Certificate, path: str, issuers: List[cryptography.x509.base.Certificate] = []) None

Save the PEM certificate to the path, optionally with an issuer chain.

Parameters
  • certificate – The certificate to save

  • path – The path to save the certificate in

  • issuer – The list of issuer certificates to write after the certificate (if any)

Returns

None

classmethod save_concat_certkey(keypair: Union[cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey], certificate: cryptography.x509.base.Certificate, issuers: List[cryptography.x509.base.Certificate], path: str) None

Create a single file with the private key, the cert and the issuer(s), in that order.

Parameters
  • keypair – The keypair to save in the concat file

  • certificate – The certificate to save in the concat file

  • issuers – The list of issuer(s) to save in the concat file

  • path – The path to save the concat file in

Returns

None

static save_csr(csr: cryptography.hazmat.backends.openssl.x509._CertificateSigningRequest, path: str) None

Save the PEM version of the CSR to the path.

chmods the file 644 after writing.

Parameters
  • csr – The CSR to be saved

  • path – The path to save the CSR to

Returns

None

static save_keypair(keypair: Union[cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey], path: str) None

Save keypair to disk.

Parameters
  • keypair – The keypair to save

  • path – The path to save the keypair in

Returns

None

Raises

ValueError – For unsupported keytypes

static save_ocsp_response(ocsp_response: cryptography.hazmat.backends.openssl.ocsp._OCSPResponse, path: str) None

Save the OCSP response to disk in DER format.

Parameters
  • ocsp_response – The OCSP response to save

  • path – The path to save in

Returns

None

show_certificate() None

The show certificate subcommand method, called for each domainset by self.grind().

Returns

None

show_ocsp() None

The show ocsp subcommand method, called for each domainset by self.grind().

Returns

None

show_paths() None

The show paths subcommand method, called for each domainset by self.grind().

Returns

None

show_spki() None

The show spki subcommand method, called for each domainset by self.grind().

Call self.output_spki() with the DER formatted public key and output the result.

Returns

None

show_tlsa() None

The ‘show tlsa’ subcommand method, called for each domainset by self.grind().

Returns

None

static split_pem_chain(pem_chain_bytes: bytes) List[bytes]

Split a PEM chain into a list of bytes of the individual PEM certificates.

Parameters

pem_chain_bytes – The bytes representing the PEM chain

Returns

A list of 0 or more bytes chunks representing each certificate

classmethod verify_tlsa_record(derkey: bytes, domain: str, port: int, protocol: str, tlsatype: str, nameserver: str = '') bool

Check the TLSA records for the port/protocol/domain and DER key in the DNS.

Output the info needed to fix things when missing records are found.

Parameters
  • derkey – The bytes representation the public key in DER format

  • domain – The service domain name (like mail.example.com)

  • port – The service port (like 443)

  • protocol – The service protocol (like tcp)

  • tlsatype – The TLSA type (like 312)

  • nameserver – The DNS server IP to use instead of system resolver (optional)

Returns

True if all is well, False if one or more problems are found