Configuration¶
Once installed, alnitak must be configured to work properly.
Configuration is performed by editing the file /etc/alnitak.conf
.
Alnitak¶
Alnitak needs to know:
- What TLSA records to create when a domain’s certificates are renewed.
- How to interact with your domain’s DNS zone(s).
This information is passed to alnitak via the creation of Targets and API Schemes respectively in the configuration file.
Targets¶
A “target” is an instruction on what TLSA records to create when a domain’s certificates are renewed. A target looks like this:
[example.com]
tlsa = 311 443 tcp
With this target in the configuration file, if a certificate in
/etc/letsencrypt/archive/example.com/
is renewed, alnitak will attempt
to create a TLSA record:
TLSA 3 1 1 _443._tcp.example.com certificate_data...
A target is constructed from the domain directory name that the Let’s Encrypt certificates are located in as a section header:
[DOMAIN]
# ...
(which indicates that the creation of TLSA records will triggered when
certificates in /etc/letsencrypt/live/DOMAIN/
are renewed), and at least
one tlsa
parameter:
tlsa = PARAMS PORT [PROTOCOL] [RECORD_DATA_DOMAIN]
A tlsa
parameter requires the concatenated parameters of the TLSA record
(the usage field, the selector field and the matching type field in that
order), the port the service is running on, and optionally an explicit
protocol for the service and the domain to appear in the record.
If no protocol is explicitly specified, “tcp” is assumed; and if no domain
RECORD_DATA_DOMAIN
is explicitly specified, the domain in the target’s
section header is used (DOMAIN
).
Note that only usage fields ‘2’ (DANE-TA) and ‘3’ (DANE-EE) are supported.
Selector field inputs ‘0’ and ‘1’ are both supported, as well as the matching
type fields ‘0’, ‘1’ and ‘2’. Hence, PARAMS
can take any value given by
the regex: “[23][01][012]”.
Example 1¶
Target:
[example.com]
tlsa = 311 25 smtp.example.com
will trigger the TLSA record:
TLSA 3 1 1 _25._tcp.smtp.example.com certificate_data...
to be created when certificates in /etc/letsencrypt/archive/example.com
are renewed.
Example 2¶
Target:
[example.com]
tlsa = 312 443
tlsa = 300 8443 udp
tlsa = 211 443 tcp www.example.com
will trigger the TLSA records:
TLSA 3 1 2 _443.tcp.example.com certificate_data...
TLSA 3 0 0 _8443.udp.example.com certificate_data...
TLSA 2 1 1 _443.tcp.www.example.com certificate_data...
to be created when certificates in /etc/letsencrypt/archive/example.com
are renewed.
API Schemes¶
An API scheme tells alnitak how it should interact with your domain’s DNS
zone(s). If your DNS is managed by Cloudflare, then alnitak can interact
with Cloudflare directly in order to create/delete DNS TLSA records. If you
manage your zone(s) locally or via another provider, then the exec
API
scheme is provided, whereby alnitak can call an external program to perform
the relevent DNS operations.
An API scheme is specified with an api
parameter like so:
api = SCHEME [inputs...]
and can either be placed within a target:
# target 1
[example.com]
tlsa = ...
api = SCHEME [inputs...]
# target 2
[example.org]
tlsa = ...
api = SCHEME [inputs...]
in which case the API schemes specified will apply to only that particular target; or else can appear outside of all targets:
api = SCHEME [inputs...]
# target
[example.com]
tlsa = ...
# target
[example.org]
tlsa = ...
in which case the API scheme will apply to all targets for which no API scheme is explicitly given in the target.
Where both an API scheme outside of all targets and a target-specific API scheme exists, only the target-specific API scheme will apply to the target in question.
Multiple API schemes cannot apply to any specific target. Where more than one
api
parameter is given in a given context, only the last such occurring one
will be in effect. That is, for the following:
api = SCHEME_1
api = SCHEME_2
[target1]
tlsa = ...
[target2]
tlsa = ...
api = SCHEME_3
api = SCHEME_4
target1
will have API scheme SCHEME_2
and target2
will have API scheme SCHEME_4
.
Exec API Scheme¶
The exec
API scheme is specified like so:
api = exec PROG [ARGS...]
which will call PROG ARGS...
as needed to create/delete DNS records.
The external program must be able to create and delete DANE TLSA records,
and should distinguish between these two operations by reading the
environment for a parameter called TLSA_OPERATION
, which will be set
to the value “publish” or “delete” respectively.
Note
Any flags specified in the API scheme will be passed equally to both
operations. The two operations of publishing and deleting DNS records
should be distinguished only by reading the environment parameter
TLSA_OPERATION
.
Under either operation, the environment will contain:
PATH
: set to"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
IFS
: set to" \t\n"
LETSENCRYPT_DIR
: set to the Let’s Encrypt directory (typically/etc/letsencrypt
). This is provided in case the program being called needs to do something with the certificates in the Let’s Encrypt directory; this parameter provides the program with the parent directory from which the Let’s Encrypt certificates were read.RENEWED_DOMAINS
: set to all the domains whose certificates were renewed, separated by a single space character. For example:example.com example.org example.net
. This is provided in case the program being called needs to know all the domains that were renewed. Note that the program will be called for each entry in this list.ZONE_DOMAIN
: set to the domain inRENEWED_DOMAINS
that the current call to the program is expected to process.TLSA_USAGE
: set to the usage field of the TLSA record.TLSA_SELECTOR
: set to the selector field of the TLSA record.TLSA_MATCHING
: set to the matching type field of the TLSA record.TLSA_PARAM
: set to a string formed by concatenating the usage, selector and matching type fields.TLSA_PORT
: set to the TLSA record port.TLSA_PROTOCOL
: set to the TLSA record protocol.TLSA_DOMAIN
: set to the TLSA record domain.TLSA_HASH
: set to the TLSA record’s certificate association data.
Creating records¶
In addition to the environment parameters above, the following will be set:
TLSA_OPERATION
: set to"publish"
The program ought to create a DANE TLSA record with certificate association
data as contained in the parameter TLSA_HASH
.
When done, the program must exit with code:
- 0 - if the TLSA record was published successfully.
- 1 - if the TLSA record is already up.
- 2+ - if an error occurred that should cause alnitak to exit with an error code.
- 128+ - if an error occurred that should not cause alnitak to exit with an error code.
Deleting records¶
In addition to the environment parameters above, the following will be set:
TLSA_OPERATION
: set to"delete"
TLSA_LIVE_HASH
: may be present, and if so, will be set to the certificate association data of the new TLSA record that was previously published.
The program ought to delete a DANE TLSA record with certificate association
data as contained in the parameter TLSA_HASH
. If the parameter
TLSA_LIVE_HASH
is set, the program ought only to do such a deletion if a
DANE TLSA record with certificate association data given by the value of
TLSA_LIVE_HASH
is live.
When done, the program must exit with code:
- 0 - if the old record was deleted successfully.
- 1 - if the new record was not up yet, so the old one was not yet deleted.
- 2+ - if an error occurred that should cause alnitak to exit with an error code.
- 128+ - if an error occurred that should not cause alnitak to exit with an error code.
Example Code¶
Here is an outline of some basic bash shell code that will help illustrate the above requirements:
#!/bin/bash
#
# api_get() - check if a DNS record is live
# api_post() - publish a DNS record
# api_delete() - delete a DNS record
# set 'json' to the json data of the record we will be processing:
read -d = json <<EOF
{ "tlsa": "_$TLSA_PORT._$TLSA_PROTOCOL._$TLSA_DOMAIN",
"data": {
"usage": $TLSA_USAGE,
"selector": $TLSA_SELECTOR,
"matching_type": $TLSA_MATCHING
"certificate_data": "$TLSA_HASH"
}
}
=
EOF
# set 'json_new' for when 'TLSA_LIVE_HASH' is set. If not set we
# won't use this anyway
read -d = json_new <<EOF
{ "tlsa": "_$TLSA_PORT._$TLSA_PROTOCOL._$TLSA_DOMAIN",
"data": {
"usage": $TLSA_USAGE,
"selector": $TLSA_SELECTOR,
"matching_type": $TLSA_MATCHING
"certificate_data": "$TLSA_LIVE_HASH"
}
}
=
EOF
# delete a TLSA record
if [[ "$TLSA_OPERATION" == "delete" ]]; then
# if 'TLSA_LIVE_HASH' is set, we must first check if that
# record is live before we can delete anything:
if [[ -z "$TLSA_LIVE_HASH" ]]; then
# 'TLSA_LIVE_HASH' not set; unconditionally delete the
# old TLSA record:
if api_delete "$json"; then
exit 0
else
exit 2
fi
else
# first we need to check if the new TLSA record is up:
if api_get "$json_new"; then
# new TLSA record is up; we can delete the old one...
if api_delete "$json"; then
exit 0
else
exit 2
fi
else
# new TLSA not yet up; we cannot delete the old
# one yet...
exit 1
fi
fi
# publish a TLSA record
else
# check if record is already up:
if api_get "$json"; then
# record is already up
exit 1
else
# record not already up
if api_post "$json"; then
exit 0
else
exit 2
fi
fi
fi
Cloudflare API Scheme¶
The cloudflare
API scheme is specified either like:
api = cloudlfare email:EMAIL key:KEY
where EMAIL
and KEY
are your Cloudflare API login credentials;
or alternatively:
api = cloudflare FILE
where FILE
is the location of the file that contains the credentials.
Where a credentials file is given, it should contain:
# comments are allowed
dns_cloudflare_email=EMAIL # comments allowed here too
dns_cloudflare_api_key = KEY # whitespace is also allowed
It is recommended to use a credentials file rather than placing the credentials directly in the configuration file. The credentials file should also be appropriately secured against arbitrary access. Alnitak needs root permissions to operate, and will open the file as root, so as restrictive a set of permissions as operationally necessary should be considered. At least the file should not be world readable or writable.
Note
The format for the credentials file is designed to be able to read the file that certbot itself needs to interact with Cloudflare in order to renew a certificate (if utilized; for example if you generated a wildcard certificate). This means that if such a file exists on your system, you can reuse it for alnitak and do not need to expose your credentials in two different files.
Other Commands¶
The following commands can also be placed anywhere in the configuration file:
dane_directory = /dir
will set the directory in which the dane certificates will be located to
/dir
. The default value is /etc/alnitak/dane
. The command-line
equivalent is the flag --dane-directory
(or -D
).
letsencrypt_directory = /dir
will search for Let’s Encrypt certificate in /dir
instead of the default
/etc/letsencrypt/
. The command-line equivalent is the flag
--letsencrypt-directory
(or -C
).
ttl = N
will set the time-to-live value for the TLSA record renewal to N
seconds
(see Running for more info). The default value is 86400 (1 day).
The command-line equivalent is the flag --ttl
(or -t
).
log_level = <no|normal|verbose|debug>
will set the logging level to the desired value (normal
is the default).
The only difference between setting the logging level in the configuration
file instead of at the command line (via the -L
or --log-level
flag) is that certain logging output from the reading of the
configuration file itself will be missed. This is often not an issue, since
the filesystem/DNS actions of the program are the real targets of the
logging mechanisms.
If, however, you do want to capture configuration file parsing in the log
file, then you must use the equivalent command-line flag instead.
Certbot¶
Alnitak is designed to run on certbot’s pre-hook and deploy-hook in order to ensure that certificates being used by a service do not break DANE authentication (see How Alnitak Works for more details). As such, whenever alnitak is being used to manage DANE TLSA records, all certbot renewals must call alnitak on these hooks in order for DANE authentication to continue working.
When running certbot explicitly, simply ensure the hooks are specified:
$ certbot renew --pre-hook "alnitak pre" --deploy-hook "alnitak deploy" ...
You must run alnitak pre
on the certbot pre-hook and alnitak deploy
on the certbot deploy-hook.
The command alnitak pre ...
ensures that dane certificates are prepared
for a potential certificate renewal (amongst other things). Likewise, the
command alnitak deploy
ensures that dane certificates are either
restored if no renewal occurs, or creates DANE TLSA records otherwise.
When certificate renewal is automated, either as a cron job or systemd timer,
the hooks must be set in the Let’s Encrypt renewal configuration files (in the
directory /etc/letsencrypt/renewal
); the following lines must be added to
the renewalparams
section:
[renewalparams]
pre_hook = alnitak pre
renew_hook = alnitak deploy
These changes must be made to all such renewal configuration files for which you wish alnitak to manage DANE TLSA records.
Technically, alnitak pre
needs to be run before certbot renewal occurs,
and alnitak deploy
needs to be run after certbot renewal occurs and be
given a list of domains that were renewed in the environment parameter
RENEWED_DOMAINS
(space or tab delimited).
The most convenient way to do this is on the certbot pre and deploy hooks, but
it is not necessary.
Warning
Do not run alnitak deploy
on certbot’s post-hook. Alnitak needs to
know which domains were renewed, and the environment parameter
RENEWED_DOMAINS
is not set on the post-hook; it is only set on the
deploy hook.
Older versions of certbot may be in conflict with this prescription.
Ensure that alnitak deploy
runs on whichever hook sets
RENEWED_DOMAINS
and things will work fine.
System¶
In addition to running on certbot’s pre and deploy hooks whenever a certificate renewal occurs, alnitak also needs to run periodically on the system so that any certificates that were being held back until the new certificates’ DANE TLSA records go live will be switched to the new ones when they do. Here, you simply need to run alnitak (without any special flags) however often you like. For example, as a cron job every day at 1am and 1pm:
# crontab
PATH = /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
#
# m h dom mon dow command
0 1,13 * * * alnitak
The times chosen to run at can be anything that is convenient: when
alnitak is called, it will check if the DNS records are live only after a
set period of time has elapsed in order to allow the changes to the zone to
propagate. By default this time is set to 24 hours, but can be adjusted with
the --ttl
flag.