Welcome to Certgrinder 0.18.0-dev Documentation!
Introduction
Certgrinder is a client/server system written in Python to handle Letsencrypt certificate issuing on a central host (the Certgrinder server), rather than on the machines which need the certificates (the Certgrinder clients). This is accomplished by redirecting the LetsEncrypt challenges to the Certgrinder server. For DNS-01
challenges this is done with a CNAME record
and for HTTP-01
challenges it is done with a HTTP 301 redirect
.
To get a certificate the Certgrinder client calls the Certgrinder server (typically over SSH
) with a CSR
on stdin
and (if all goes well) get a signed certificate in return on stdout
.
Certgrinder support both RSA
and ECDSA
keys and certificates, and defaults to getting both kinds.
Advantages
The approach with a central host serving all challenges simplifies getting certificates for stuff like loadbalanced or anycast services, where it can be impossible to predict which cluster node the LetsEncrypt challenge checker will hit when using
HTTP-01
.Using
DNS-01
with a separate delegated zone dedicated to serve the challenges is safer than opening up dynamic updates of your primary zone(s) with your provider.Migrating services to new infrastructure becomes simpler because the new infrastructure can get real certificates before changing DNS to point to the new infrastructure.
Certgrinder makes it trivial to get certificates for infrastructure behind firewalls or even on networks with no Internet connection. As long as the Certgrinder client can reach the Certgrinder server it is possible to use
DNS-01
to issue certificates for the client.Certgrinder does not rotate the RSA/ECDSA keypair on each certificate renewal, which makes
TLSA
and similar public key pinning easy. The Certgrinder client can output and check suchTLSA
andSPKI
pins for the keypairs it manages, as well as checking correctness ofTLSA
records in the DNS.Certgrinder supports fetching OCSP responses via the Certgrinder server. Having Certgrinder fetch the OCSP response makes it possible to configure OCSP stapling without relying on the various TLS servers own OCSP-fetching implementation. It also means that OCSP stapling can be done on servers behind strict firewalls or with no Internet connection, as the communication with CA is done via the Certgrinder server.
Certgrinder supports alternate chain selection and understands the longer chain used by LetsEncrypt since May 2021.
Terminology
The central host with the LetsEncrypt signing stack is called the “Certgrinder server”. The individual servers (the ones that need the certificates) are called “Certgrinder clients”. These match the two Python packages certgrinderd
and certgrinder
, respectively.
Challenges
Certgrinder supports the two ACME
challenge types DNS-01
and HTTP-01
. Certgrinderd will try both challenge types (DNS-01
first, then HTTP-01
), so your clients can use whatever is the best fit. Usually my webservers use HTTP-01
and everything else uses DNS-01
. YMMV.
The following sections describes how Certgrinder handles these two challenge types.
DNS-01
With the DNS-01
challenge type the Certgrinder server serves the challenge over DNS, which means you need to run an authoritative DNS server on the Certgrinder server. You can also use an external DNS server or provider, as long as you can make a hook script to add and delete records from the Certgrinder server as needed.
To prepare the Certgrinder server for serving DNS-01
challenges you first need a zone to serve the challenges. Invent and delegate a new zone (like acme.example.com
) to your Certgrinder server or DNS provider. Use an NS
record to delegate, or follow your providers instructions. The zone name then needs to be configured in certgrinderd.conf
. This zone will be used to serve all DNS-01
challenges, it will be updated automatically by certgrinderd
as needed.
The default manual-auth-hook
script is made for the bind
DNS server. It creates and deletes the DNS record using nsupdate
and an rndc.key
file in the path /usr/local/etc/namedb/rndc.key
. If you want to use other paths or another script for a local or external DNS provider you can configure it in certgrinderd.conf
. The same goes for the cleanup script manual-cleanup-hook
.
- Note:
Since
certbot
is responsible for calling the hooks they are run as root, just likecertbot
.
This concludes the server part of the DNS-01
configuration.
A client wanting a certificate must now create a CNAME
record called _acme-challenge.${DOMAIN}
pointing at ${DOMAIN}.${ACMEZONE}
for each domain in the CSR
.
For example, to get a certificate for smtp.example.org
you would create _acme-challenge.smtp.example.org CNAME smtp.example.org.acme.example.com
if your acme challenge zone was acme.example.com
. certgrinderd will create the smtp.example.org.acme.example.com TXT
record containing the validation string, and delete it afterwards.
HTTP-01
With the HTTP-01
challenge type the Certgrinder server serves the challenge over HTTP, which means it needs a webserver somewhere to serve the challenges. It can be on the Certgrinder server or it can be an external webserver or provider, as long as you can make a hook script to add and delete files in the webroot from the Certgrinder server as needed. The hostname of this webserver will be the target of the Certgrinder clients HTTP redirects.
Each Certgrinder client then implements an HTTP redirect from /.well-known/acme-challenge/
to the Certgrinder server like so (nginx syntax):
location /.well-known/acme-challenge/ {
return 301 http://acme.example.com$request_uri;
}
When requesting a certificate the Certgrinder server receives the challenge and path from Certbot (which in turn gets it from LetsEncrypt of course). The challenge is then passed to the manual-auth-hook
script which writes it in the webroot under /.well-known/acme-challenge/
.
In another datacenter somewhere LetsEncrypts challenge checker then loops over the domains in the CSR
and does a HTTP request to each for /.well-known/acme-challenge/${path}
and expects the response to contain the challenge.
Certgrinderd
The Certgrinder server certgrinderd
takes care of getting certificates and OCSP responses on behalf of the calling clients. certgrinderd
doesn’t run always like a daemon, so it never acts on its own. It only does something when a Certgrinder client runs it, usually over SSH.
The following sections explain the steps you need to setup a Certgrinder server.
Install Certgrinder Server
Create a VM or Jail or Docker thing or whatever somewhere. This will be your Certgrinder server. Give it a proper public hostname like certgrinder.example.com
. You can use real proper IP addresses or port forwarding, whichever you prefer. The relevant ports are TCP/22
(so the Certgrinder clients can reach the Certgrinder server), TCP/53
and UDP/53
if you want to serve DNS-01
challenges locally, and TCP/80
if you use HTTP-01
challenges locally.
Create DNS records for the new hostname (A+AAAA, and an SSHFP record wouldn’t hurt) and you should be ready to begin the install.
The hostname of your Certgrinder server will be the hostname your Certgrinder clients use to SSH into (if you use SSH), and also the hostname you use to serve HTTP challenges locally (if you use HTTP-01
challenges).
Create User
Create a dedicated user to run the Certgrinder server, usually the username is just certgrinderd
. The user needs sudo
access to run the certbot
binary, and to set a couple of environment variables. This works:
certgrinderd ALL=(ALL) NOPASSWD: /usr/local/bin/certbot
Defaults env_keep += "ACMEZONE WEBROOT"
Install certgrinderd
You can install certgrinderd
from pip with pip install certgrinderd
. It will pull in the dependencies it needs automatically. Create a venv for it if you don’t want to pollute the global Python env.
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 Github the following may help:
pip install "git+https://github.com/tykling/certgrinder/#egg=certgrinderd&subdirectory=server"
The Certgrinder server needs to be reachable from the outside world on port 53/80 if you plan to serve DNS/HTTP challenges locally. It also needs to be accessible over SSH from all your Certgrinder clients if you plan to use SSH.
Configuration
Configuration of certgrinderd
can be done using command-line options, or a configuration file, or a combination of the two.
The certgrinderd
configuration file is in YAML format. An example config named certgrinderd.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:
- acme-email
The email to use for the ACME account creation. Only required for the first run.
Default:
None
- acme-server-url
The URL for the ACME server.
Default:
https://acme-v02.api.letsencrypt.org/directory
- acme-zone
The DNS zone to pass to auth-hook script as environment variable ACMEZONE. Leave this unset to disable DNS-01 challenges.
Default:
None
- auth-hook
The script to run to prepare challenges before running Certbot.
Default:
manual-auth-hook.sh
- certbot-command
The Certbot command to run between the auth hook and the cleanup hook.
Default:
/usr/local/bin/sudo /usr/local/bin/certbot
- certbot-config-dir
The path to pass to Certbot as
--config-dir
.Default:
None
- certbot-logs-dir
The path to pass to Certbot as
--logs-dir
.Default:
None
- certbot-work-dir
The path to pass to Certbot as
--logs-dir
.Default:
None
- cleanup-hook
The script to run to cleanup challenges after running Certbot.
Default:
manual-cleanup-hook.sh
- config-file
The path to the configuration file. The file is in YAML format.
Default:
None
- debug
Enables debug mode. This is the same as setting –log-level to DEBUG. Outputs lots info about the internal workings of certgrinderd.
Default:
False
- log-level
Sets the verbosity level for console and syslog logging. One of DEBUG, INFO, WARNING, ERROR, CRITICAL.
Default:
INFO
- pid-dir
The directory to place the certgrinderd PID file in.
Default:
/tmp
- skip-acme-server-cert-verify
Set to skip verification of the ACME servers TLS certificate. Used for testing, do not use in real world.
Default:
False
- staging
Enable staging mode. To make Certbot use the LetsEncrypt staging servers.
Default:
False
- syslog-facility
Set this and syslog-socket to enable logging to syslog. Must be a value supported by
logging.handlers.SysLogHandler
likeLOG_USER
orLOG_LOCAL0
.Default:
None
- syslog-socket
Set this and syslog-facility to enable logging to syslog.
Default:
None
- temp-dir
Set this to the directory to use for temporary files (CSR and certificates). Directory should be owned by the user running
certgrinderd
. A directory will be created and deleted inside this temp-dir for each run. Leave blank to create one automatically.Default:
None
- web-root
The path to pass to the auth-hook script as environment variable WEBROOT. Leave this blank to disable HTTP-01 challenges.
Default:
None
Finally the permitted domains for the current client must be specified as an environment variable (see next section).
Restricting Client Hostnames
To determine whether a Certgrinder client is authorised to get a certificate for a given list of hostnames certgrinderd
checks the environment variable named CERTGRINDERD_DOMAINSETS
which must contain a semicolon-separated list of comma-separated lists of hostnames permitted for the current client.
For example, if the Certgrinder client was a webserver with two vhosts, one with the name example.net
and another vhost with the two names example.com
and www.example.com
. In this case the environment variable CERTGRINDERD_DOMAINSETS="example.net;example.com,www.example.com"
would permit the client to get the two certificates it needs, and nothing else.
The list of hostnames is case insensitive. IDN names can be in either IDNA or unicode format, meaning xn--plse-gra.example
and pølse.example
will both work. The order of the hostnames in the list does not matter.
Configure SSH Access
Usually Certgrinder clients connect to the Certgrinder server using SSH, but other connection methods can be used if needed. The rest of this section is about configuring SSH access for clients.
Each Certgrinder client must generate an SSH key which is to be added to ~/.ssh/authorized_keys
on the Certgrinder server. Each entry must be restricted with:
A
from=
specifying the IP the Certgrinder client connects from (optional but recommended).An
environment=
restricting which names it may ask for, see above (required).command=
to restrict the command it can run (optional but recommended). Remember$SSH_ORIGINAL_COMMAND
socertgrinder
can setcertgrinderd
command-line arguments.The
restrict
keyword to limit tunneling and forwarding and such (optional but recommended). Therestrict
option was added to OpenSSH in version 7.4, it might not be available everywhere.
Something like this works:
from="2001:DB8::15",environment="CERTGRINDERD_DOMAINSETS=example.com,www.example.com;example.net",command="/path/to/certgrinderd $SSH_ORIGINAL_COMMAND",restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOegnR+qnK2FEoaSrVwHgCIxjFkVEbW4VO31/Hd2mAwk ansible-generated on webproxy2.example.com
To make the environment=
foo work the option PermitUserEnvironment=CERTGRINDERD_DOMAINSETS
needs to be added to sshd_config
.
Auth and Cleanup Hooks
The configured auth-hook
and cleanup-hook
scripts can be adapted as needed to update whatever local or remote web- or DNS-server you decide to use to serve challenges.
Both scripts get the same environment variables to work with:
- $CERTBOT_DOMAIN
The domain being authenticated, like www.example.com
- $CERTBOT_VALIDATION
The validation string (the secret which LE looks for)
- $CERTBOT_TOKEN
The filename containing the secret (only relevant for HTTP-01)
- $ACMEZONE
The DNS zone used for challenges (only relevant for DNS-01)
- $WEBROOT
The path to the webroot used for challenges (only relevant for HTTP-01)
Both scripts must be able to handle the challenge type(s) you use. The same script will be called first for DNS-01 (if enabled), then for HTTP-01 (if enabled).
Testing
When the server has been configured with hooks you can test from a client using just SSH and a manually generated CSR, with something like: cat mail4.example.com.csr | ssh certgrinderd@certgrinder.example.org -T -- --staging get certificate
where -T
is to prevent SSH from allocating a TTY on the server, --
is to mark the end of the SSH args, and --staging
is to make certgrinderd
use the LetsEncrypt staging servers. If all goes well it should output some logging and a certificate chain.
Command Line Usage
certgrinderd version 0.18.0-dev. See the manpage certgrinderd(8) or ReadTheDocs for more info.
usage: certgrinderd [-h] [--acme-email ACME-EMAIL]
[--acme-server-url ACME-SERVER-URL] [-z ACME-ZONE]
[-A AUTH-HOOK] [--certbot-command CERTBOT-COMMAND]
[--certbot-config-dir CERTBOT-CONFIG-DIR]
[--certbot-logs-dir CERTBOT-LOGS-DIR]
[--certbot-work-dir CERTBOT-WORK-DIR] [-C CLEANUP-HOOK]
[-c CONFIG-FILE] [--certificate-file CERTIFICATE-FILE]
[--csr-file CSR-FILE] [-d]
[-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [-p PID-DIR]
[--preferred-chain PREFERRED-CHAIN]
[--skip-acme-server-cert-verify] [-s]
[--syslog-facility SYSLOG-FACILITY]
[--syslog-socket SYSLOG-SOCKET] [-t TEMP-DIR]
[-w WEB-ROOT]
{get,show,help,ping} ...
Positional Arguments
- command
Possible choices: get, show, help, ping
Command (required)
Named Arguments
- --acme-email
The email for the ACME account.
- --acme-server-url
The url for the ACME server to use.
- -z, --acme-zone
The DNS zone to pass to the auth hook script as env. var. ACMEZONE. For DNS-01 challenges.
- -A, --auth-hook
The hook script to call to prepare auth challenges before calling Certbot
- --certbot-command
The Certbot command to call between auth hook and cleanup hook
- --certbot-config-dir
The path to pass to Certbot as –config-dir
- --certbot-logs-dir
The path to pass to Certbot as –logs-dir
- --certbot-work-dir
The path to pass to Certbot as –work-dir
- -C, --cleanup-hook
The hook script to call to clean up auth challenges after calling Certbot
- -c, --config-file
The path to the certgrinderd config file to use, in YML format.
- --certificate-file
The path to the PEM formatted certificate chain file to use instead of getting it from stdin.
- --csr-file
The path to the PEM formatted CSR file to use instead of getting it from stdin.
- -d, --debug
Debug mode. Equal to setting –log-level=DEBUG.
- -l, --log-level
Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL
Logging level. One of DEBUG, INFO, WARNING, ERROR, CRITICAL. Defaults to INFO.
- -p, --pid-dir
The directory to store the PID file in
- --preferred-chain
The preferred chain to use. Adds –preferred-chain to the Certbot command. Use to pick preferred signing chain when alternatives are available. Replace spaces with underscores in the chain name, so DST_Root_CA_X3 or ISRG_Root_X1 for prod or Fake_LE_Root_X1 or Fake_LE_Root_X2 for staging.
- --skip-acme-server-cert-verify
Do not verify the ACME servers certificate
- -s, --staging
Staging mode. Equal to setting –acme-server-url https://acme-staging-v02.api.letsencrypt.org/directory
- --syslog-facility
The facility to use for syslog messages
- --syslog-socket
The socket to use for syslog messages
- -t, --temp-dir
The directory to store temporary files in
- -w, --web-root
The path to pass to the auth hook script as env WEBROOT to use for HTTP-01 challenges.
Sub-commands:
get
Use the “get” command to get certificates or OCSP responses
certgrinderd 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:
Get a new certificate. Requires a CSR.
certgrinderd get certificate [-h]
Get an OCSP response for the provided certificate.
certgrinderd get ocsp [-h]
show
Use the “show” command to show configuration, CSR info, or certificate info.
certgrinderd show [-h] {certificate,csr,configuration} ...
Positional Arguments
- subcommand
Possible choices: certificate, csr, configuration
Specify what to show using one of the available show sub-commands
Sub-commands:
Tell certgrinder to output information about the provided certificate.
certgrinderd show certificate [-h]
Tell certgrinder to output information about the provided CSR.
certgrinderd show csr [-h]
Tell certgrinder to output the current configuration
certgrinderd show configuration [-h]
help
The “help” command just outputs the usage help
certgrinderd help [-h]
ping
The “ping” command is used by the certgrinder client to verify connectivity to the server. It just outputs the word “pong” to stdout.
certgrinderd ping [-h]
Class Methods
- class certgrinderd.Certgrinderd(userconfig: Optional[Dict[str, Optional[Union[str, bool]]]] = None)
The Certgrinderd server class.
- __init__(userconfig: Optional[Dict[str, Optional[Union[str, bool]]]] = None) None
Merge userconfig with defaults and configure logging.
- Parameters
userconfig – A dict of configuration to merge with default config
- Returns
None
- static check_csr(csr: cryptography.hazmat.backends.openssl.x509._CertificateSigningRequest) bool
Check that this CSR is valid, all things considered.
First check that the CSR has exactly one
CommonName
, and that that CN is also present in the list ofSubjectAltNames
.Then make sure that the environment var
CERTGRINDERD_DOMAINSETS
exists and contains all the names from the CSR in one of the domainsets.- Parameters
csr – The CSR object
- Returns
True if the CSR is OK, False otherwise
- classmethod check_ocsp_response(ocsp_request: cryptography.hazmat.backends.openssl.ocsp._OCSPRequest, ocsp_response: cryptography.hazmat.backends.openssl.ocsp._OCSPResponse, certificate: cryptography.x509.base.Certificate, issuer: cryptography.x509.base.Certificate) bool
Check that the OCSP response is valid for the OCSP request and cert/issuer.
Return True if the OCSP response is good, regardless of the certificate revocation status. Implements all the checks in RFC2560 3.2.
- Parameters
ocsp_request – The OCSP request object to check
ocsp_response – The OCSP response object to check
certificate – The certificate the OCSP request is for
issuer – The issuer of the certificate
- Returns
True if the OCSP response is valid, False if not
- static check_ocsp_response_issuer(ocsp_request: cryptography.hazmat.backends.openssl.ocsp._OCSPRequest, ocsp_response: cryptography.hazmat.backends.openssl.ocsp._OCSPResponse) bool
Check that the response matches the request.
- Parameters
ocsp_request – The OCSP request object
ocsp_response – The OCSP response object
- Returns
Boolean - True if all is well, False if a problem was found
- classmethod check_ocsp_response_signature(ocsp_response: cryptography.hazmat.backends.openssl.ocsp._OCSPResponse, issuers: List[cryptography.x509.base.Certificate]) bool
Check the signature of the OCSP response.
- Parameters
ocsp_response – The OCSP response to check
issuers – A list of issuer(s)
- Returns
Boolean - True if all is well, False if a problem was found
- static check_ocsp_response_timing(ocsp_response: cryptography.hazmat.backends.openssl.ocsp._OCSPResponse) bool
Check the timestamps of the OCSP response.
- Parameters
ocsp_response – The OCSP response object to check
- Returns
Boolean - True if all is well, False if a problem was found
- classmethod create_ocsp_request(certificate: cryptography.x509.base.Certificate, issuer: cryptography.x509.base.Certificate) cryptography.hazmat.backends.openssl.ocsp._OCSPRequest
Create and return an OCSP request based on the cert+issuer.
- Parameters
certificate – The certificate to create an OCSP request for
issuer – The issuer of the certificate
- Returns
The OCSP request
- get_certbot_command(challengetype: str, csrpath: str, fullchainpath: str, certpath: str, chainpath: str) List[str]
Put the certbot command together.
Start with
self.conf["certbot-command"]
and append all the needed options.Optionally add
--email
and a bunch of certbot settings as needed.- Parameters
challengetype – The type of challenge,
dns
orhttp
csrpath – The path to the CSR
fullchainpath – The path to save the certificate+issuer
certpath – The path to save the certificate (without issuer)
chainpath – The path to save the issuer (without certificate)
- Returns
The certbot command as a list
- get_certificate(csrpath: str) None
Get a cert using
DNS-01
orHTTP-01
by callingself.run_certbot()
for each.If
self.conf["acme-zone"]
is set thenDNS-01
is attempted. Return if it results in a new certificate.If
self.conf["web-root"]
is set thenHTTP-01
is attempted. Return if it results in a new certificate.If there is still no certificate log an error and return anyway.
- Parameters
csrpath – The path to the CSR
- Returns
None
- get_certificate_command() None
This method is called when the get certificate subcommand is used.
- Parameters
None –
- Returns
None
- get_ocsp_command() None
This method is called when the get ocsp subcommand is used.
It simply prints the DER formatted OCSP response to stdout if we get one.
- Parameters
None –
- Returns
None
- get_ocsp_response(certpath: Optional[str]) cryptography.hazmat.backends.openssl.ocsp._OCSPResponse
Parse certificate, get and return OCSP response.
- Parameters
certpath – The path of the certificate chain to get OCSP response for (optional)
- Returns
The OCSPRequest object
- classmethod parse_certificate(certificate_bytes: bytes) cryptography.x509.base.Certificate
Parse and return individual certificate, or calls sys.exit(1) if something goes wrong.
- Parameters
certificate_bytes – A chunk of bytes representing a PEM certificate
- Returns
A cryptography.x509.Certificate object.
- classmethod parse_certificate_chain(certpath: Optional[str], expected_length: Optional[int] = None) List[cryptography.x509.base.Certificate]
Parse certificate chain from path or stdin.
- Parameters
certpath – The path of the certificate chain to parse (optional), chainbytes are taken from stdin if not provided.
expected_length – The number of certificates to expect. Optional.
- Returns
A list of cryptography.x509.Certificate objects in the order they appear in the input.
- static parse_csr(csrstring: str = '') cryptography.hazmat.backends.openssl.x509._CertificateSigningRequest
Parse CSR with cryptography.x509.load_pem_x509_csr(), return CSR object.
Takes the CSR data from
sys.stdin
if thecsrstring
argument is empty.- Parameters
csrstring – The PEM formatted CSR as a string (optional)
- Returns
The CSR object
- static ping_command() None
Reply to the ping command by outputting the string ‘pong’ to stdout.
Args: None Returns: None
- process_csr(csrpath: str = '') None
Load the CSR, use it to get a certificate, and cleanup.
Calls
self.parse_csr()
followed byself.check_csr()
, and then exists if any problems are found with the CSR.Then
self.get_certificate()
is called, which in turn calls Certbot, which writes the certificate to stdout.Finally the CSR is deleted.
- Parameters
None –
- Returns
None
- run_certbot(command: List[str], env: Dict[str, str], fullchainpath: str) bool
Call certbot, check exitcode, output cert, return bool success.
- Parameters
command – A list of certbot command elements
env – A dictionary of the environment to pass to subprocess.run()
fullchainpath – The path to read the certificate+chain from after Certbot runs
- Returns
True if Certbot command exitcode was 0, False otherwise
- static save_csr(csr: cryptography.hazmat.backends.openssl.x509._CertificateSigningRequest, path: str) None
Save the CSR object to the path in PEM format.
- Parameters
csr – The CSR object
path – The path to save it in
- 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
- static verify_signature(pubkey: Union[cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey, cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey], signature: bytes, payload: bytes, hashalgo: cryptography.hazmat.primitives.hashes.HashAlgorithm) bool
Verify a signature on a payload using the provided public key and hash algorithm.
Supports RSA and EC public keys. Assumes PKCS1v15 padding for RSA keys.
- Parameters
pubkey – The public key
signature – The bytes representing the signature
payload – The bytes representing the signed data
hashalgo – The hashing algorithm used for the signature
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 byDST Root CA X3
.For staging it means using
Fake LE Root X2
(1 intermediate) instead of the usualFake LE Root X1
(2 intermediates).Default:
False
- certgrinderd
The command to run as
certgrinderd
. Usually this will be something likessh certgrinderd@certgrinder.example.com -T
, possibly also with a--config-file
forcertgrinderd
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
andnext_update
that must have passed before an OCSP response is considered too old. As of January 2021 LetsEncrypt has 7 days betweenproduced_at
andnext_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()
. Setpost-renew-hooks-dir-runner
if something likesudo
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 fileshook1
andhook2
andpost-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 thecertgrinderd
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
likeLOG_USER
orLOG_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 usingshow tlsa
orcheck tlsa
subcommands.Default:
None
- tlsa-protocol
Set this to the protocol (like
tcp
) when usingshow tlsa
orcheck 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
or312
. 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
andnext_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:
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]
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]
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]
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
- 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:
Tell certgrinder to get new certificate(s), regardless of their current state. Rarely needed, use ‘periodic’ command instead.
certgrinder get certificate [-h]
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:
Tell certgrinder to output information about certificates.
certgrinder show certificate [-h]
Tell certgrinder to output the current configuration
certgrinder show configuration [-h]
Tell certgrinder to output the paths used
certgrinder show paths [-h]
Tell certgrinder to output information about OCSP responses.
certgrinder show ocsp [-h]
Tell certgrinder to generate and print the pin-sha256 spki pins for the public keys it manages.
certgrinder show spki [-h]
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
- 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 byself.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 currentself.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 theget 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 byself.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
(defaultINFO
)
- 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 byself.grind()
.- Returns
None
- show_ocsp() None
The
show ocsp
subcommand method, called for each domainset byself.grind()
.- Returns
None
- show_paths() None
The
show paths
subcommand method, called for each domainset byself.grind()
.- Returns
None
- show_spki() None
The
show spki
subcommand method, called for each domainset byself.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
Certgrinderd Change Log
This is the changelog for certgrinderd
. The latest version of this file
can always be found on
Github
All notable changes to certgrinderd
will be documented in this file.
This project adheres to Semantic Versioning.
v0.18.0 (unreleased)
No changes
v0.17.2 (27-nov-2021)
Changed
Include Python 3.10 support
Update setup.py to include license_file
Update description in setup.py
v0.17.1 (21-nov-2021)
Changed
Update dependency PyYAML==5.4.1 to PyYAML==6.0
Update dependency certbot==1.15.0 to certbot==1.21.0
Update dependency requests==2.25.1 to requests==2.26.0
Update a bunch of development dependencies
Switch to Github Actions instead of Travis CI
v0.17.0 (21-may-2021)
No changes since v0.17.0-rc3
v0.17.0-rc3 (21-may-2021)
Fixed
Replace underscores with spaces in the preferred-chain name
Do not check number of intermediates when getting OCSP.
v0.17.0-rc2 (20-may-2021)
Fixed
Replace spaces with underscores in chain names to get around quoting woes in the SSH commands
v0.17.0-rc1 (20-may-2021)
Added
New config and command-line option
preferred-chain
can be used to ask the ACME server (LetsEncrypt) to sign with the specified chain. This is used by the certgrinder clientsalternate-chain
option to ask for primary or alternate chain for staging or prod. The value of the option is passed directly to Certbot as--preferred-chain
.
Changed
Refactor a bunch of code to support the new two-intermediates chain from LetsEncrypt
Upgrade dependencies
v0.16.0 (18-Jan-2021)
Added
Certgrinderd now keeps a pidfile while running to prevent running multiple times simultaneously.
New
ping
command used by thecertgrinder
commandcheck connection
to check connection to thecertgrinderd
server without doing anything else.Python3.9 support
Fixed
IDN domain handling now works again
v0.15.1 (29-Sep-2020)
No changes
v0.15.0 (29-Sep-2020)
No changes
v0.15.0-beta2 (28-Sep-2020)
No changes
v0.15.0-beta1 (28-Sep-2020)
Added
Enabled
check-spelling
Github action and fixed a bunch of misspelled words all over.
Fixed
Removed unused
--rsa-key-size
arg from certbot commandFix wrong requirements line for pre-commit (remove extra equal sign)
Updated all dependencies in requirements.txt, and switch to pinning deps with == rather than >= so dependabot on github can do its thing
v0.14.2 (13-Sep-2020)
No changes
v0.14.1 (13-Sep-2020)
Changed
Change
intermediate
toissuer
in the code and tests.
Fixed
Fix a wrong error message in an assert in
test_parse_certificate_chain_path()
v0.14.0 (29-Aug-2020)
Changed
Change log message to
INFO
when getting a new OCSP response
v0.14.0-beta2 (29-Aug-2020)
No changes
v0.14.0-beta1 (29-Aug-2020)
Added
Introduce commands and subcommands (like “get certificate” or “get ocsp”)
Add OCSP response fetching and verifying functionality
Add requests to requirements (for getting OCSP responses)
Tests for the new functionality
Changed
Refactor code to fit the commands/subcommand structure
Log certbot stderr at level ERROR
Change some default config from None to “” to keep it as str
v0.13.2 (11-Jul-2020)
Added
Manpage to MANIFEST.in to include it in the distribution
v0.13.1 (7-Jul-2020)
Changed
Specify python3.7 and 3.8 as classifiers in setup.py
v0.13.0 (7-Jul-2020)
No changes
v0.13.0-rc1 (1-Jul-2020)
Added
Information about $SSH_ORIGINAL_COMMAND to docs
Changed
Show current log-level setting in first log message
v0.13.0-beta2 (29-Jun-2020)
Added
Dev requirements now has
sphinx-rtd-theme
which is the theme used on ReadTheDocs, somake html
indocs/
now produces the same-ish output.Dev requirements now include
sphinx-argparse
used for generating automatic usage documentation.Short command-line options for a bunch of things.
Manpage certgrinderd.8
Unittests for a few Certgrinderd() methods
Changed
Move CHANGELOG.md to rst format and into
docs/
Split certbot-command related stuff into new methods get_certbot_command() and run_certbot()
Split creating the argparse object into a separate function to assist sphinx-argparse
Test suite now covers 100% of certgrinderd.py
Fixed
Only try challenge types if we have the needed info (acme-zone for DNS-01, www-root for HTTP-01)
v0.13.0-beta1 (7-May-2020)
No changes
v0.13.0-alpha8 (6-May-2020)
Changed
Changed logformat to prefix messages with certgrinderd: and Certgrinderd. instead of nothing and %(name)s, making it more clear which messages are from certgrinder and which are from certgrinderd.
v0.13.0-alpha7 (6-May-2020)
No changes
v0.13.0-alpha6 (6-May-2020)
Changed
certgrinderd
now creates a temporary directory for temporary CSR and CRT files per run. The directory and contents is at the end of each run. If –temp-dir is configured the temporary directory is created inside the path specified.
v0.13.0-alpha5 (6-May-2020)
Added
-f and -S short options for –config-file and –staging
MANIFEST.in file to include sample config and hook scripts
v0.13.0-alpha4 (5-May-2020)
Added
New –log-level option to set logging verbosity. Must be one of DEBUG, INFO, WARNING, ERROR, CRITICAL, corresponding to the levels in the Python logging framework.
A lot of new documentation about
certgrinderd
Command-line options for everything
Changed
Configuration file and command-line options aligned so everything is configurable both places.
v0.13.0-alpha3 (5-May-2020)
Added
Add missing PyYAML dependency in setup.py
Changed
Fix so certgrinderd.conf certbot_commands with spaces in them work as expected
v0.13.0-alpha2 (4-May-2020)
Added
Install
certgrinderd
binary using entry_points in setup.py
Changed
Move CSR loading and testing to class methods in the Certgrinderd class
Wrap remaining script initialisation in a main() function to support entry_points in setup.py better
v0.13.0-alpha (4-May-2020)
Added
Create Python package
certgrinderd
for the Certgrinder server, publish on pypiAdd isort to pre-commit so imports are kept neat
Tox and pytest and basic testsuite using Pebble as a mock ACME server
Travis and codecov.io integration
Changed
Move client files into client/ and server files into server/, each with their own CHANGELOG.md
Rename server from csrgrinder to certgrinderd
Rewrite server in Python
Certgrinder Change Log
This is the changelog for certgrinder
. The latest version of this file
can always be found on
Github
All notable changes to certgrinder
will be documented in this file.
This project adheres to Semantic Versioning.
v0.18.0 (unreleased)
No changes
v0.17.2 (27-nov-2021)
Changed
Include Python 3.10 support
Update setup.py to include license_file
Update description in setup.py
v0.17.1 (21-nov-2021)
Changed
Update dependency PyYAML==5.4.1 to PyYAML==6.0
Cryptography 35.0.0 is incompatible with Certgrinder v0.17.x so the Cryptography dependency has been pinned to a version lower than <35 in setup.py. Next version of Certgrinder will support cryptography v35.0.0 and newer.
Update a bunch of development dependencies
Switch to Github Actions instead of Travis CI
v0.17.0 (21-may-2021)
No changes since v0.17.0-rc3
v0.17.0-rc3 (21-may-2021)
No changes since v0.17.0-rc2
v0.17.0-rc2 (20-may-2021)
Fixed
Replace spaces with underscores in chain names to get around quoting woes in the SSH commands
v0.17.0-rc1 (20-may-2021)
Added
New config option
alternate-chain
to tell certgrinderd to tell Certbot to tell LetsEncrypt to use the alternate chain. Sets the certgrinderd optionpreferred-chain
to the appropriate value accordingly.
Fixed
Support the new longer chain from LetsEncrypt (with two intermediates).
Use
shlex
to parse certgrinderd command instead of just splitting on spaces
Changed
Refactor a bunch of code to deal with multiple intermediates
Upgrade dependencies
v0.16.0 (18-Jan-2021)
Added
New config option
ocsp-renew-threshold-percent
to specify the amount of time in percent which must have passed before an OCSP response is considered too old. The new option defaults to 50% which matches when LetsEncrypt currently issues new OCSP responses, which is after half the time between produced_at and next_update has passed.Certgrinder now keeps a pidfile while running to prevent running multiple times simultaneously.
New
check connection
command to check connection to thecertgrinderd
server without doing anything else.New config options
post-renew-hooks-dir
andpost-renew-hooks-dir-runner
. The former can be used to specify a path to a directory containing executables to run after a certificate or OCSP response has been renewed. The latter can be used to specify a runner likesudo
to be used to run all the hooks. The existingpost-renew-hooks
setting will continue to work as expected.Python3.9 support
Removed
Config option
ocsp-renew-threshold-seconds
was removed and replaced withocsp-renew-threshold-percent
.
Fixed
Show keytype in
show ocsp
outputThe new
ocsp-renew-threshold-percent
code and default setting eliminates redundant OCSP response fetchingIDN domain handling now works again
Changed
Better logging when running post renew hooks - exit code is always logged, and the time spent running each hook is now logged.
v0.15.1 (29-Sep-2020)
Fixed
Check OCSP response age and get a new one when needed
Added
Configuration option
ocsp-renew-threshold-seconds
- defaults to 86400.
v0.15.0 (29-Sep-2020)
Changed
Change output a bit for the
show tlsa
subcommand
Fixed
The
show tlsa
command did not work due to type mismatch triggering an assertShow keytype in the
show certificate
output
v0.15.0-beta2 (28-Sep-2020)
Changed
Check if files exist in the
show paths
subcommand.
v0.15.0-beta1 (28-Sep-2020)
Added
Enabled ECDSA keys and certificates. Default to getting both RSA and ECDSA certificates. Control which keytypes are enabled with the new
key-type-list
configuration option. Curve for ECDSA is SECP384R1, this might be made configurable later.Added
show paths
subcommand to output the various filepaths used.Enabled
check-spelling
Github action and fixed a bunch of misspelled words all over.
Changed
Changed filenames of keys and certificates. Run the following commands to rename existing RSA files from pre 0.15 installs:
The keypair:
mv example.com.key example.com-keypair.rsa.key
The CSR:
mv example.com.csr example.com-request.rsa.csr
The certificate chain:
mv example.com.crt example.com-chain.rsa.crt
The certificate:
mv example.com-certonly.crt example.com-certificate.rsa.crt
The concat key and chain:
mv example.com-concat.pem example.com-concat.rsa.pem
The issuer certificate:
mv example.com-issuer.crt example.com-issuer.rsa.crt
The OCSP response:
mv example.com.ocsp example.com-response.rsa.ocsp
In other words: - All files got the keytype (always
rsa
for pre-0.15 files) inserted just before the extension, so.crt
becomes.rsa.crt
and.key
becomes.rsa.key
. - Additionally the keypair files got-keypair
inserted just after the hostname, soexample.com.rsa.key
becomesexample.com-keypair.rsa.key
. - Additionally the CSR files got-request
inserted just after the hostname, soexample.com.rsa.csr
becomesexample.com-request.rsa.csr
. - Finally the OCSP response got-response
inserted just after the hostname, soexample.com.rsa.ocsp
becomesexample.com-response.rsa.ocsp
.This rename must be done for each domainset. If a keypair with the old filename is found Certgrinder will quit with exit code 1 and refuse to run. Use the new
show paths
subcommand to figure out what the new filenames should be.Prefix certgrinderd output with
certgrinderd:
when not in debug mode.Updated all dependencies in requirements.txt, and switch to pinning deps with == rather than >= so dependabot on github can do its thing
Fixed
Fix wrong requirements line for pre-commit (remove extra equal sign)
v0.14.2 (13-Sep-2020)
Added
Make
show certificate
output certificatenot_valid_before
andnot_valid_after
Changed
Rename test
test_show_certificate()
totest_show_certificate_file_not_found()
v0.14.1 (13-Sep-2020)
Added
Workaround to get certificate from chain in installations from before foo-certonly.crt was written separately. This makes the “get ocsp” subcommand work even if the current certificate was issued with an older version of certgrinder.
Changed
Rename parse_certgrinderd_certificate_output() to parse_certificate_chain() and clean it up a bit
Update some log messages and update tests to match
Change “intermediate” to “issuer” in the code and tests.
Rename intermediate cert path to example.com-issuer.crt instead of example.com-intermediate.crt. Existing intermediate/issuer certs will be renamed next time “get ocsp” is run, which is done automatically by the “periodic” command.
v0.14.0 (29-Aug-2020)
Changed
Update log message when running post-renew hooks
v0.14.0-beta2 (29-Aug-2020)
Added
Workaround to get intermediate from chain in installations from before foo-intermediate.crt was written separately. This makes the “get ocsp” subcommand work even if the current certificate was issued with an older version of certgrinder.
Changed
Separated the PEM chain splitting logic into a new split_pem_chain method
v0.14.0-beta1 (29-Aug-2020)
Added
OCSP response support
Log certgrinderd output at the level certgrinderd logs it at, when possible (otherwise log at WARNING)
Tests for the new functionality
Changed
Support the new certgrinderd commands and subcommands
Change short command for –config-file from -f to -c
Set default certgrinder command to “certgrinderd”
Use with for opening files a few places to avoid leaving open fds
Fixed
Changed certgrinder syslog ident from “certgrinderd” to “certgrinder”
v0.13.2 (11-Jul-2020)
Added
Manpage to MANIFEST.in to include it in the distribution
v0.13.1 (7-Jul-2020)
Changed
Specify python3.7 and 3.8 as classifiers in setup.py
v0.13.0 (7-Jul-2020)
Changed
Test suite now covers 100% of certgrinder.py
Fixed
Fix broken test client/certgrinder/tests/test_certgrinder.py::test_check_certificate_not_cert
Fix broken show_certificate() method, and make it output more useful info
v0.13.0-rc1 (1-Jul-2020)
Changed
Writing the certificate only (without the intermediate) to
example.com-certonly.crt
is new in 0.13, so make thecheck_certificate()
method checks the chain certificate to make sure upgrading 0.12 to 0.13 doesn’t trigger needlessly renewing all existing certs.
v0.13.0-beta2 (29-Jun-2020)
Added
Dev requirements now has
sphinx-rtd-theme
which is the theme used on ReadTheDocs, somake html
indocs/
now produces the same-ish output.Dev requirements now include
sphinx-argparse
used for generating automatic usage documentation.Very preliminary support for EC keys in addition to RSA keys.
More tests
Better validation of returned certificate and intermediate
Save intermediate in separate file, save certificate only in separate file.
Documentation for all config settings
Manpage certgrinder.8
periodic command to run from cron
Changed
Move CHANGELOG.md to rst format and into
docs/
Rework command-line options, add commands, rework configuration and configfile. This is a backwards incompatible change. Run
/venv/bin/certgrinder periodic
from cron,certgrinder help
for more info.Configuration is now a combination of command-line options (if any), config file (if any) and default config; in decreasing precedence order. A default setting will be overridden by a config file setting which will be overridden by a command-line setting.
Update
certgrinder.conf.dist
with new options and better commentsMark most methods as
@staticmethod
or@classmethod
, refactor code as needed. This makes the code more reusable and easier to test.Split certificate validity tests into separate methods
Split parsing of
certgrinderd
output into separate methodparse_certgrinderd_output()
Split argparse stuff (which grew considerably with this change) into separate
get_parser()
funcSupport calling
certgrinder.main()
function andcertgrinder.Certgrinder.grind()
method with a list of mocked command-line argsUpdate existing tests to deal with all the new stuff
Make pytest logformat look like regular logging
Split creating the argparse object into a separate function to assist sphinx-argparse
Reorder argparse commands and subcommands in alphabetical order
Re-add -v / –version to show version and exit
Test suite now covers 100% of certgrinder.py
v0.13.0-beta1 (7-May-2020)
Fixed
Made -q / –quiet mode work
Made certgrinder always pass
--log-level LEVEL
to certgrinderd, so the effects of both--quiet
and--debug
are passed to the certgrinderd call.
v0.13.0-alpha8 (6-May-2020)
Changed
Changed logformat to prefix messages with certgrinder: and Certgrinder. instead of nothing and %(name)s, making it more clear which messages are from certgrinder and which are from certgrinderd
Output logging from certgrinderd call
v0.13.0-alpha7 (6-May-2020)
Fixed
Old bug where permissions of private key would be fixed to 640 even if it was already 640
–log-level didn’t work without –debug
v0.13.0-alpha6 (6-May-2020)
No changes
v0.13.0-alpha5 (6-May-2020)
Added
MANIFEST.in file to include certgrinder.conf.dist in installs
Changed
Default config file is now ~/certgrinder.conf instead of ~/certgrinder.yml
v0.13.0-alpha4 (5-May-2020)
Added
There is now a –log-level=LEVEL command line argument to set loglevel more flexibly. It can be set to one of DEBUG, INFO, WARNING, ERROR, or CRITICAL.
Changed
Config file path should be given with the -f flag
Pass –staging and –debug flag to certgrinderd when given to certgrinder
Prefix syslog messages with “certgrinder” instead of “Certgrinder” to match the package name
v0.13.0-alpha3 (5-May-2020)
No changes
v0.13.0-alpha2 (4-May-2020)
Added
Install
certgrinder
binary using entry_points in setup.py
Changed
Wrap script initialisation in a main() function to support entry_points in setup.py better
v0.13.0-alpha (4-May-2020)
Added
Create Python package
certgrinder
for the Certgrinder client, publish on pypiAdd isort to pre-commit so imports are kept neat
Tox and pytest and basic testsuite using Pebble as a mock ACME server
Travis and codecov.io integration
Add -C argument which simply checks if the certificates are present and valid and have more than 30 days validity left. Exit code 0 if all is well or exit code 1 if one or more certificates needs attention.
Changed
Move client files into client/ and server files into server/, each with their own CHANGELOG.md, in preparation for Python packaging.
Reorder commandline arguments alphabetically.
Change a few imports to make mypy and isort happy
v0.12.1 (4-Jan-2020)
Added
Add RELEASE.md so I don’t forget how to do this
Fixed
Fixed release date for v0.12.0 in CHANGELOG.md
Add a few type: ignore for some of the cryptography imports and calls to make newer mypy happy
Changed
Update mypy to 0.761 and add to requirements-dev.txt
v0.12.0 (4-Jan-2020)
Changed
Support python3 instead of (NOT in addition to) python2
Format code with Black
Check code with flake8
Add type annotations and check code with mypy –strict
Fixed
pyyaml load deprecation warning: ./certgrinder.py:54: YAMLLoadWarning: calling yaml.load() without Loader=… is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
v0.11.0 (25-Dec-2018)
Added:
Support for setting SSH user: in certgrinder.yml config file.
Changed:
Remove OpenSSL dependency for key and X509 operations, use cryptography directly instead. This affects any method which deals with keys and/or X509.
Do not use shell=True for the subprocess.pOpen SSH call.
Removed:
Support for selfsigned certificates.
v0.10.2 (5-Apr-2018)
Added:
Support setting syslog_facility and syslog_socket in certgrinder.yml (defaults to “user” and “/var/run/log” to maintain backwards compat)
Warn in the last line when one or more selfsigned certificates has been created
Show a counter with the number of domainsets being processed
Fixed:
Typo in variable name in logoutput
Only log SSH output and exception info when in debug mode
Various improvements to logging
v0.10.1 (2-Mar-2018)
Fixed:
Version number was wrong in certgrinder.py
v0.10.0 (2-Mar-2018)
Added:
Move from webroot to manual Certbot authenticator, using hook scripts manual-auth-hook and manual-cleanup hook
Add DNS-01 support in hook scripts. DNS-01 is now the recommended challenge type.
csrgrinder got a config file
Describe new features in README
Many improvements to logging and error handling
Fixed:
Language and typos and layout in README
v0.9.5 (16-Feb-2018)
Fixed:
v0.9.4 had the wrong version number in the .py file.
Added:
-p / –showspki switch to output pin-sha256 pins for the public keys. Useful for HPKP or other pinning that uses the same format.
v0.9.4 (17-Jan-2018)
Fixed:
The showtlsa (-s) and checktlsa (-c) features did not work for multiple domain sets
v0.9.3 (17-Jan-2018)
Fixed:
Custom nameserver functionality was not working due to an error
Catch more types of exceptions when looking up DNS results, and exit if a serious error occurs.
v0.9.2 (17-Jan-2018)
Fixed:
Typo in CHANGELOG.md
v0.9.1 (17-Jan-2018)
Fixed:
Logic for using a custom nameserver with -n / –nameserver was inverted.
Add example directory structure to README.md
Added:
Show version number in usage and add -v / –version switch to show it.
Add shebang line to certgrinder.py and make the script executable.
v0.9.0 (16-Jan-2018)
Added:
This changelog. First numbered release.