mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-10-27 14:41:28 +01:00
Replace error/notice by [ALERT]/[WARNING]/[NOTICE] like it's done in haproxy. ALERT means a failure and the program will exit 1 just after it WARNING will continue the execution of the program NOTICE will continue the execution as well
231 lines
5.4 KiB
Bash
Executable File
231 lines
5.4 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Dump certificates from the HAProxy stats or master socket to the filesystem
|
|
# Experimental script
|
|
#
|
|
|
|
set -e
|
|
|
|
export BASEPATH=${BASEPATH:-/etc/haproxy}/
|
|
export SOCKET=${SOCKET:-/var/run/haproxy-master.sock}
|
|
export DRY_RUN=0
|
|
export DEBUG=
|
|
export VERBOSE=
|
|
export M="@1 "
|
|
export TMP
|
|
|
|
vecho() {
|
|
|
|
[ -n "$VERBOSE" ] && echo "$@"
|
|
return 0
|
|
}
|
|
|
|
read_certificate() {
|
|
name=$1
|
|
crt_filename=
|
|
key_filename=
|
|
|
|
OFS=$IFS
|
|
IFS=":"
|
|
|
|
while read -r key value; do
|
|
case "$key" in
|
|
"Crt filename")
|
|
crt_filename="${value# }"
|
|
key_filename="${value# }"
|
|
;;
|
|
"Key filename")
|
|
key_filename="${value# }"
|
|
;;
|
|
esac
|
|
done < <(echo "${M}show ssl cert ${name}" | socat "${SOCKET}" -)
|
|
IFS=$OFS
|
|
|
|
if [ -z "$crt_filename" ] || [ -z "$key_filename" ]; then
|
|
return 1
|
|
fi
|
|
|
|
# handle fields without a crt-base/key-base
|
|
[ "${crt_filename:0:1}" != "/" ] && crt_filename="${BASEPATH}${crt_filename}"
|
|
[ "${key_filename:0:1}" != "/" ] && key_filename="${BASEPATH}${key_filename}"
|
|
|
|
vecho "name:$name"
|
|
vecho "crt:$crt_filename"
|
|
vecho "key:$key_filename"
|
|
|
|
export NAME="$name"
|
|
export CRT_FILENAME="$crt_filename"
|
|
export KEY_FILENAME="$key_filename"
|
|
|
|
return 0
|
|
}
|
|
|
|
cmp_certkey() {
|
|
prev=$1
|
|
new=$2
|
|
|
|
if [ ! -f "$prev" ]; then
|
|
return 1;
|
|
fi
|
|
|
|
if ! cmp -s <(openssl x509 -in "$prev" -noout -fingerprint -sha256) <(openssl x509 -in "$new" -noout -fingerprint -sha256); then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
dump_certificate() {
|
|
name=$1
|
|
prev_crt=$2
|
|
prev_key=$3
|
|
r="tmp.${RANDOM}"
|
|
d="old.$(date +%s)"
|
|
new_crt="$TMP/$(basename "$prev_crt").${r}"
|
|
new_key="$TMP/$(basename "$prev_key").${r}"
|
|
|
|
if ! touch "${new_crt}" || ! touch "${new_key}"; then
|
|
echo "[ALERT] ($$) : can't dump \"$name\", can't create tmp files" >&2
|
|
return 1
|
|
fi
|
|
|
|
echo "${M}dump ssl cert ${name}" | socat "${SOCKET}" - | openssl pkey >> "${new_key}"
|
|
# use crl2pkcs7 as a way to dump multiple x509, storeutl could be used in modern versions of openssl
|
|
echo "${M}dump ssl cert ${name}" | socat "${SOCKET}" - | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs >> "${new_crt}"
|
|
|
|
if ! cmp -s <(openssl x509 -in "${new_crt}" -pubkey -noout) <(openssl pkey -in "${new_key}" -pubout); then
|
|
echo "[ALERT] ($$) : Private key \"${new_key}\" and public key \"${new_crt}\" don't match" >&2
|
|
return 1
|
|
fi
|
|
|
|
if cmp_certkey "${prev_crt}" "${new_crt}"; then
|
|
echo "[NOTICE] ($$) : ${crt_filename} is already up to date" >&2
|
|
return 0
|
|
fi
|
|
|
|
# move the current certificates to ".old.timestamp"
|
|
if [ -f "${prev_crt}" ] && [ -f "${prev_key}" ]; then
|
|
mv "${prev_crt}" "${prev_crt}.${d}"
|
|
[ "${prev_crt}" != "${prev_key}" ] && mv "${prev_key}" "${prev_key}.${d}"
|
|
fi
|
|
|
|
# move the new certificates to old place
|
|
mv "${new_crt}" "${prev_crt}"
|
|
[ "${prev_crt}" != "${prev_key}" ] && mv "${new_key}" "${prev_key}"
|
|
|
|
return 0
|
|
}
|
|
|
|
dump_all_certificates() {
|
|
echo "${M}show ssl cert" | socat "${SOCKET}" - | grep -v '^#' | grep -v '^$' | while read -r line; do
|
|
export NAME
|
|
export CRT_FILENAME
|
|
export KEY_FILENAME
|
|
|
|
if read_certificate "$line"; then
|
|
[ "${DRY_RUN}" = "0" ] && dump_certificate "$NAME" "$CRT_FILENAME" "$KEY_FILENAME"
|
|
else
|
|
echo "[WARNING] ($$) : can't dump \"$name\", crt/key filename details not found in \"show ssl cert\"" >&2
|
|
fi
|
|
|
|
done
|
|
}
|
|
|
|
usage() {
|
|
echo "Usage:"
|
|
echo " $0 [options]* [cert]*"
|
|
echo ""
|
|
echo " Dump certificates from the HAProxy stats or master socket to the filesystem"
|
|
echo " Require socat and openssl"
|
|
echo " EXPERIMENTAL script, backup your files!"
|
|
echo " The script will move your previous files to FILE.old.unixtimestamp (ex: foo.com.pem.old.1759044998)"
|
|
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${SOCKET})"
|
|
echo " -s, --socket <path> Use the stats socket at <path>"
|
|
echo " -p, --path <path> Specifiy a base path for relative files (default: ${BASEPATH})"
|
|
echo " -n, --dry-run Read certificates on the socket but don't dump them"
|
|
echo " -d, --debug Debug mode, set -x"
|
|
echo " -v, --verbose Verbose mode"
|
|
echo " -h, --help This help"
|
|
echo " -- End of options"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 -v -p ${BASEPATH} -S ${SOCKET}"
|
|
echo " $0 -v -p ${BASEPATH} -S ${SOCKET} bar.com.rsa.pem"
|
|
echo " $0 -v -p ${BASEPATH} -S ${SOCKET} -- foo.com.ecdsa.pem bar.com.rsa.pem"
|
|
}
|
|
|
|
main() {
|
|
while [ -n "$1" ]; do
|
|
case "$1" in
|
|
-S|--master-socket)
|
|
SOCKET="$2"
|
|
M="@1 "
|
|
shift 2
|
|
;;
|
|
-s|--socket)
|
|
SOCKET="$2"
|
|
M=
|
|
shift 2
|
|
;;
|
|
-p|--path)
|
|
BASEPATH="$2/"
|
|
shift 2
|
|
;;
|
|
-n|--dry-run)
|
|
DRY_RUN=1
|
|
shift
|
|
;;
|
|
-d|--debug)
|
|
DEBUG=1
|
|
shift
|
|
;;
|
|
-v|--verbose)
|
|
VERBOSE=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage "$@"
|
|
exit 0
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
-*)
|
|
echo "[ALERT] ($$) : Unknown option '$1'" >&2
|
|
usage "$@"
|
|
exit 1
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -n "$DEBUG" ]; then
|
|
set -x
|
|
fi
|
|
|
|
TMP=${TMP:-$(mktemp -d)}
|
|
|
|
if [ -z "$1" ]; then
|
|
dump_all_certificates
|
|
else
|
|
# compute the certificates names at the end of the command
|
|
while [ -n "$1" ]; do
|
|
if ! read_certificate "$1"; then
|
|
echo "[ALERT] ($$) : can't dump \"$1\", crt/key filename details not found in \"show ssl cert\"" >&2
|
|
exit 1
|
|
fi
|
|
[ "${DRY_RUN}" = "0" ] && dump_certificate "$NAME" "$CRT_FILENAME" "$KEY_FILENAME"
|
|
shift
|
|
done
|
|
fi
|
|
}
|
|
|
|
trap 'rm -rf -- "$TMP"' EXIT
|
|
main "$@"
|