--- categories: ['DNS'] date: 2022-09-01T00:32:16+02:00 description: "Comment gérer DNSSEC, les enregistrements TLSA, pour valider les certificats TLS sous OpenBSD avec nsd, ldns et consorts…" draft: false include_toc: true show_comments: false tags: ['DNS','DANE','DNSSEC','OpenBSD','TLSA'] title: "OpenBSD : Gérer DNS, DNSSEC (puis automatiser ses enregistrements TLSA)" translationKey: 'openbsd-nsd-dnssec-tlsa' --- ## Description En 2018, je me suis posé la question de la gestion des enregistrements TLSA, selon le protocole DANE, lié au protocole de sécurité DNSSEC, dans mes zones DNS. *(cf: {{< inside2 l="/post/generer-enregistrement-tlsa" >}}) - je vous invite à le lire…* *Certains vont utiliser l'outil **knot**, fourni en tant que paquet sous OpenBSD, car ils trouvent complexe à gérer. Mais nous verrons avec un peu d'astuces comment gérer cela de manière automatisé en shell, sous OpenBSD*. Pour rappel, mon service DNS fonctionne depuis plus de 4 ans, sous OpenBSD grâce au logiciel natif **nsd**. La gestion des enregistrements DNSSEC se fait grâce aux outils **[ldns](https://openports.pl/path/net/ldns,-utils)** à installer en tant que paquet tiers. J'utilise dans les faits l'outil **[ldnscript](https://www.22decembre.eu/fr/2017/11/01/ldnscripts/)** qui permet de gérer la création des clés nécessaires puis s'occupe de gérer les enregistrements DNSSEC adéquats. ⇒ En Juin 2022, j'ai décidé de basculer du chiffrement RSA par l'utilisation de l'algorithme à courbes elliptiques nommés ECDSA. Avant d'aller plus loin en ce sens, passons à l'installation des prérequis nécessaires : ## Installation ### ldnscript Pour me rappeler comment faire, je me suis fait le petit mémo suivant : 1. installer les binaires nécessaires :
`$ doas pkg_add ldns-utils git`
2. télécharger et installer ldnscripts ```sh $ cd /usr/local/src/ $ doas mkdir ldnscripts $ doas chown $USER ldnscripts $ git clone https://framagit.org/22decembre/ldnscripts.git $ cd ldnscripts $ doas make install ``` 3. configurer le fichier /etc/ns/ldnscript.conf ```cfg ; SHA256 est largement suffisant et sécuritaire ALG=ECDSAP256SHA256 NSEC3_ALG=SHA-256 ``` 4. initialisation du domaine
`$ doas ldnscript init domain.tld` 5. nécessité de créer un lien symbolique /usr/bin/dig vers /usr/sbin/dig :
`$ doas ln -sf /usr/bin/dig /usr/sbin/dig`
*(sans ce dernier point, l'outil `ldnscript` ne pourra trouver le binaire `dig` et donc refusera de fonctionner en se mettant en erreur)*. ## Configuration ### /etc/monthly.local Tous les mois, je crée le rollover nécessaire des clés en intégrant dans le script `/etc/monthly.local`, le code shell suivant : ```sh ### ldnscript printf '%s\n' "=> ldnscript rollover" /usr/local/sbin/ldnscript rollover all ``` #### Let's Encrypt Dans la foulée, du rollover des clés DNSSEC, mon script interroge les services Let's Encrypt pour savoir s'il faut un renouvellement des certificats pour les noms de domaines que j'utilise. Normalement, on utiliserait le client `acme` natif sous OpenBSD, mais ayant eu certains déboires, j'ai décidé de basculer sur l'usage de `certbot`. Une simple écriture shell suffit pour le renouvellement :
`/usr/local/bin/certbot renew --pre-hook "rcctl stop nginx" --post-hook "rcctl start nginx"` *(en effet, j'utilise aussi le serveur nginx, en lieu et place du serveur web natif **httpd** - mais il suffit de remplacer le nom du service, si jamais c'est votre cas)*. Bien-sûr, cette partie n'entre pas directement en ligne de compte de la gestion DNS. Néanmoins, c'est important à prendre en compte car lors du renouvellement de certificat, il est nécessaire de regénérer les enregistrements TLSA - *nous verrons ce point plus tard*. ### Exemple zone DNS Voici pour l'exemple une zone DNS minimaliste, gérée par le serveur **ns** : ```conf $TTL 1H $ORIGIN domain.tld. @ IN SOA domain.tld. dns.domain.tld. ( 2022090101 ; 3H ; refresh 1H ; retry 2W ; expire 1H ; negative ) @ IN NS ns1.domain.tld. @ IN NS ns2.domain.tld. @ IN A 46.23.90.29 IN AAAA 2a03:6000:6e65:619::29 ; enregistrement CAA @ IN CAA 0 iodef "mailto:mail@domain.tld" @ IN CAA 0 issue "letsencrypt.org" @ IN CAA 0 issuewild "letsencrypt.org" www IN A 46.23.90.29 IN AAAA 2a03:6000:6e65:619::29 ; TLSA _443._tcp.domain.tld. IN TLSA 3 1 2 5c8fdd68178ce4cd8d88bd90b82a96df41674d555340b88283c24a0b3416aa375144cd6c16a58160ba3b168e59f5003bff656ce67cb24931b462fe4910bd62f5 ``` ## shell ### Générer un Enregistrement TLSA En shell, générer un enregistrement TLSA n'est pas compliqué : `openssl x509 -noout -pubkey -in "${cert}" | openssl "${command}" -pubin -outform der 2>/dev/null | "${algo}" | tr "a-z" "A-Z"` **Explications** : ⇒ La commande ci-dessus nous permet de générer une variable intermédiaire nommée `tlsa_cert_associated` où : - la variable `$cert` est le nom du chemin absolu du certificat TLS serveur, dans le système de fichier du serveur, lié au domaine cible. - la variable `$command` est le nom de la commande utilisée par OpenSSL, soit `rsa` ou `ec` *(réciproquement pour les enregistrements selon le chiffrement RSA ou ECDSA)*. - et, où la variable `$algo` est le nom de l'utilitaire `sha256`, ou `sha512`, *au choix personnel et* restituera un condensé de message lié à l'algorithme choisi. ⇒ l'écriture relative à l'enregistrement TLSA est aussi simple : `tlsa_record="_${tls_port}._${tls_proto}.${domain}. IN TLSA ${tlsa_usage} ${tlsa_selector} ${tlsa_method} ${tlsa_cert_associated}"` où : - `${tls_port}` est le numéro de port du serveur web utilisé, ici **443** - `${tls_proto}` est le nom du protocole utilisé, ici **tls** - `${domain}` est le nom de domaine cible - `${tlsa_usage}` est le chiffre correspondant à la contrainte utilisée, ici **3**, correspondant à la contrainte **DANE-EE**, et recommandée par Let's Encrypt. - `${tlsa_selector}` est le chiffre correspondant au nom de sélecteur utilisé, ici **1**, correspondant au sélecteur **SPKI**, là aussi recommandé par Let's Encrypt. - `${tlsa_method}` étant la méthode utilisant l'algorithme de chiffrement choisi ; ici **SHA256**, qui offre un niveau *actuel* de chiffrement dit sécurisé et tout autant recommandé par Let's Encrypt. - et pour finir la variable `tlsa_cert_associated` précédemment créée. ### Vérifier un Enregistrement TLSA Vérifier un enregistrement TLSA est un poil plus compliqué ; il faut : 1. 1/ interroger le serveur DNS pour connaître l'enregistrement TLSA actuel par le biais de l'outil `dig`, pour l'exemple. 2. 2/ le comparer avec la sortie fournie par l'outil `openssl` qui va interroger le certificat installé sur le serveur web utilisé, lié au nom de domaine en question. --- - interroger le serveur DNS se fait ainsi : ```sh tlsa="$(dig TLSA _443._tcp."${domain}" +short)" d_tlsa="$(echo "${tlsa}" | awk '{ for(i=4;i<=NF;++i) printf "%s", tolower($i); print "" }')" ``` - utiliser openssl pour interroger le certificat TLS utilisé sur le serveur web : ```sh tlsa="$(echo | openssl s_client -servername "${domain}" -showcerts -connect "${domain}":443 2>/dev/null | openssl x509 -noout -pubkey | openssl pkey -outform der -pubin 2>/dev/null | openssl dgst -"${algo}" 2>/dev/null )" o_tlsa="$(echo "${tlsa}" | awk -F'=' '{ print $2 }' | tr -d ' ')" ``` Pour finir, il suffit vraiment de comparer les deux variables shell `d_tlsa` et `o_tlsa` pour savoir si elles correspondent, ce qui en temps normal doit être le cas. **Sauf en cas, de renouvellement de certificat TLS, qui nécessitera donc de renouveller l'enregistrement TLSA, dans la zone du nom de domaine en question, sur le serveur DNS**. Pour rappel, si cela n'est pas fait, une interrogation du serveur DNS sur le protocole DNSSEC générera une erreur puisque l'enregistrement TLSA ne correspondra pas un certificat web fraîchement utilisé, ou renouvellé, ce qui va entraîner cette conséquence : l'accès au serveur, lié au nom de domaine cible, sera impossible. Il faudra donc générer un nouvel enregistrement TLSA correspondant au renouvellement du certificat TLS, puis régénerer la signature des enregistrements DNSSEC liés à la zone DNS du domaine cible. ## Scripts Shell Pour info, les scripts shell **tlsa.sh**, **dns.conf**, **dns.ksh** sont écrits dans un répertoire nommé **dns-tools** dans le répertoire personnel. *À vous de voir où vous désirez les mettre, et modifier le script mensuel*. ### monthly.local Voici la teneur de mon script shell **monthly.local** : ```sh #!/bin/sh ### ldnscript printf '%s\n' "=> ldnscript rollover" /usr/local/sbin/ldnscript rollover all ### renew ssl by certbot printf '%s\n' "=> renew letsencrypt certs" /usr/local/bin/certbot renew --pre-hook "rcctl stop nginx" --post-hook "rcctl start nginx" ### check tlsa records for domain; only for tcp:443 for domain in "sub.domain.tld" "domain.tld" "www.domain.tld" "sub.domain2.tld" "domain2.tld" "www.domain2.tld"; do printf '%s\n' "=> Test TLSA for ${domain}" /home/-your-user-/dns-tools/tlsa.sh "${domain}" done (…) ``` ### tlsa.sh Voici mon script shell nommé **tlsa.sh** : ```sh #!/bin/sh set -e #set -x ######################################################################## # # Author: Stéphane HUC # mail: devs@stephane-huc.net # gpg:fingerprint: CE2C CF7C AB68 0329 0D20 5F49 6135 D440 4D44 BD58 # # License: BSD Simplified # # Github: # # Date: 2022/07/01 06:45 # ######################################################################## # # Purpose: tool to test TLSA record # - for the geek: DANE-TLSA... # # Needed tools: dig, openssl # # OS: Tested on OpenBSD, Devuan # ######################################################################## ### ## # see: https://www.bortzmeyer.org/monitor-dane.html ## ### ######################################################################## ROOT="$(dirname "$(readlink -f -- "$0")")" . "${ROOT}/dns.conf" dir_admin="/home/-your-user-/dns-tools" domain="$1" ### DO NOT TOUCH! d_tlsa='' # TLSA record by dig o_tlsa='' # TLSA record by openssl tlsa_record='' # TLSA record tlsa_method=2 ######################################################################## #### ## # All needed functions! DO NOT TOUCH-IT! ## ### ######################################################################## byebye() { mssg "KO" "Script stop here!" mssg "KO" "Please, search to understand reasons." exit 1 } check_uid() { if [ "$(id -u)" -ne 0 ]; then mssg "KO" "ERROR: Script not launch with rights admin!" byebye fi } _dig() { tlsa="$(dig TLSA _443._tcp."${domain}" +short)" d_tlsa="$(echo $tlsa | awk '{ for(i=4;i<=NF;++i) printf "%s", tolower($i); print "" }')" } _openssl() { tlsa="$(echo | openssl s_client -servername "${domain}" -showcerts -connect "${domain}":443 2>/dev/null | openssl x509 -noout -pubkey | openssl pkey -outform der -pubin 2>/dev/null | openssl dgst -"${algo}" 2>/dev/null )" o_tlsa="$(echo $tlsa | awk -F'=' '{ print $2 }' | tr -d ' ')" } mssg() { typeset statut info text statut="$1" info="$2" case "${statut}" in "KO") text="[ ${red}${statut}${neutral} ] ${info}" ;; "OK") text="[ ${green}${statut}${neutral} ] ${info}" ;; #*) mssg="${text}" ;; esac printf "%s \n" "${text}" unset info statut text } new_tlsa() { cert="/etc/letsencrypt/live/${domain}/cert.pem" case "${le_key_type}" in "ecdsa") tlsa_cert_associated="$(openssl x509 -noout -pubkey -in "${cert}" | openssl ec -pubin -outform der 2>/dev/null | "${algo}")" ;; "rsa") tlsa_cert_associated="$(openssl x509 -noout -pubkey -in "${cert}" | openssl rsa -pubin -outform der 2>/dev/null | "${algo}")" ;; esac tlsa_record="_${tls_port}._${tls_proto}.${domain}. IN TLSA ${tlsa_usage} ${tlsa_selector} ${tlsa_method} ${tlsa_cert_associated}" unset tlsa_cert_associated } ######################################################################## #### ## # Execution ## ### ######################################################################## if [ -z "${domain}" ]; then printf '%s\n' "[ KO ] Script stops here; no domain!"; exit; fi _dig _openssl if [ "${d_tlsa}" = "${o_tlsa}" ]; then mssg "OK" "Similar TLSA records! :D" else mssg "KO" "There seems to be a problem with the TLSA records of the domain: ${domain}!" printf '%s\n' "Have you renew recently the TLS certs for the domain? If yes, change the TLSA record into the DNS zone relevent!" printf '%s\n%s\n' "⇒ Perhaps, the dns.sh script shell can help you. ;-)" check_uid printf '%s\n' "⇒ Display new TLSA record:" new_tlsa printf '%s\n%s\n' "Add/modify tlsa into your DNS zone for ${domain}: " "${tlsa_record}" printf '%s\n' "⇒ Modify TLSA record into the domain zone for ${domain}" "${dir_admin}"/dns.ksh tlsa "${domain}" fi ``` **Modifiez la valeur de la variable `dir_admin` !** Comme vous pouvez le remarquer : - il fait appel au fichier de configuration **dns.conf**, *publié ci-dessous* - et si la correspondance des enregistrements TLSA ne se fait pas, il appelle le script pdksh **dns.ksh** avec l'argument `tlsa` suivi du nom de domaine à cibler ### dns.conf Voici le fichier de configuration qui sert à la fois pour le script shell **tlsa.sh** et le script pdksh **dns.ksh**. ```conf ######################################################################## # # Author: Stéphane HUC # mail: devs@stephane-huc.net # gpg:fingerprint: CE2C CF7C AB68 0329 0D20 5F49 6135 D440 4D44 BD58 # # License: BSD Simplified # # Github: https://framagit.org/hucste/AH.git # # Date: 2022/06/01 07:20 # ######################################################################## ### ## # Config file to dns.ksh script ## ### ######################################################################## ### Algorithm ## values: sha256, sha512; choose-it segun TLSA Method algo="sha256" ### SOA Serial type ## values: date, timestamp ## DNS recommandation: prefer date SOA_serial_type="date" ### Port number tls_port=443 ### Protocols ## values: stcp, tcp, udp tls_proto="tcp" ### TLSA ## Lets Encrypt Recommandation; ## see: https://community.letsencrypt.org/t/please-avoid-3-0-1-and-3-0-2-dane-tlsa-records-with-le-certificates/7022 ## usage: Lets Encrypt recommands 3, at least 2 ## values: 0 => 3; or (PKIX-TA, PKIX-EE, DANE-TA, DANE-EE; respectivly: 0 -> 3) tlsa_usage=3 ## selector: Lets Encrypt recommands 1 ## values: 0 or 1; or (CERT, SPKI; respectively: O or 1) tlsa_selector=1 ## method: Lets Encrypt recommands 1 ## values: 0 => 2; or (FULL, SHA256, SHA512; respectively: 0 -> 2) # this change segun algo tlsa_method=1 ### Key Type Letsencrypt ## rsa or ecdsa ## if ecdsa, specify elliptic curve: secp256r1, secp384r1, secp512r1 (256 is enough) le_key_type=ecdsa le_curve=secp256r1 ``` Personnellement, je fais le choix d'utiliser : - l'algorithme **sha512**. - le type de clé **ecdsa** qui déclenche l'utilisation de la commande **ec** lors de l'utilisation d'openssl, par les scripts **dns.ksh** ou **tlsa.sh**. Il est bien sûr possible d'utiliser **rsa** ; dans ce cas, les scripts shell n'utiliseront pas la variable `le_curve`. ### dns.ksh Ce script long et complexe est capable de : - générer les signatures DNSSEC pour la zone DNS d'un domaine cible. - modifier l'enregistrement TLSA puis regénérer les signatures DNSSEC ; dans ce cas : - récupèrer l'enregistrement de numéro de série SOA en cours dans la zone DNS du domaine cible - créer un nouveau fichier de zone DNS et sauvegarder l'actuel - si le nouveau fichier de zone DNS est accessible, le script écrit : - un nouveau numéro de série SOA - remplace l'ancien enregistrement TLSA par le nouveau - essaye de signer à nouveau la zone DNS à l'aide du protocole DNSSEC - s'il réussit, il supprime l'ancien fichier de zone DNS - s'il échoue, il avertit, arrête son exécution **et** dans ce cas, il faudra renommer à la main la sauvegarde de la zone DNS pour en faire à nouveau l'actuelle, puis chercher à comprendre pourquoi le script à échouer - dans ce cas-là, il est intéressant de positionner la variable `debug` à `1`, ce qui activera la journalisation des différentes étapes, et pourra aider à l'analyse, lors d'une exécution manuelle. Un petit coup de `./dns.ksh help` vous en dira peut-être plus sur son utilisation. ;) ```ksh #!/bin/ksh set -e #set -x ################################################################################ # # Author: Stéphane HUC # mail: devs@stephane-huc.net # gpg:fingerprint: CE2C CF7C AB68 0329 0D20 5F49 6135 D440 4D44 BD58 # # License: BSD Simplified # # Github: # # Date: 2022/06/01 07:25 # ################################################################################ # # Purpose: to add a TLSA Record into DNS zone, segun your cert TLS (LE) # - for the geek: DANE-TLSA... #### IMPORTANT: recreate your TLSA Record after (re?)new cert... # # Needed tools: nsd* and ldnscript ## ldnscript is a tool to sign dns zone. (DNSSEC) ## https://framagit.org/22decembre/ldnscripts.git # # OS: Tested on OpenBSD # ################################################################################ ROOT="$(dirname "$(readlink -f -- "$0")")" . "${ROOT}/dns.conf" ################################################################################ ### ## # DONT TOUCH THOSES VARIABLES! ## ### ################################################################################ debug=0 dir_le="/etc/letsencrypt/live" dir_ns_cfg="/etc/ns" # folder config ns dir_sbin="/usr/local/sbin" log="${ROOT}/dns-script.log" nsd_cfg="/var/nsd/etc/nsd.conf" timestamp="$(date +%s)" today="$(date +"%Y%m%d")" server="nsd" SOA_ns="" tlsa_record="" set -A tlsa_records # if X509 DNS Alternative Names > 1 set -A tlsa_method_names -- "FULL" "SHA256" "SHA512" set -A tlsa_selector_names -- "CERT" "SPKI" set -A tlsa_usage_names -- "PKIX-TA" "PKIX-EE" "DANE-TA" "DANE-EE" NB_PARAMS="$#" set -A PARAMS -- "$@" ROOT="$(dirname "$(readlink -f -- "$0")")" if [ -z "${PARAMS[0]}" ]; then MENU_CHOICE="help" else PARAMS[0]="$(printf '%s' "${PARAMS[0]}" | tr -s "[:upper:]" "[:lower:]")" MENU_CHOICE=${PARAMS[0]} fi [ -n "${PARAMS[1]}" ] && domain="$(printf '%s' "${PARAMS[1]}" | tr -s "[:upper:]" "[:lower:]")" ################################################################################ #### ## # All needed functions! DO NOT TOUCH-IT! ## ### ################################################################################ _add_tlsa() { check_var_algo check_var_soa_serial_type check_var_tls_port check_var_tls_proto check_tlsa_methods check_tlsa_selectors check_tlsa_usages get_soa_ns danefile="${zonefile}.dane" newzonefile="${zonefile}.${today}" oldzonefile="${zonefile}.${OLD_SOA_sn}" create_new_filezone if [ -f "${newzonefile}" ]; then write_soa_serial_number build_tlsa_record write_tlsa_record mv_new_file_zone if _resign; then del_old_zonefile; fi fi unset danefile newzonefile oldzonefile } build_needed_variables() { check_var_domain printf '%s\n' "*** Build needed variables:" # build cert variable if menu 'tlsa' if [ "${MENU_CHOICE}" = "tlsa" ]; then cert="${dir_le}/${domain}/cert.pem" if [ ! -f "${cert}" ]; then display_mssg "KO" "*** It seems cert file not exists!" byebye else printf '%s\n' "cert: ${cert}" fi fi # build zonedir and zonefile variables zonedir="$(awk -F '"' '/zonesdir/ { print substr($2,-1) }' "${nsd_cfg}")" if [ -z "${zonedir}" ]; then zonedir="/var/nsd/zones/"; fi printf '%s\n' "zonedir: ${zonedir}" #zonefile="$(awk -F '"' '/zonefile: "[a-z]*\/'"${domain}"'"/ { print substr($2, -1) }' "${nsd_cfg}")" #if [ "$(printf '%s' "${zonefile}" | awk -F'/' '{ print $1}')" == "signed" ]; then ##zonefilesigned=$zonefile #zonefile="$(find "${dir_ns_cfg}" -name "${domain}")" #if [ -z "${zonefile}" ]; then zonefile="$(find "${zonedir}" -name "${domain}")"; fi #if [ -z "${zonefile}" ]; then #display_mssg "KO" "ERROR: It seems zonefile for domain: '${domain}' not exists!" #byebye #fi #else #zonefile="${zonedir}${zonefile}" #fi zonefile="${dir_ns_cfg}/${domain}" printf '%s\n' "zonefile: ${zonefile}" } build_tlsa_record() { # get TLSA by reading cert pem case "${le_key_type}" in "ecdsa") command="ec" ;; "rsa") command="rsa" ;; esac tlsa_cert_associated="$(openssl x509 -noout -pubkey -in "${cert}" | openssl "${command}" -pubin -outform der 2>/dev/null | "${algo}")" _log "TLSA Cert Associated: ${tlsa_cert_associated}" unset command if [ -z "${tlsa_cert_associated}" ]; then display_mssg "KO" "ERROR: TLSA Cert Associated could not generated!" byebye else # rebuild tlsa method segun algo choosed; possible: 0 (no match), 1 (sha256), 2 (sha512) case "${algo}" in "sha256") tlsa_method=1 ;; "sha512") tlsa_method=2 ;; *) tlsa_method=0 ;; esac if [ "${tls_port}" = "443" ] && [ "${tls_proto}" = "tcp" ]; then get_dns_alternative_names count="${#domains[@]}" if [ "${count}" -eq 1 ]; then set_tlsa_record else set_tlsa_records fi fi fi unset tlsa_cert_associated } byebye() { display_mssg "KO" "Script stop here!" display_mssg "KO" "Please, search to understand reasons." exit 1 } check_domain_name() { pattern="^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$" # like RFC 1123 if [ ${#domain} -gt 67 ]; then # Larg domain name <= 67 display_mssg "KO" "Error: Domain Length: ${domain}; >= 67 carachters!" byebye fi if printf '%s\n' "${domain}" | grep -Eio "${pattern}"; then display_mssg "OK" "Domain Name: ${domain} is valid!" sleep 1 else display_mssg "KO" "Error: Bad Domain Name: ${domain}" byebye fi unset pattern } check_tlsa_methods() { case "${tlsa_method}" in 0|"FULL") tlsa_method=0 ;; 1|"SHA256") tlsa_method=1 ;; 2|"SHA512") tlsa_method=2 ;; *) display_mssg "KO" "/!\ The TLSA Method: not correctly configurated!" byebye :: esac _log "TLSA Method: ${tlsa_method}" } check_tlsa_selectors() { case "${tlsa_selector}" in 0|"CERT") tlsa_selector=0 ;; 1|"SPKI") tlsa_selector=1 ;; *) display_mssg "KO" "/!\ The TLSA Selector: not correctly configurated!" byebye ;; esac _log "TLSA Selector: ${tlsa_selector}" } check_tlsa_usages() { case "${tlsa_usage}" in 0|"PKIX-TA") tlsa_usage=0 ;; 1|"PKIX-EE") tlsa_usage=1 ;; 2|"DANE-TA") tlsa_usage=2 ;; 3|"DANE-EE") tlsa_usage=3 ;; *) display_mssg "KO" "/!\ The TLSA Usage: not correctly configurated!" byebye ;; esac _log "TLSA Usage: ${tlsa_usage}" } check_uid() { if [ "$(id -u)" -ne 0 ]; then display_mssg "KO" "ERROR: Script not launch with rights admin!" byebye fi } check_var_algo () { if [ "${algo}" != "sha256" ] && [ "${algo}" != "sha512" ]; then display_mssg "KO" "/!\ Algorythm: not correctly configurated!" byebye fi _log "Algo: ${algo}" } check_var_domain() { if [ -z "${domain}" ]; then display_mssg "KO" "*** It seems fault informations!" help byebye fi _log "Domain: ${domain}" } check_var_soa_serial_type() { if [ "${SOA_serial_type}" != "date" ] && [ "${SOA_serial_type}" != "timestamp" ]; then display_mssg "KO" "/!\ SOA Serial Type: not correctly configurated!" byebye fi _log "SOA Serial Type: ${SOA_serial_type}" } check_var_tls_port(){ if [ "${tls_port}" -lt 0 ]; then display_mssg "KO" "/!\ TLS port: not correctly configurated!" byebye fi _log "TLS port: ${tls_port}" } check_var_tls_proto() { if [ "${tls_proto}" != "sctp" ] && [ "${tls_proto}" != "tcp" ] && [ "${tls_proto}" != "tcp" ]; then display_mssg "KO" "/!\ TLS proto: not correctly configurated!" byebye fi _log "TLS proto: ${tls_proto}" } checkconf() { nsd-checkconf "${nsd_cfg}" } checkzone() { nsd-checkzone "${domain}" "${zonefile}" } confirm () { read -r response?"${1} [y|n] " case "${response}" in # 'o', 'O': Oui and not 0! y|Y|o|O|1) true ;; *) false ;; esac unset response } create_new_filezone() { cp "${zonefile}" "${newzonefile}" } del_old_zonefile() { if [ -f "${oldzonefile}" ]; then rm -fP "${oldzonefile}" fi } display_mssg() { typeset statut info text statut="$1" info="$2" case "${statut}" in "KO") text="[ ${red}${statut}${neutral} ] ${info}" ;; "OK") text="[ ${green}${statut}${neutral} ] ${info}" ;; #*) mssg="${text}" ;; esac printf "%s \n" "${text}" unset info statut text } get_dns_alternative_names() { # get "X509 DNS Alternative Names" characters domains="$(echo | openssl x509 -text -noout -in "${cert}" | awk -F ',' '/DNS:/ { for(i=1;i> "${log}"; fi } main() { check_uid verify_need_softs build_needed_variables check_domain_name case "${MENU_CHOICE}" in "help") help ;; "sign") _resign ;; "tlsa") _add_tlsa ;; *) display_mssg "KO" "ERROR: this option ${MENU_CHOICE} is not exists!" help byebye ;; esac } mv_new_file_zone() { if [ -f "${newzonefile}" ]; then mv "${zonefile}" "${oldzonefile}" mv "${newzonefile}" "${zonefile}" fi } _resign() { if checkzone && checkconf; then display_mssg "OK" "file config nsd and zone ${domain} are good! :D" sign_zone else display_mssg "KO" "ERROR: it exists a problem with file config nsd or zone ${domain}" byebye fi } restart_server() { printf '%s\n' "=> Restart Server: " stop_server start_server status_server } set_soa_serial_number() { case "${SOA_serial_type}" in "date") SOA_date="$(printf '%s' "${OLD_SOA_sn}" | awk '{print substr($0, 0, 8)}')"; SOA_number="$(printf '%s' "${OLD_SOA_sn}" | awk '{print substr($0, 9)}')"; if [ "${SOA_date}" == "${today}" ]; then #let SOA_number=$SOA_number+1 (( SOA_number=${SOA_number}+1 )) || true if [ "${SOA_number}" -lt 10 ]; then SOA_number="0${SOA_number}"; fi SOA_sn="${SOA_date}${SOA_number}" else SOA_sn="${today}01" fi ;; "timestamp") SOA_sn="${timestamp}" ;; *) display_mssg "KO" "Invalid SOA Serial Type!" byebye ;; esac _log "New SOA Serial Number: ${SOA_sn}!" } set_tlsa_record() { # build tlsa record tlsa_records[0]="_${tls_port}._${tls_proto}.${domains[0]}. IN TLSA ${tlsa_usage} ${tlsa_selector} ${tlsa_method} ${tlsa_cert_associated}" _log "TLSA Record: ${tlsa_records[0]}" } set_tlsa_records() { # do not use domain variable here i=0 for dom in "${domains[@]}"; do tlsa_records[$i]="_${tls_port}._${tls_proto}.${dom}. IN TLSA ${tlsa_usage} ${tlsa_selector} ${tlsa_method} ${tlsa_cert_associated}" (( i=i+1 )) done unset i dom _log "TLSA Records: ${tlsa_records[*]}" } sign_zone() { "${dir_sbin}"/ldnscript signing "${domain}" } start_server() { printf '%s\n' "Start serveur: ${server}" rcctl start "${server}" sleep 1s } status_server() { printf '%s\n' "Check serveur: ${server}" rcctl check "${server}" } stop_server() { printf '%s\n' "Stop serveur: ${server}" rcctl stop "${server}" sleep 1s } verify_need_softs() { if [ ! -f "${dir_sbin}/ldnscript" ]; then display_mssg "KO" "ERROR: ldnscript seems not install!" byebye elif [ ! -x "${dir_sbin}/ldnscript" ]; then display_mssg "KO" "ERROR: ldnscript is not executable!" byebye fi } write_soa_serial_number() { set_soa_serial_number if sed -i -e "s#\(.*\)${OLD_SOA_sn} \;#\1${SOA_sn} \;#" "${newzonefile}"; then _log "SOA serial number changed!" else display_mssg "KO" "/!\ Script cant change SOA serial number!" fi } write_tlsa_record() { if [ ! -f "${danefile}" ]; then touch "${danefile}"; fi i=0 # add tlsa records into dns zone for tlsa_record in "${tlsa_records[@]}"; do dom="${domains[$i]}" _log "domain: $dom" ### /!\ ERROR with $domain /!\ if sed -i -e "s#_${tls_port}._${tls_proto}.${dom}. IN TLSA\(.*\)#${tlsa_record}#" "${newzonefile}"; then _log "TLSA Record rewrited!" else printf '%s\n' "${tlsa_record}" >> "${newzonefile}" _log "TLSA Record added!" fi (( i=i+1 )) # add record in first line into dane file printf '%s\n' "${timestamp}:${tlsa_record}" >> "${danefile}" _log "${timestamp}:${tlsa_record}" unset dom done unset i tlsa_record } ################################################################################ main ``` --- ## EOD **End Of Documentation** Voilà mon processus de gestion de mes zones DNS, avec le protocole DNSSEC et les enregistrements TLSA pour le service web. D'un processus long et complexe, je peux ainsi gérer simplement à l'aide de deux commandes bien utiles : - signer par DNNSEC mes zones DNS : ```sh ./dns.ksh sign domain ``` - tester les enregistrements TLSA à tout moment, et si besoin les regénérer : ```sh ./tlsa.sh domain ``` Mais si vous avez bien compris, c'est le cron mensuel qui s'en occupe tout seul et qui me fait le rapport adéquat, ainsi je sais comment cela s'est exécuté. ---