Skip to content

Commit

Permalink
letsencrypt-tlsa.sh fix TLSA creation & add features
Browse files Browse the repository at this point in the history
- Fix TLSA record creation.
- Support both certificate mode.
- Support matching type selection.
- Support custom labels.
- Remove duplicates.
  • Loading branch information
oh2fih committed Jul 23, 2024
1 parent 7229874 commit ba08568
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 25 deletions.
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,28 @@ Miscellaneous scripts for different purposes. Mostly unrelated to each other.

| Category | Script & Language | Purpose & Usage |
|:---|:---|:---|
| Automation | [`find-inactive-ssh-sessions.sh`](bin/find-inactive-ssh-sessions.sh) <br> Shell (bash) | Find inactive (idle) SSH sessions or kill (`-k`) them.<br>`find-inactive-ssh-sessions.sh [-k] [-i seconds] [-s]`<br>Could be used as a [workaround](https://serverfault.com/a/1162840/274176) for OpenSSH < 9.2 that did not have the [sshd_config(5)](https://man.openbsd.org/sshd_config) keywords `ChannelTimeout` & `UnusedConnectionTimeout`.|
| DNS <br> DANE | [`letsencrypt-tlsa.sh`](bin/letsencrypt-tlsa.sh) <br> Shell (bash) | Create TLSA records from the current & backup Let's Encrypt Intermediate CAs. |
| Email | [`mail-prepender.sh`](bin/mail-prepender.sh) <br> Shell (bash) | Prepends (to stdin/stdout) email header strings given in as flags `i`, `I`, `a`, or `A`; after possible mbox `From` & `Return-Path` header lines. Intended as a limited `formail` replacement that ignores the nyanses of the flags and simply prepends the valid (RFC 5322, 2.2) non-empty headers keeping the other headers as is. Flags `x` & `X` are implemented. Any other flags are ignored. |
| Git | [`git-find-commits-by-file-hash.sh`](bin/git-find-commits-by-file-hash.sh) <br> Shell (bash) | Search Git repository history for commits with SHA-256 checksum of a file. Answers the question "Has this version of this file ever been committed as the file on this path of this Git repository?" and shows a summary (`git show --stat`) of the matching commit(s). The `path` should be relative to the repository root. <br> `git-find-commits-by-file-hash.sh sha256sum path`|
| Infosec | [`netcat-proxy.sh`](bin/netcat-proxy.sh) <br> Shell (sh) | Creates a simple persistent TCP proxy with netcat & named pipes. <br> `netcat-proxy.sh listenport targethost targetport` |
| Infosec | [`partialpassword.sh`](bin/partialpassword.sh) <br> Shell (bash) | Creates a new wordlist from a wordlist by replacing all ambiguous characters with all their possible combinations. <br> `partialpassword.sh input.txt output.txt O0 [Il1 ...]` |
| Infosec | [`duplicate-ssh-hostkeys.sh`](bin/duplicate-ssh-hostkeys.sh) <br> Shell (bash) | Find duplicate SSH host keys in a CIDR range. Examine your network for shared host keys that could potentially be dangerous.<br> `duplicate-ssh-hostkeys.sh CIDR [HostKeyAlgorithm ...]` |
| Infosec <br> Automation | [`make-mac-prefixes.py`](bin/make-mac-prefixes.py) <br> Python 3 | Processes registered MAC address prefixes from [IEEE MA-L Assignments (CSV)](https://standards.ieee.org/products-programs/regauth/) (stdin) to Nmap's [`nmap-mac-prefixes`](https://github.com/nmap/nmap/blob/master/nmap-mac-prefixes) (stdout) with a few additional unregistered OUIs.<br> `curl https://standards-oui.ieee.org/oui/oui.csv \| make-mac-prefixes.py > nmap-mac-prefixes` |
| WordPress | [`test-cache-enabler.py`](bin/test-cache-enabler.py) <br> Python 3 | Tests whether the Cache Enabler by KeyCDN (WordPress) is working properly on the URLs given as arguments. <br> `test-cache-enabler.py https://example.com [...]` |
| Web | [`detect-modified-html-element.sh`](bin/detect-modified-html-element.sh) <br> Shell (bash) | Checks HTML element changes on a web page since last run. <br> Recommended to be executed as a SystemD [service](systemd/detect-modified-html-element.service.example). |
| Web | <del>`koronarokotusaika.sh`</del> <br> Shell (bash) | This script has been removed as koronarokotusaika.fi (bookcovidvaccine.fi) has been shut down on April 28, 2023. |
| Web | [`xxl-product-pricelimiter.sh`](bin/xxl-product-pricelimiter.sh) <br> Shell (bash) | XXL.fi product price checker / limiter. <br> `xxl-product-pricelimiter.sh XXL.fi-ProductURL MaxPrice` |
| Automation | [`find-inactive-ssh-sessions.sh`](bin/find-inactive-ssh-sessions.sh)<br>Shell (bash) | Find inactive (idle) SSH sessions or kill (`-k`) them.<br>`find-inactive-ssh-sessions.sh [-k] [-i seconds] [-s]`<br>Could be used as a [workaround](https://serverfault.com/a/1162840/274176) for OpenSSH < 9.2 that did not have the [sshd_config(5)](https://man.openbsd.org/sshd_config) keywords `ChannelTimeout` & `UnusedConnectionTimeout`.|
| DNS<br>DANE | [`letsencrypt-tlsa.sh`](bin/letsencrypt-tlsa.sh)<br>Shell (bash) | Create TLSA records from the current & backup Let's Encrypt Intermediate CAs.<br>Defaults to `le-ca TLSA 2 1 1` with configurable selector (`-f`) & matching type (`-m`).<br>`letsencrypt-tlsa.sh [-f] [-m N] [-l "label [TTL]"] [-h] [2>/dev/null]`|
| Email | [`mail-prepender.sh`](bin/mail-prepender.sh)<br>Shell (bash) | Prepends (to stdin/stdout) email header strings given in as flags `i`, `I`, `a`, or `A`; after possible mbox `From` & `Return-Path` header lines. Intended as a limited `formail` replacement that ignores the nyanses of the flags and simply prepends the valid (RFC 5322, 2.2) non-empty headers keeping the other headers as is. Flags `x` & `X` are implemented. Any other flags are ignored. |
| Git | [`git-find-commits-by-file-hash.sh`](bin/git-find-commits-by-file-hash.sh)<br>Shell (bash) | Search Git repository history for commits with SHA-256 checksum of a file. Answers the question "Has this version of this file ever been committed as the file on this path of this Git repository?" and shows a summary (`git show --stat`) of the matching commit(s). The `path` should be relative to the repository root.<br>`git-find-commits-by-file-hash.sh sha256sum path`|
| Infosec | [`netcat-proxy.sh`](bin/netcat-proxy.sh)<br>Shell (sh) | Creates a simple persistent TCP proxy with netcat & named pipes.<br>`netcat-proxy.sh listenport targethost targetport` |
| Infosec | [`partialpassword.sh`](bin/partialpassword.sh)<br>Shell (bash) | Creates a new wordlist from a wordlist by replacing all ambiguous characters with all their possible combinations.<br>`partialpassword.sh input.txt output.txt O0 [Il1 ...]` |
| Infosec | [`duplicate-ssh-hostkeys.sh`](bin/duplicate-ssh-hostkeys.sh)<br>Shell (bash) | Find duplicate SSH host keys in a CIDR range. Examine your network for shared host keys that could potentially be dangerous.<br>`duplicate-ssh-hostkeys.sh CIDR [HostKeyAlgorithm ...]` |
| Infosec<br>Automation | [`make-mac-prefixes.py`](bin/make-mac-prefixes.py)<br>Python 3 | Processes registered MAC address prefixes from [IEEE MA-L Assignments (CSV)](https://standards.ieee.org/products-programs/regauth/) (stdin) to Nmap's [`nmap-mac-prefixes`](https://github.com/nmap/nmap/blob/master/nmap-mac-prefixes) (stdout) with a few additional unregistered OUIs.<br>`curl https://standards-oui.ieee.org/oui/oui.csv \| make-mac-prefixes.py > nmap-mac-prefixes` |
| WordPress | [`test-cache-enabler.py`](bin/test-cache-enabler.py)<br>Python 3 | Tests whether the Cache Enabler by KeyCDN (WordPress) is working properly on the URLs given as arguments.<br>`test-cache-enabler.py https://example.com [...]` |
| Web | [`detect-modified-html-element.sh`](bin/detect-modified-html-element.sh)<br>Shell (bash) | Checks HTML element changes on a web page since last run.<br>Recommended to be executed as a SystemD [service](systemd/detect-modified-html-element.service.example). |
| Web | <del>`koronarokotusaika.sh`</del><br>Shell (bash) | This script has been removed as koronarokotusaika.fi (bookcovidvaccine.fi) has been shut down on April 28, 2023. |
| Web | [`xxl-product-pricelimiter.sh`](bin/xxl-product-pricelimiter.sh)<br>Shell (bash) | XXL.fi product price checker / limiter.<br>`xxl-product-pricelimiter.sh XXL.fi-ProductURL MaxPrice` |

## Scripts that require `sudo` privileges ([`sbin/`](sbin/))

| Category | Script & Language | Purpose & Usage |
|:---|:---|:---|
| Automation | [`autoreboot-on-segfaults.sh`](sbin/autoreboot-on-segfaults.sh) <br> Shell (sh) | Temporary solution that automatically reboots the system if there has been more than `MAX_SEGFAULTS` segmentation faults on the current boot. Fix the system! <br> Recommended to be scheduled with a SystemD [service](systemd/autoreboot-on-segfaults.service.example) & [timer](systemd/autoreboot-on-segfaults.service.example).<br> |
| Automation | [`backup-mysql-databases.sh`](sbin/backup-mysql-databases.sh) <br> Shell (bash) | Backup all MySQL/MariaDB databases; dump & compress. Overwrites older backups matching the same date pattern. Recommended to be scheduled with a SystemD [service](systemd/backup-mysql-databases.service.example) & [timer](systemd/backup-mysql-databases.timer.example).<br> |
| Automation | [`create-site.sh`](sbin/create-site.sh) <br> Shell (bash) | Web hosting automation for Debian with Apache2, PHP-FPM & Let's Encrypt. <br> `sudo create-site.sh username example.com [www.example.com ...]` |
| Firewall | [`list2bans.sh`](sbin/list2bans.sh) <br> Shell (bash) | Lists all Fail2Ban jail statuses or jails banning an IP. <br> `sudo list2bans.sh [ip]` |
| Firewall | [`unfail2ban.sh`](sbin/unfail2ban.sh) <br> Shell (bash) | Unbans the given IPs from all Fail2Ban jails. <br> `sudo unfail2ban.sh ip [ip ...]` |
| Automation | [`autoreboot-on-segfaults.sh`](sbin/autoreboot-on-segfaults.sh)<br>Shell (sh) | Temporary solution that automatically reboots the system if there has been more than `MAX_SEGFAULTS` segmentation faults on the current boot. Fix the system!<br>Recommended to be scheduled with a SystemD [service](systemd/autoreboot-on-segfaults.service.example) & [timer](systemd/autoreboot-on-segfaults.service.example).<br>|
| Automation | [`backup-mysql-databases.sh`](sbin/backup-mysql-databases.sh)<br>Shell (bash) | Backup all MySQL/MariaDB databases; dump & compress. Overwrites older backups matching the same date pattern. Recommended to be scheduled with a SystemD [service](systemd/backup-mysql-databases.service.example) & [timer](systemd/backup-mysql-databases.timer.example).<br>|
| Automation | [`create-site.sh`](sbin/create-site.sh)<br>Shell (bash) | Web hosting automation for Debian with Apache2, PHP-FPM & Let's Encrypt.<br>`sudo create-site.sh username example.com [www.example.com ...]` |
| Firewall | [`list2bans.sh`](sbin/list2bans.sh)<br>Shell (bash) | Lists all Fail2Ban jail statuses or jails banning an IP.<br>`sudo list2bans.sh [ip]` |
| Firewall | [`unfail2ban.sh`](sbin/unfail2ban.sh)<br>Shell (bash) | Unbans the given IPs from all Fail2Ban jails.<br>`sudo unfail2ban.sh ip [ip ...]` |

## Install & update

Expand Down
121 changes: 113 additions & 8 deletions bin/letsencrypt-tlsa.sh
Original file line number Diff line number Diff line change
@@ -1,22 +1,83 @@
#!/bin/bash
read -r -d '' USAGE << EOM
# ------------------------------------------------------------------------------
# Create TLSA records from the current & backup Let's Encrypt Intermediate CAs
#
# Usage: letsencrypt-tlsa.sh [-f] [-m N] [-l "label [TTL]"] [-h] [2>/dev/null]
#
# -f Full certificate mode (RFC 6698, 2.1.2 The Selector Field 0).
# Without this option, SubjectPublicKeyInfo (1) is used by default.
#
# -m Matching Type (RFC 6698, 2.1.3); defaults to SHA-256
# 0 Exact match on selected content
# 1 SHA-256 hash of selected content [RFC6234]
# 2 SHA-512 hash of selected content [RFC6234]
#
# -l Label (domain) part. Defaults to le-ca without FQDN.
# Can contain TTL after the label; this has no validation!
# * Example with FQDN, for SMTP: _25._tcp.example.com.
# * Example with TTL, for HTTPS: "_443._tcp.ecample.com. 3600"
#
# -h Help. Prints this and exits (ignoring all other options).
#
# Unique TLSA records will be printed to stdout, everything else to stderr.
# To get a clean output you can paste to your zone file, add 2>/dev/null.
#
# Author : Esa Jokinen (oh2fih)
# Home : https://github.com/oh2fih/Misc-Scripts
# ------------------------------------------------------------------------------
EOM

SOURCE="/certificates/"
BASE_URL="https://letsencrypt.org"

SELECTOR=1 # SubjectPublicKeyInfo
DIGEST=1 # SHA-256
LABEL="le-ca"

while getopts ":hfm:l:" opt; do
case ${opt} in
h)
echo -e "$USAGE" >&2
echo "# LE Chains of Trust page: ${BASE_URL}${SOURCE}"
exit 1
;;
f)
SELECTOR=0
;;
m)
case $OPTARG in
0|1|2)
LABEL="$OPTARG"
;;
*)
echo "Invalid option: -m must be 0, 1 (SHA-256), or 2 (SHA-512)" >&2
exit 1
;;
esac
;;
l)
DOMAIN="$OPTARG"
;;
\?)
echo "Invalid option: $OPTARG" >&2
exit 1
;;
:)
echo "Invalid option: $OPTARG requires an argument" >&2
exit 1
;;
esac
done

# Check for requirements. Print all unmet requirements at once.

required_command() {
if ! command -v "$1" &> /dev/null; then
if [ -z ${2+x} ]; then
echo -e "\033[0;31mThis script requires ${1}!\033[0m" >&2
echo "This script requires ${1}!" >&2
else
echo -e "\033[0;31mThis script requires ${1} ${2}!\033[0m" >&2
echo "This script requires ${1} ${2}!" >&2
fi
((UNMET=UNMET+1))
fi
Expand All @@ -30,6 +91,10 @@ required_command "grep"
required_command "sed"
required_command "awk"

if (( DIGEST == 0 )); then
required_command "hexdump" "for -m 0"
fi

if [ "$UNMET" -gt 0 ]; then
exit 1
fi
Expand All @@ -48,16 +113,56 @@ if [ "$INTERMEDIATE_PATHS" = "" ]; then
exit 1
fi

# Helper functions to handle different options

extract_der() {
case "$1" in
0)
openssl x509 -outform DER
;;
1)
openssl x509 -noout -pubkey | openssl pkey -pubin -outform DER
;;
esac
}

digest() {
case "$1" in
0)
hexdump -ve '/1 "%02X"'
;;
1)
openssl dgst -sha256 -hex
;;
2)
openssl dgst -sha512 -hex
;;
esac
}

# Create TLSA records

declare -a RECORDS=()
while IFS= read -r path ; do
echo "[${BASE_URL}${path}]" >&2
PEM=$(curl --silent "${BASE_URL}${path}")
echo "[${BASE_URL}${path}]" >&2
if [[ "$PEM" =~ ^[-]+BEGIN[[:space:]]CERTIFICATE[-]+ ]]; then
echo "$PEM" \
| openssl x509 -outform DER \
| openssl dgst -sha256 -hex \
| awk '{print "le-ca TLSA 2 1 1", $NF}'
fi
TLSA=$(
echo "$PEM" \
| extract_der "$SELECTOR" \
| digest "$DIGEST" \
| sed -e 's/\(.*\)/\U\1/' \
| awk '{print "'"$LABEL"' TLSA 2 '"$SELECTOR"' '"$DIGEST"'", $NF}'
)

# Do not print duplicate records to stdout
if [[ ! ${RECORDS[*]} =~ $TLSA ]]; then
echo "$TLSA"
RECORDS+=("$TLSA")
else
echo "(${TLSA})" >&2
fi
else
echo "(Reply was not a PEM encoded certificate)" >&2
fi
done <<< "$INTERMEDIATE_PATHS"

0 comments on commit ba08568

Please sign in to comment.