Skip to content

Instantly share code, notes, and snippets.

@mmguero
Last active August 3, 2023 03:28
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save mmguero/53f4c9c04ac49c330800e463e4620808 to your computer and use it in GitHub Desktop.
Save mmguero/53f4c9c04ac49c330800e463e4620808 to your computer and use it in GitHub Desktop.
a wrapper script for wg/wg-quick/systemctl wireguard operations; supports encrypting/decrypting/using openssl-encrypted wireguard config files with wg-quick up and down
#!/usr/bin/env bash
# wwg.sh
# a wrapper script for wg/wg-quick/systemctl wireguard operations
# https://gist.github.com/mmguero/53f4c9c04ac49c330800e463e4620808/edit
# The idea is you create your wireguard config file (eg, `wg0.conf`),
# then run `wwg.sh enc wg0.conf` to encrypt it. Then, you can use `wwg.sh up wg0.conf`
# which will temporarily decrypt the file, run `wg-quick up` for that interface with
# the decrypted config file, then shred it so the plaintext version doesn't remain on
# disk for longer than the time the `wg-quick` operation takes.
# Usage:
# wwg.sh [operation] [interface]
OPERATION="$1"
INTERFACE="$2"
FINAL_EXIT_CODE=
# Operations include:
# up - run wg-quick up (detects and handles encrypted configuration files)
# down - run wg-quick down
# enc - encrypt a config file
# dec - decrypt a config file (e.g., for when you need to make edits to it)
# show - run wg show (don't confuse with status)
# status - run systemctl status wg-quick@XXX.service
# enable - run systemctl enable wg-quick@XXX.service
# disable - run systemctl enable wg-quick@XXX.service
# start - run systemctl start wg-quick@XXX.service (don't confuse with up; doesn't handle encrypted configuration files)
# stop - run systemctl stop wg-quick@XXX.service (don't confuse with down)
# if your wireguard configuration directory is something other than /etc/wireguard, you can override it
# with the WIREGUARD_CONFIG_DIR environment variable
CONFIG_DIR=${WIREGUARD_CONFIG_DIR:-"/etc/wireguard"}
CONFIG_FILE="$CONFIG_DIR/$INTERFACE.conf"
CONFIG_FILE_RESTORE=
# map a user-supplied operation to the exutable, executable operation, and argument in which the interface should reside
# op;exe;exe_op;iface_replacer
IFACE_REPLACER=XXX
WG_OPERATIONS=(
"dec;;;"
"down;wg-quick;down;$IFACE_REPLACER"
"enc;;;"
"ls;;;"
"show;wg;show;$IFACE_REPLACER"
"start;systemctl;start;wg-quick@$IFACE_REPLACER.service"
"status;systemctl;status;wg-quick@$IFACE_REPLACER.service"
"enable;systemctl;enable;wg-quick@$IFACE_REPLACER.service"
"disable;systemctl;disable;wg-quick@$IFACE_REPLACER.service"
"stop;systemctl;stop;wg-quick@$IFACE_REPLACER.service"
"up;wg-quick;up;$IFACE_REPLACER"
)
# shred a file if possible, and rm it if not
function shred_file {
TARGET="$1"
if [[ -n $TARGET ]] && [[ -f "$TARGET" ]]; then
type shred >/dev/null 2>&1 && shred -f -u "$TARGET" || rm -f "$TARGET"
fi
[[ -n $TARGET ]] && [[ ! -f "$TARGET" ]] && return 0 || return 1
}
# shred a file with user-provided confirmation
function shred_file_confirm {
TARGET="$1"
RETURN_CODE=1
read -p "Remove "$TARGET" [Y/n]? " CONFIRMATION
CONFIRMATION=${CONFIRMATION:-Y}
if [[ $CONFIRMATION =~ ^[Yy] ]]; then
shred_file "$TARGET"
RETURN_CODE=$?
fi
return $RETURN_CODE
}
# encrypt a file (openssl will prompt for the password); does not allow encrypting an already encrypted file
function encrypt_file() {
DECFILE="$1"
ENCFILE="$2"
( [[ -n $DECFILE ]] && \
[[ -r "$DECFILE" ]] && \
( ! ( file "$DECFILE" | grep -q 'openssl enc' ) ) && \
openssl enc -base64 -aes-256-cbc -md sha512 -pbkdf2 -iter 1024 -salt -in "$DECFILE" -out "$ENCFILE" && \
[[ -f "$ENCFILE" ]] && \
chmod --reference="$DECFILE" "$ENCFILE" ) && return 0 || return 1
}
# decrypt a file (openssl will prompt for the password)
function decrypt_file() {
ENCFILE="$1"
DECFILE="$2"
( [[ -n $ENCFILE ]] && \
[[ -r "$ENCFILE" ]] && \
openssl enc -d -base64 -aes-256-cbc -md sha512 -pbkdf2 -iter 1024 -salt -in "$ENCFILE" -out "$DECFILE" && \
[[ -f "$DECFILE" ]] && \
chmod --reference="$ENCFILE" "$DECFILE" ) && return 0 || return 1
}
# restore the contents original config file ($CONFIG_FILE) if the variable $CONFIG_FILE_RESTORE is set and that file exists
function restore_config_file {
if [[ -n $CONFIG_FILE_RESTORE ]]; then
shred_file "$CONFIG_FILE"
mv -f "$CONFIG_FILE_RESTORE" "$CONFIG_FILE"
fi
}
# determine the operation provided by the user
OP_MATCH=
EXE=
EXE_OP=
EXE_TARGET=
for i in ${WG_OPERATIONS[@]}; do
OP="$(echo "$i" | cut -d';' -f1)"
if [[ "$OP" == "$OPERATION" ]]; then
OP_MATCH="$OP"
EXE="$(echo "$i" | cut -d';' -f2)"
EXE_OP="$(echo "$i" | cut -d';' -f3)"
EXE_TARGET="$(echo "$i" | cut -d';' -f4 | sed "s/$IFACE_REPLACER/$INTERFACE/g")"
break;
fi
done
if [[ -n $OP_MATCH ]]; then
if [[ "$EXE_OP" == "show" ]]; then
# if "all" or "" is provided for the interface, show all
if [[ -z $EXE_TARGET ]] || [[ "$EXE_TARGET" == "all" ]]; then
"$EXE" "$EXE_OP"
else
"$EXE" "$EXE_OP" "$EXE_TARGET"
fi
elif [[ "$OP_MATCH" == "ls" ]]; then
ls "$CONFIG_DIR"/ | sed "s/\.conf$//"
elif [[ -r "$CONFIG_FILE" ]]; then
# only proceed if /etc/wireguard/XXXX.conf exists
if [[ "$OP_MATCH" == "enc" ]]; then
# encrypt a config file but that's all
CONFIG_FILE_ENC="$CONFIG_FILE.enc"
if encrypt_file "$CONFIG_FILE" "$CONFIG_FILE_ENC"; then
ls -l "$CONFIG_FILE_ENC"
# if requested, replace the original with the encrypted version
shred_file_confirm "$CONFIG_FILE" && mv -v "$CONFIG_FILE_ENC" "$CONFIG_FILE"
else
FINAL_EXIT_CODE=$?
echo "Error encrypting configuration file "$CONFIG_FILE"" >&2
fi
elif [[ "$OP_MATCH" == "dec" ]]; then
# decrypt a config file but that's all
CONFIG_FILE_DEC="$CONFIG_FILE.dec"
if decrypt_file "$CONFIG_FILE" "$CONFIG_FILE_DEC"; then
ls -l "$CONFIG_FILE_DEC"
# if requested, replace the encrypted version with the decrypted version
shred_file_confirm "$CONFIG_FILE" && mv -v "$CONFIG_FILE_DEC" "$CONFIG_FILE"
else
FINAL_EXIT_CODE=$?
echo "Error decrypting configuration file "$CONFIG_FILE"" >&2
fi
elif [[ "$EXE_OP" == "up" ]] && ( file "$CONFIG_FILE" | grep -q 'openssl enc' ); then
# if we're doing an "up" operation and the file is encrypted, decrypt it first
CONFIG_FILE_ENC="$CONFIG_FILE.enc"
CONFIG_FILE_DEC="$CONFIG_FILE.dec"
if decrypt_file "$CONFIG_FILE" "$CONFIG_FILE_DEC"; then
mv "$CONFIG_FILE" "$CONFIG_FILE_ENC"
mv "$CONFIG_FILE_DEC" "$CONFIG_FILE"
# ensure the decrypted version of the file is erased again before we exit
CONFIG_FILE_RESTORE="$CONFIG_FILE_ENC"
trap "restore_config_file" EXIT
else
FINAL_EXIT_CODE=$?
echo "Error decrypting configuration file "$CONFIG_FILE"" >&2
fi
fi
if [[ -n $EXE_OP ]]; then
# perform the actual operation (wg, systemctl, whatever)
"$EXE" "$EXE_OP" "$EXE_TARGET"
FINAL_EXIT_CODE=$?
fi
else
echo "Unable to read configuration file "$CONFIG_FILE"" >&2
FINAL_EXIT_CODE=1
fi
else
echo "Usage:" >&2
echo -e "\t$(basename $(test -L "$0" && readlink "$0" || echo "$0")) [operation] [interface]" >&2
FINAL_EXIT_CODE=1
fi
[[ -n $FINAL_EXIT_CODE ]] && exit $FINAL_EXIT_CODE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment