Last active
February 5, 2024 18:02
-
-
Save QNimbus/010ccc77919e07292e2a21edcf78b94a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
## | |
## Title: deploy_traefik.sh | |
## Description: Script to deploy uploaded certificates for use with traefik container | |
## Author: B. van wetten | |
## Created date: 26-01-2020 | |
## Updated date: 07-02-2020 | |
## Version: 0.3.1 | |
## GitHub Gist: https://gist.github.com/QNimbus/010ccc77919e07292e2a21edcf78b94a | |
## | |
## Usage: deploy_traefik.sh -c certificates [-r] [-d destination] [-n container] | |
## deploy_traefik.sh --help | |
## To do: Also allow for a filename to be passed as a positional parameter to the '-c' argument | |
## Notes: For remote use (e.g. SSH) use the following command as example: | |
## /bin/cat | /bin/xargs -0 -I % /opt/ssl/scripts/deploy_traefik.sh -r -n traefik -d /opt/ssl/traefik -c --% | |
## | |
## You can use this in your 'authorized_keys' file to run this command remotely: | |
## command="/bin/cat | /bin/xargs -0 -I % /opt/ssl/scripts/deploy_traefik.sh -r -n traefik -d /opt/ssl/traefik -c % --",no-pty,from="<client-ip>" ssh-rsa AAAAB3******** | |
set -o nounset | |
# http://www.dwheeler.com/essays/filenames-in-shell.html | |
IFS=$'\n\t' | |
# Prevents environment variables from corrupting script | |
unset DST_DIR CONTAINER_NAME | |
# Shell utilities | |
CP=$(which cp); [[ $? != 0 ]] && echo "Command 'cp' not found" >&2 && exit 1 | |
MV=$(which mv); [[ $? != 0 ]] && echo "Command 'mv' not found" >&2 && exit 1 | |
RM=$(which rm); [[ $? != 0 ]] && echo "Command 'rm' not found" >&2 && exit 1 | |
FIND=$(which find); [[ $? != 0 ]] && echo "Command 'find' not found" >&2 && exit 1 | |
CHMOD=$(which chmod); [[ $? != 0 ]] && echo "Command 'chmod' not found" >&2 && exit 1 | |
XARGS=$(which xargs); [[ $? != 0 ]] && echo "Command 'xargs' not found" >&2 && exit 1 | |
DOCKER=$(which docker); [[ $? != 0 ]] && echo "Command 'docker' not found" >&2 && exit 1 | |
GETOPT=$(which getopt); [[ $? != 0 ]] && echo "Command 'getopt' not found" >&2 && exit 1 | |
# Initialize variables | |
_PWD=$(pwd) | |
_UID=$(id -u) | |
_GID=$(id -g) | |
_ME=$(basename "${0}") | |
_TMPFILE="tmp-${$}-${RANDOM}" | |
_CONTAINER_NAME_DEFAULT="traefik" | |
_TRAEFIK_CERT_FILE="cert.pem" | |
_TRAEFIK_KEY_FILE="key.pem" | |
_TRAEFIK_CHAIN_FILE="chain.pem" | |
_CERT_PERMS="0400" | |
_RFLAG=false | |
_DFLAG=false | |
# Option strings | |
SHORT=rhd:n: | |
LONG=restart,help,destination:,container: | |
########################################################################## | |
# Init # | |
########################################################################## | |
# Cleanup any temp files after script execution (traps signals: 0, 1, 2, 15) | |
trap '${FIND} . -maxdepth 1 -name "${_TMPFILE}*" -type f -print0 | ${XARGS} -I % -0 rm %' INT TERM HUP EXIT | |
# Read certificate from command line and remove from positional arguments | |
for((i=1; i<=$#; i++)) | |
do | |
_cur=$i | |
_next=$(expr $i + 1) | |
_param=${!_cur} | |
_next_param=${!_next:-} | |
if [[ "${_param}" =~ (^-c$|^--certificate$) && ! -z "${_next_param}" ]] | |
then | |
echo -en "${_next_param}" > "${_TMPFILE}.pem" | |
set -- "${@:1:_cur-1}" "${@:_cur+2}" | |
fi | |
done | |
# Get command line arguments | |
if [[ "${#}" -gt 0 ]] | |
then | |
getopt -T > /dev/null | |
if [ $? -eq 4 ]; then | |
# GNU enhanced getopt is available | |
_ARGS=$(${GETOPT} --name "$_ME" --long ${LONG} --options ${SHORT} -- "$@") | |
else | |
# Original getopt is available (no long option names, no whitespace, no sorting) | |
_ARGS=$(${GETOPT} ${SHORT} "$@") | |
fi | |
if [ $? -ne 0 ]; then | |
echo "$_ME: usage error (use -h for help)" >&2 | |
exit 2 | |
fi | |
fi | |
eval set -- "${_ARGS:-}" | |
############################################################################### | |
# Functions # | |
############################################################################### | |
# _print_usage() | |
# | |
# Usage: | |
# _print_usage | |
# | |
# Print the program usage information. | |
_print_usage() { | |
cat <<HEREDOC | |
___ ____ ___ _ ____ _ _ ___ ____ ____ ____ ____ _ _ _ | |
| \ |___ |__] | | | \_/ | |__/ |__| |___ |___ | |_/ | |
|__/ |___ | |___ |__| | ___ | | \ | | |___ | | | \_ | |
Usage: | |
${_ME} -c file [-r] [-d destination] [-n container] | |
${_ME} -h | |
Options: | |
-h, --help Show this screen | |
-r, --restart Restart Traefik docker container | |
-c, --certificate Certificate file | |
-n, --container Traefik docker container name | |
-d, --destination Destination folder | |
HEREDOC | |
exit ${1:-0} | |
} | |
# _openssl() | |
# | |
# Usage: | |
# _openssl variable value | |
# | |
# Runs the OpenSSL utility as a docker container | |
_openssl() { | |
$DOCKER run --rm -v "$_PWD:$_PWD" --user "${_UID}:${_GID}" --workdir="$_PWD" frapsoft/openssl "$@" | |
} | |
# _set_variable() | |
# | |
# Usage: | |
# _set_variable variable value | |
# | |
# Sets the variable to a value if not already set. Otherwise exits with an error message | |
_set_variable() | |
{ | |
local varname="$1" | |
shift | |
if [ -z "${!varname:-}" ]; then | |
# eval="${varname}=${@@Q}" | |
eval "$varname=\"$@\"" | |
else | |
echo "Error: $varname already set" | |
usage | |
fi | |
} | |
# _parse_commandline_arguments() | |
# | |
# Usage: | |
# _parse_commandline_arguments | |
# | |
# Parses and validates commandline arguments and populates appropriate variables. | |
_parse_commandline_arguments() | |
{ | |
while true; | |
do | |
case "${1:-}" in | |
-r|--restart) | |
_RFLAG=true | |
shift | |
;; | |
-n|--container) | |
[[ -z "${2}" || "${2}" == *[[:space:]]* || "${2}" == -* ]] && { echo "$_ME: $1 needs a value" >&2; _print_usage 1; } | |
_set_variable CONTAINER_NAME "${2}" | |
shift 2 | |
;; | |
-d|--destination) | |
[[ -z "${2}" || "${2}" == *[[:space:]]* || "${2}" == -* ]] && { echo "$_ME: $1 needs a value" >&2; _print_usage 1; } | |
_set_variable DST_DIR "${2}" | |
_DFLAG=true | |
shift 2 | |
;; | |
\?) echo "$_ME: Unknown option -$1" >&2; | |
exit 1 | |
;; | |
:) echo "$_ME: -$1 needs a value" >&2; | |
exit 1 | |
;; | |
*) shift | |
break | |
;; | |
esac | |
done | |
} | |
# _validate_parameters() | |
# | |
# Usage: | |
# _validate_parameters | |
# | |
# Performs several checks on the supplied command line arguments | |
_validate_parameters() { | |
# Check certificates | |
[[ ! -f "${_TMPFILE}.pem" || ! $(_openssl rsa -in "${_TMPFILE}.pem" -check 2> /dev/null) ]] && echo -e "$_ME: Invalid or no certificate supplied\n" && _print_usage 1; | |
# Check if container is running | |
CONTAINER_NAME=${CONTAINER_NAME:-$_CONTAINER_NAME_DEFAULT} | |
[[ -z "$(${DOCKER} ps -q -f name=${CONTAINER_NAME})" ]] && echo -e "$_ME: Docker container ${CONTAINER_NAME} is not running\n" && _print_usage 1; | |
# Check if destination was given as argument otherwise set destination to source | |
! "$_DFLAG" && DST_DIR="$_PWD" | |
# Remove trailing slash unless root | |
case $DST_DIR in | |
*[!/]*/) DST_DIR=${DST_DIR%"${DST_DIR##*[!/]}"};; | |
*[/]) DST_DIR="/";; | |
esac | |
# Check if destination dir argument exists as a directory | |
[[ ! -d "$DST_DIR" || ! -w "$DST_DIR" ]] && echo -e "Error: '${DST_DIR}' does not exist or current user does not have writing permissions\n" && _print_usage 1; | |
} | |
############################################################################### | |
# Main # | |
############################################################################### | |
# _main() | |
# | |
# Usage: | |
# _main [<options>] [<arguments>] | |
# | |
# Description: | |
# Entry point for the program, handling basic option parsing and dispatching. | |
_main() { | |
# Avoid complex option parsing when only one program option is expected. | |
if [[ "${@:-}" =~ -h|--help ]] | |
then | |
_print_usage | |
else | |
_parse_commandline_arguments "$@" | |
_validate_parameters | |
# Extract certificate, key and certificate authority chain | |
_openssl x509 -in "${_TMPFILE}.pem" -outform PEM -out "${_TMPFILE}.cert.pem" || exit 1 | |
_openssl pkey -out "${_TMPFILE}.key.pem" -in "${_TMPFILE}.pem" || exit 1 | |
_openssl crl2pkcs7 -nocrl -certfile "${_TMPFILE}.pem" > "${_TMPFILE}.pkcs7" || exit 1 | |
_openssl pkcs7 -print_certs -out "${_TMPFILE}.chain.pem" -in "${_TMPFILE}.pkcs7" || exit 1 | |
# Copy certificate files | |
${CP} -a "${_TMPFILE}.cert.pem" ${DST_DIR}/${_TRAEFIK_CERT_FILE} || exit 1 | |
${CP} -a "${_TMPFILE}.key.pem" ${DST_DIR}/${_TRAEFIK_KEY_FILE} || exit 1 | |
${CP} -a "${_TMPFILE}.chain.pem" ${DST_DIR}/${_TRAEFIK_CHAIN_FILE} || exit 1 | |
# Set the correct permissions | |
${CHMOD} ${_CERT_PERMS} ${DST_DIR}/${_TRAEFIK_CERT_FILE} || exit 1 | |
${CHMOD} ${_CERT_PERMS} ${DST_DIR}/${_TRAEFIK_KEY_FILE} || exit 1 | |
${CHMOD} ${_CERT_PERMS} ${DST_DIR}/${_TRAEFIK_CHAIN_FILE} || exit 1 | |
# Restart docker container | |
"$_RFLAG" && ${DOCKER} restart ${CONTAINER_NAME} 2>&1 >/dev/null || exit 1 | |
fi | |
} | |
# Call `_main` after everything has been defined. | |
_main "$@" | |
exit 0; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment