Letsencrypt & TLSA - automation
John Allen
john at klam.ca
Sun Feb 19 19:20:52 CET 2017
Attached is a bash script that I am developing to automate the
generation of TLSA records from Letsencrypt certificates.
the script is called from the certbot renew hook, it can also be run
stand alone - Certbot_TLSAgen path-to-certificate "space separated list
of domains included in cert"
It seems to work, but would some kind sole take a look and where I have
or are about to screw up.
Any suggestions as to how to get the output into my DNS (Bind9)
preferably without using nsupdate. I am not keen on nsupdate as it makes
a mess of the zone files, which I use as documentation for my DNS.
Has anybody heard of a electronic "one time pad" system.
TIA
JohnA
-------------- next part --------------
#!/bin/bash
#=====================================================================================================================
#
# Copyright (c) 2017 John L. Allen
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#=====================================================================================================================
declare -a TLSA_Services=( smtp imap submission sieve dav davical https ) # An arrrayof services for which TLSA might be needed
# these two variables are ????
Certbot_Lineage= # path to the certbot generated cert ...(RENEWED_LINEAGE or $1)
declare -a Certbot_Domains # An array into which RENEWED_DOMAINS or $2 Domains are split
# default values
#TLSA_File_Path="/etc/bind/tlsa/" # where do you want to put the TLSA records, default values
TLSA_File_Path="./tlsa_test/" # where do you want to put the TLSA records, default values - during testing
TLSA_Filename_Base="tlsa" # base filename for the TLSA records - generated as base_filename.sequence_number
TLSA_TTL=3600 # TLSA DNS record TTL
TLSA_Usage=3 # TLSA usage
TLSA_Selector=1 # TLSA selector
TLSA_Type=1 # TLSA type
#
#=====================================================================================================================
#===================Message output function - will need changing to output multi language message=====================
function error_message() {
echo $1 1>&2
exit 1
}
#=====================================================================================================================
#================ Check of either the Certbot set environment variables or the command line paramater=================
#================ depending upon how this script is invocked =================
#=====================================================================================================================
function Check_Env_Variables()
{
# Has command line parameter 1 or the environment variable RENEWED_LINEAGE been set.
# The Command line parameter overides the environment variable
[[ -z $RENEWED_LINEAGE ]] && Certbot_Lineage=$1 || Certbot_Lineage=$RENEWED_LINEAGE
if [[ -z $Certbot_Lineage ]]; then
error_code= $(error_message "Command line parameter 1 or the evironment variable \"RENEWED_LINEAGE\" was not set, exiting")
elif ! [[ -d $Certbot_Lineage ]]; then
error_code= $(error_message "Command line parameter 1 or the evironment variable \"RENEWED_LINEAGE\" is not a directory, exiting")
fi
# Has command line parameter 2 or the environment variable RENEWED_DOMAINS been set.
# The Command line parameter overides the environment variable
# If more than one domain is specified they are expected to be in the form of a space seperated list
[[ -z $RENEWED_DOMAINS ]] && Certbot_Domains=( $2 ) || Certbot_Domains=( ${RENEWED_DOMAINS} )
if [[ ${#Certbot_Domains[@]} -le 0 ]]; then
error_code=$(error_message "Command line parameter 2 or the evironment variable \"RENEWED_DOMAINS\" was not set, exiting")
fi
# Check that the TLSA_File_Path is the location (directory) where files containing the generated TLSA records will be put
#
if ! [ -d $TLSA_File_Path ]; then
error_code=$(error_message "the directory $TLSA_File_Path does not exist, please specify where the generated records are to be stored")
fi
# Check that the TLSA filename base has been set.
# The filename base is used to generate actual filenames in the form of base.sequence
#
if [ -z "$TLSA_Filename_Base" ]; then
error_code=$(error_message "Please specify a base filename into which the generated TLSA records can be placed")
fi
return $error_code
}
#=====================================================================================================================
#=======Set the output file name, including path.
#=======The filename is derived from TLSA_Filename_Base plus a generated serial number suffix
zzz=" "
function Set_Output_Destination()
{
count=$( ls -1 $1 | wc -l )
name=$(printf "%s%s.%04d" "$1" "$2" "$count")
while [ -a $name ]; do
count=$(( $count+1))
name=$(printf "%s%s.%04d" "$1" "$2" "$count")
done
echo $name
}
#=====================================================================================================================
#********************************************************************************************************************
#********************************************************************************************************************
# THe following functions were stolen from Viktor Dukhovni's tlsagen
#********************************************************************************************************************
#********************************************************************************************************************
#=====================================================================================================================
extract() {
case "$1" in
0) openssl x509 -in "$2" -outform DER;;
1) openssl x509 -in "$2" -noout -pubkey | openssl pkey -pubin -outform DER;;
esac
}
digest() {
case "$1" in
0) cat;;
1) openssl dgst -sha256 -binary;;
2) openssl dgst -sha512 -binary;;
esac
}
Set_TLSA_Usage() {
case "$(echo $1 | tr '[A-Z]' '[a-z]')" in
0|pkix-[ct]a) TLSA_usage=0;;
1|pkix-ee) TLSA_usage=1;;
2|dane-[ct]a) TLSA_usage=2;;
3|dane-ee) TLSA_usage=3;;
*) error "Invalid certificate usage: $1";;
esac
}
Set_TLSA_Selector() {
case "$(echo $1 | tr '[A-Z]' '[a-z]')" in
0|cert) TLSA_selector=0;;
1|spki|pkey) TLSA_selector=1;;
*) error "Invalid selector: $1";;
esac
}
Set_TLSA_Type() {
case "$(echo $1 | tr '[A-Z]' '[a-z]')" in
0|full) TLSA_Type=0;;
1|sha2-256|sha256|sha-256) TLSA_Type=1;;
2|sha2-512|sha512|sha-512) TLSA_Type=2;;
*) error "Invalid matching type: $1";;
esac
}
#=====================================================================================================================
#=====================================================================================================================
# as we cannot pass and parameters when this is called from the certbot renew hooks
# we will have to put them somewhere else,
# Defailt for general parameters
#
if [ -f /etc/default/Cerbot_TLSAgen.cf ]; then . /etc/default/Cerbot_TLSAgen.cf; fi
Check_Env_Variables "$1" "$2" # If we are running as a command then these two should be set, if not certbot environment variables should be
[[ $? != 0 ]] && exit # ooops! exit. this needs some improvement
#******needs testing
# Certbot_Lineage should now be set, so pick up any cert based paramamters from the ".../live/certname" directory
#
if [ -f ${Certbot_Lineage}Certbot_TLSAgen.cf ]; then . ${Certbot_Lineage}Certbot_TLSAgen.cf; fi
TLSA_Record_Store=$(Set_Output_Destination $TLSA_File_Path $TLSA_Filename_Base) # generate the output file location Path + generate filename
[[ $? != 0 ]] && exit # ooops! exit. this needs some improvement
TLSA_Digest=$( extract $TLSA_Selector ${Certbot_Lineage}cert.pem | digest $TLSA_Type | od -vAn -tx1 | tr -d ' \012' # Generate the TLSA key as a HEX string. As we are dealiing with a single cert do this once
exit $(( ${PIPESTATUS[0]} | ${PIPESTATUS[1]} | ${PIPESTATUS[2]} )) )
[[ $? != 0 ]] && exit # ooops! exit. this needs some improvement
# for each (sub)domain listed, check to see if it is a service doamin that we might like to have a TLSA record for
#
for domain in $( seq 0 $((${#Certbot_Domains[@]} -1 )) ); do
TLSA_target=${Certbot_Domains[domain]%.} # I am not sure whether the Cerbot list of (sub)domains has a trailing "."
TLSA_domain=${TLSA_target#*.} # Hopefully this will strip off the sub-domain part giving me a domain-name + TLD
if [[ -z "${TLSA_domain##*.*}" ]]; then # Primitive test - if there is a "." this is likely to be domin + TLD
for service in $( seq 0 $((${#TLSA_Services[@]} -1 )) ); do # Do this for each of the services that might us a TLSA record
while read srv_priority srv_weight srv_port srv_host; # read the out put of a DIG for a SRV record
do
if [ ${srv_host%.} == $TLSA_target ]; then # if the host returned by dig = the Cerbot target output a TLSA record
printf "_%d._tcp.%s %d IN TLSA %d %d %d %s\n"\
"$srv_port" "$srv_host" "$TLSA_TTL" "$TLSA_Usage" "$TLSA_Selector" "$TLSA_Type" "$TLSA_Digest" >> $TLSA_Record_Store
fi
done < <(dig SRV _${TLSA_Services[$service]}._tcp.${TLSA_domain} +short) #
done
# SOA_domain=$TLSA_domain
# read soa_ns soa_admin soa_seq soa_t1 soa_t2 soa_t3 soa_t4 < <(dig SOA $SOA_domain +short)
fi
done
More information about the dane-users
mailing list