Last active
February 7, 2022 15:45
-
-
Save QNimbus/8b7153a2cdfc560df16e2ae0fb89eee4 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_pikvm.sh | |
## Description: Script to deploy uploaded certificates for use with PiKVM | |
## Author: B. van wetten | |
## Created date: 07-02-2022 | |
## Updated date: 07-02-2022 | |
## Version: 0.1 | |
## GitHub Gist: | |
## | |
## Usage: deploy_pikvm.sh -c certificates [-r] [-d destination] | |
## deploy_pikvm.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 % /home/dsmr/deploy_pikvm.sh -c --% | |
## | |
## You can use this in your 'authorized_keys' file to run this command remotely: | |
## command="/bin/cat | /usr/bin/xargs -0 -I % /config/scripts/deploy_pikvm.sh -r -d /etc/kvmd/nginx/ssl -c % --",no-user-rc,no-port-forwarding,no-x11-forwarding,no-pty,from="<client-ip>" ssh-rsa AAAAB3******** | |
set -o nounset | |
# http://www.dwheeler.com/essays/filenames-in-shell.html | |
IFS=$'\n\t' | |
# 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 | |
CHOWN=$(which chown); [[ $? != 0 ]] && echo "Command 'chown' not found" >&2 && exit 1 | |
XARGS=$(which xargs); [[ $? != 0 ]] && echo "Command 'xargs' not found" >&2 && exit 1 | |
GETOPT=$(which getopt); [[ $? != 0 ]] && echo "Command 'getopt' not found" >&2 && exit 1 | |
OPENSSL=$(which openssl); [[ $? != 0 ]] && echo "Command 'openssl' not found" >&2 && exit 1 | |
# Initialize variables | |
_PWD=$(pwd) | |
_UID=$(id -u) | |
_GID=$(id -g) | |
_ME=$(basename "${0}") | |
_TMPFILE="tmp-${$}-${RANDOM}" | |
_CERT_FILE="cert.crt" | |
_KEY_FILE="server.key" | |
_CHAIN_FILE="server.crt" | |
_CERT_PERMS="0440" | |
_CERT_OWNER="root:kvmd-nginx" | |
_RFLAG=false | |
_DFLAG=false | |
_READ_ONLY_FS=true | |
# Option strings | |
SHORT=hrd: | |
LONG=help,restart,destination: | |
############################################################################### | |
# Functions # | |
############################################################################### | |
# _fs_restore() | |
# | |
# Usage: | |
# _fs_restore | |
# | |
# Re-mounts the FS in read-only mode if it was mounted read-only when script was launched | |
_fs_restore() { | |
# Restore fs to previous state | |
if [ "${_READ_ONLY_FS}" = true ] | |
then | |
sudo ro | |
fi | |
} | |
# _fs_mount_rw() | |
# | |
# Usage: | |
# _fs_mount_rw | |
# | |
# Re-mounts the FS in read-write mode if it was mounted read-only | |
_fs_mount_rw() { | |
if findmnt -n -o OPTIONS / | egrep "^ro,|,ro,|,ro$" > /dev/null 2> /dev/null | |
then | |
sudo rw | |
_READ_ONLY_FS=true | |
else | |
_READ_ONLY_FS=false | |
fi | |
} | |
# _print_usage() | |
# | |
# Usage: | |
# _print_usage | |
# | |
# Print the program usage information. | |
_print_usage() { | |
cat <<HEREDOC | |
___ ____ ___ _ ____ _ _ ___ _ _ _ _ _ _ _ | |
| \ |___ |__] | | | \_/ |__] | |_/ | | |\/| | |
|__/ |___ | |___ |__| | ___ | | | \_ \/ | | | |
Usage: | |
${_ME} -c file [-r] [-d destination] | |
${_ME} -h | |
Options: | |
-h, --help Show this screen | |
-r, --restart Restart kvmd | |
-c, --certificate Certificate file | |
-d, --destination Destination folder | |
HEREDOC | |
exit ${1:-0} | |
} | |
# _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 | |
;; | |
-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 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" ]] && echo -e "Error: '${DST_DIR}' does not exist or current user does not have writing permissions\n" && _print_usage 1; | |
} | |
########################################################################## | |
# Init # | |
########################################################################## | |
_fs_mount_rw | |
# Cleanup any temp files after script execution (traps signals: 0, 1, 2, 15) | |
trap '${FIND} ${_PWD} -maxdepth 1 -name "${_TMPFILE}*" -type f -print0 | ${XARGS} -I % -0 ${RM} %; _fs_restore' 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:-}" | |
############################################################################### | |
# 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 | |
# Move certificate files | |
#sudo ${MV} "${_TMPFILE}.cert.pem" ${DST_DIR}/${_CERT_FILE} || exit 1 | |
sudo ${MV} "${_TMPFILE}.key.pem" ${DST_DIR}/${_KEY_FILE} || exit 1 | |
sudo ${MV} "${_TMPFILE}.chain.pem" ${DST_DIR}/${_CHAIN_FILE} || exit 1 | |
# Set the correct permissions | |
#sudo ${CHMOD} ${_CERT_PERMS} ${DST_DIR}/${_CERT_FILE} || exit 1 | |
sudo ${CHMOD} ${_CERT_PERMS} ${DST_DIR}/${_KEY_FILE} || exit 1 | |
sudo ${CHMOD} ${_CERT_PERMS} ${DST_DIR}/${_CHAIN_FILE} || exit 1 | |
# Set the correct ownership | |
sudo ${CHOWN} ${_CERT_OWNER} ${DST_DIR}/${_KEY_FILE} || exit 1 | |
sudo ${CHOWN} ${_CERT_OWNER} ${DST_DIR}/${_CHAIN_FILE} || exit 1 | |
# Reload kvmd | |
"$_RFLAG" && sudo systemctl restart kvmd-nginx | |
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