917 lines
29 KiB
Bash
Executable File
917 lines
29 KiB
Bash
Executable File
#!/bin/bash
|
||
trap nfcunmount EXIT
|
||
|
||
set -o noglob
|
||
|
||
SCRIPT="$(readlink -f "${BASH_SOURCE}")"
|
||
CONFIGURATION="/home/user/scripts/admin.conf"
|
||
|
||
if [ "$EUID" -ne 0 ]; then
|
||
echo "Ce script nécessite des privilèges administrateur."
|
||
echo "Relance avec sudo..."
|
||
exec sudo "$0" "$@"
|
||
fi
|
||
|
||
echo "Le script est bien lancé avec sudo/root !"
|
||
|
||
function show()
|
||
{
|
||
NC='\033[0m'
|
||
if [ -z "$2" ]; then
|
||
COLOR=$NC
|
||
elif [ "$2" == "ERR" ]; then
|
||
COLOR='\033[1;31m'
|
||
else
|
||
COLOR='\033[0;32m'
|
||
fi
|
||
printf "${COLOR}$1${NC}\n"
|
||
}
|
||
|
||
function assign()
|
||
{
|
||
KEYS=$(cat "$1"|sed 's/.*%\([A-Z0-9]*\)%.*/%\1/g'|grep "%"|tr -d '%')
|
||
while read -r KEY; do
|
||
REALKEY=$(eval echo \$${KEY})
|
||
show "Apply key $KEY" 1
|
||
sed -i "s#%${KEY}%#${REALKEY}#g" /mnt/usb/rclone/rclone.conf
|
||
done < <(echo "${KEYS}")
|
||
}
|
||
|
||
function extract()
|
||
{
|
||
ALLCONTENT=$(cat "${SCRIPT}"|grep "#$1"|sed "s/^#$1\(.*\)$/\1/")
|
||
CONTENT=$(echo "${ALLCONTENT}"|tail -n +2)
|
||
CONTENTSIZE=$(echo "${CONTENT}"|wc -c)
|
||
FILEINFO=$(echo "${ALLCONTENT}"|head -n1)
|
||
FILENAME=$(echo "${FILEINFO}"|cut -d"," -f1)
|
||
FILEMOD=$(echo "${FILEINFO}"|cut -d"," -f2)
|
||
show "Extract piece $1 (${CONTENTSIZE}o) at ${FILENAME} with ${FILEMOD}"
|
||
echo "${CONTENT}" > "${FILENAME}"
|
||
chmod ${FILEMOD} "${FILENAME}"
|
||
}
|
||
|
||
function excludepath()
|
||
{
|
||
EXCLUD=""
|
||
while read SRC; do
|
||
if [ ! -z "${SRC}" ]; then
|
||
EXCLUD="${EXCLUD} --exclude ${SRC}"
|
||
fi
|
||
done < <(echo "$1"|tr "," "\n")
|
||
echo "${EXCLUD}"
|
||
}
|
||
|
||
function nfcunmount()
|
||
{
|
||
TMPFS=$(mount -t tmpfs|grep "/mnt/usb")
|
||
if [ ! -z "${TMPFS}" ]; then
|
||
umount /mnt/usb
|
||
fi
|
||
TMPFS=$(mount -t tmpfs|grep "/mnt/usb")
|
||
if [ ! -z "${TMPFS}" ]; then
|
||
show "Unable to umount" ERR
|
||
fi
|
||
}
|
||
|
||
function nfcmountonly()
|
||
{
|
||
if [ ! -d /mnt/usb ]; then
|
||
mkdir -p /mnt/usb
|
||
fi
|
||
TMPFS=$(mount -t tmpfs|grep "/mnt/usb")
|
||
if [ -z "${TMPFS}" ]; then
|
||
mount -t tmpfs tmpfs /mnt/usb
|
||
fi
|
||
}
|
||
|
||
function rclonemount()
|
||
{
|
||
if [ ! -f /mnt/usb/rclone/rclone.conf ]; then
|
||
show "NO RCLONE CONFIGURATION FILE" ERR
|
||
nfcmountonly
|
||
if [ ! -d /mnt/usb/rclone ]; then
|
||
show "Create /mnt/usb/rclone" 1
|
||
mkdir -p /mnt/usb/rclone
|
||
show "Create rclone.conf" 1
|
||
extract 5
|
||
TOKEN=$(${ZFS} get rclone:token $1 -o value -H|decrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
if [[ "${TOKEN}" == "" ]]; then
|
||
show "Unable to find connexion information: token"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
if [[ "$2" == "rclone" ]]; then
|
||
show "RCLONE mode"
|
||
PASSWD1=$(${ZFS} get rclone:password1 $1 -o value -H|decrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
if [[ "${PASSWD1}" == "" ]]; then
|
||
show "Unable to find connexion information: passwd1"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
PASSWD2=$(${ZFS} get rclone:password2 $1 -o value -H|decrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
if [[ "${PASSWD2}" == "" ]]; then
|
||
show "Unable to find connexion information: passwd2"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
REMOTE=$(${ZFS} get sync:dstpath $1 -o value -H)
|
||
if [[ "${REMOTE}" == "" ]]; then
|
||
show "Unable to find connexion information: dstpath"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
ACTIVE=$(${ZFS} get sync:active $1 -o value -H)
|
||
if [[ ! "${ACTIVE}" == "on" ]]; then
|
||
show "Sync not active"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
RCLONE_EXCLUDE=$(${ZFS} get sync:exclude $1 -o value -H)
|
||
RCLONE_VOLS="/$1"
|
||
else
|
||
PASSWD1=$(${ZFS} get restic:password1 $1 -o value -H|decrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
if [[ "${PASSWD1}" == "" ]]; then
|
||
show "Unable to find connexion information: passwd1"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
PASSWD2=$(${ZFS} get restic:password2 $1 -o value -H|decrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
if [[ "${PASSWD2}" == "" ]]; then
|
||
show "Unable to find connexion information: passwd2"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
show "RESTIC mode"
|
||
REMOTE=$(${ZFS} get backup:dstpath $1 -o value -H)
|
||
if [[ "${REMOTE}" == "" ]]; then
|
||
show "Unable to find connexion information: dstpath"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
ACTIVE=$(${ZFS} get backup:active $1 -o value -H)
|
||
if [[ ! "${ACTIVE}" == "on" ]]; then
|
||
show "Backup not active"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
|
||
RESTIC_PASSWORD=$(${ZFS} get restic:password $1 -o value -H|decrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
if [[ "${RESTIC_PASSWORD}" == "" ]]; then
|
||
show "Unable to find connexion information: passwd"
|
||
nfcunmount
|
||
exit
|
||
fi
|
||
RESTIC_EXCLUDE=$(${ZFS} get backup:exclude $1 -o value -H)
|
||
RESTIC_ALL=$(${ZFS} get backup:retention $1 -o value -H)
|
||
RESTIC_MONTHLY=$(echo "${RESTIC_ALL}"|cut -d',' -f1)
|
||
RESTIC_WEEKLY=$(echo "${RESTIC_ALL}"|cut -d',' -f2)
|
||
RESTIC_DAILY=$(echo "${RESTIC_ALL}"|cut -d',' -f3)
|
||
RESTIC_VOLS="/$1"
|
||
fi
|
||
assign /mnt/usb/rclone/rclone.conf
|
||
fi
|
||
fi
|
||
TOKEN=""
|
||
PASSWD1=""
|
||
PASSWD2=""
|
||
show "List remotes" 1
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf listremotes
|
||
show "Connect to Cloud without cyphering" 1
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf lsd pcloud:/
|
||
show "Connect to Cloud with cyphering" 1
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf lsd crypt:/
|
||
}
|
||
|
||
function attribute()
|
||
{
|
||
VAL=""
|
||
ATTRIBUTE=$(${ZFS} get $1 $4 -o value -H)
|
||
if [[ "${ATTRIBUTE}" == "-" ]]; then
|
||
ATTRIBUTE="$5"
|
||
show "Init attribute $1 with $5"
|
||
${ZFS} set $1=$5 $4
|
||
fi
|
||
if [[ "$2" == "passwd" ]]; then
|
||
ATTRIBUTE_DEC=$(echo -en "${ATTRIBUTE}"|decrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
BEGIN=$(echo "${ATTRIBUTE_DEC}"|head -c 10)
|
||
MIDDLE="..."
|
||
END=$(echo "${ATTRIBUTE_DEC}"|tail -c 10)
|
||
BLOB="${BEGIN}${MIDDLE}${END}"
|
||
read -p "$3 [${BLOB}]:" ASK_DEC </dev/tty
|
||
if [[ "${ASK_DEC}" == "" ]]; then
|
||
ASK=""
|
||
else
|
||
ASK=$(echo -en "${ASK_DEC}"|encrypt_now "{\"url\": \"${URL}\"}" 2> /dev/null)
|
||
fi
|
||
ATTRIBUTE_DEC=""
|
||
ASK_DEC=""
|
||
elif [[ "$2" == "bool" ]]; then
|
||
if [[ "${ATTRIBUTE}" == "on" ]]; then
|
||
VALUE="off"
|
||
else
|
||
VALUE="on"
|
||
fi
|
||
read -p "$3 [${ATTRIBUTE}]/${VALUE}:" ASK </dev/tty
|
||
else
|
||
read -p "$3 [${ATTRIBUTE}]:" ASK </dev/tty
|
||
fi
|
||
if [[ "${ASK}" != "${ATTRIBUTE}" ]]; then
|
||
if [[ "${ASK}" == "" ]]; then
|
||
#${ZFS} set $1=${ATTRIBUTE} $4
|
||
VAL=${ATTRIBUTE}
|
||
elif [[ "${ASK}" == "-" ]]; then
|
||
show "...with $5"
|
||
${ZFS} set $1=$5 $4
|
||
VAL=$5
|
||
else
|
||
show "..."
|
||
${ZFS} set $1=${ASK} $4
|
||
VAL=${ASK}
|
||
fi
|
||
else
|
||
VAL="${ATTRIBUTE}"
|
||
fi
|
||
}
|
||
|
||
function configure()
|
||
{
|
||
show "********* Configure pools ***********"
|
||
while read ITEM; do
|
||
show " - ${ITEM}"
|
||
attribute rclone:token passwd "Pcloud token" ${ITEM} ""
|
||
if [[ "${VAL}" == "" ]]; then
|
||
continue
|
||
fi
|
||
attribute rclone:password1 passwd "Pcloud password N1" ${ITEM} ""
|
||
if [[ "${VAL}" == "" ]]; then
|
||
continue
|
||
fi
|
||
attribute rclone:password2 passwd "Pcloud password N2" ${ITEM} ""
|
||
if [[ "${VAL}" == "" ]]; then
|
||
continue
|
||
fi
|
||
attribute sync:active bool "Sync active" ${ITEM} off
|
||
if [[ "${VAL}" == "on" ]]; then
|
||
attribute sync:dstpath str "Sync path" ${ITEM} ${RCLONE_PATH}
|
||
attribute sync:exclude str "Sync exclude" ${ITEM} ""
|
||
fi
|
||
attribute restic:password1 passwd "Restic password N1" ${ITEM} ""
|
||
if [[ "${VAL}" == "" ]]; then
|
||
continue
|
||
fi
|
||
attribute restic:password2 passwd "Restic password N2" ${ITEM} ""
|
||
if [[ "${VAL}" == "" ]]; then
|
||
continue
|
||
fi
|
||
attribute backup:active bool "Backup active" ${ITEM} off
|
||
if [[ "${VAL}" == "on" ]]; then
|
||
attribute restic:password passwd "Backup password" ${ITEM} ""
|
||
if [[ "${VAL}" == "" ]]; then
|
||
continue
|
||
fi
|
||
attribute backup:dstpath str "Backup path" ${ITEM} ${RESTIC_PATH}
|
||
attribute backup:exclude str "Backup exclude" ${ITEM} ""
|
||
attribute backup:retention str "Backup retention" ${ITEM} ${RESTIC_MONTHLY},${RESTIC_WEEKLY},${RESTIC_DAILY}
|
||
fi
|
||
done < <(${ZFS} list -o name -d 1 -t filesystem|grep "/")
|
||
}
|
||
|
||
function help()
|
||
{
|
||
show "Usage: admin [ command ] [ args... ]" 1
|
||
show ""
|
||
show "help for COMPLEX commands: admin command"
|
||
show ""
|
||
show "-< Save/Restore"
|
||
show "sync - sync, check data to cloud COMPLEX"
|
||
show "backup - backup data to cloud COMPLEX"
|
||
show "config - configure Restic and Rclone NOARG"
|
||
show "velero - backup object on kubernetes COMPLEX"
|
||
show ""
|
||
show "-< Tang"
|
||
show "tangcrypt - encrypt /dev/stdin to /dev/stdout NOARG"
|
||
show "tangdecrypt - decrypt /dev/stdin to /dev/stdout NOARG"
|
||
show ""
|
||
exit 2
|
||
}
|
||
|
||
function help_velero()
|
||
{
|
||
show "Usage: admin velero [init|backup|restore|info ] [dataname]" 1
|
||
show ""
|
||
show "Use the velero backend to make some kubernetes backup operations"
|
||
show ""
|
||
show "init - initialize the data storage on kubernetes"
|
||
show "uninstall - uninstall velero on kubernetes"
|
||
show "backup - backup the kubernetes objects"
|
||
show "restore - restore [dataname] backup on kubernetes"
|
||
show "info - get informations about [dataname]"
|
||
show ""
|
||
exit 2
|
||
}
|
||
|
||
function help_restic()
|
||
{
|
||
show "Usage: admin backup ${ZFS}dataset [ init|view|ls|check|now|prune|remove|config ]" 1
|
||
show ""
|
||
show "Use the restic backend to make some cloud backup operations"
|
||
show ""
|
||
show "init - initialize the data storage on cloud"
|
||
show "view - view snapshots stored on cloud"
|
||
show "ls - list all files 1ARG"
|
||
show "check - check integrity of distant data"
|
||
show "now - perform a backup on cloud"
|
||
show "prune - purge unused data on cloud storage"
|
||
show "remove - remove a snapshot store on cloud 1ARG"
|
||
show "unlock - unlock data storage on cloud"
|
||
show ""
|
||
exit 2
|
||
}
|
||
|
||
function help_rclone()
|
||
{
|
||
show "Usage: admin sync ${ZFS}dataset [ now|dryrun|copy|view|ls|check|config ]" 1
|
||
show ""
|
||
show "Use the rclone backend to make cloud mirror operations"
|
||
show ""
|
||
show "now - perform a sync with the cloud"
|
||
show "dryrun - perform a simulation of sync with the cloud"
|
||
show "copy - copy ARG to local"
|
||
show "view - view interactivly server data"
|
||
show "ls - list all files"
|
||
show "check - check integrity of distant data"
|
||
show ""
|
||
exit 2
|
||
}
|
||
|
||
function encrypt_now() {
|
||
SUMMARY="Encrypts using a Tang binding server policy"
|
||
|
||
if [ "$1" == "--summary" ]; then
|
||
echo "$SUMMARY"
|
||
exit 0
|
||
fi
|
||
|
||
if [ -t 0 ]; then
|
||
exec >&2
|
||
echo
|
||
echo "Usage: clevis encrypt tang CONFIG [-y] < PLAINTEXT > JWE"
|
||
echo
|
||
echo "$SUMMARY"
|
||
echo
|
||
echo " -y Use this option for skipping the advertisement"
|
||
echo " trust check. This can be useful in automated"
|
||
echo " deployments"
|
||
echo
|
||
echo "This command uses the following configuration properties:"
|
||
echo
|
||
echo " url: <string> The base URL of the Tang server (REQUIRED)"
|
||
echo
|
||
echo " thp: <string> The thumbprint of a trusted signing key"
|
||
echo
|
||
echo " adv: <string> A filename containing a trusted advertisement"
|
||
echo " adv: <object> A trusted advertisement (raw JSON)"
|
||
echo
|
||
echo "Obtaining the thumbprint of a trusted signing key is easy. If you"
|
||
echo "have access to the Tang server's database directory, simply do:"
|
||
echo
|
||
echo " $ jose jwk thp -i \$DBDIR/\$SIG.jwk "
|
||
echo
|
||
echo "Alternatively, if you have certainty that your network connection"
|
||
echo "is not compromised (not likely), you can download the advertisement"
|
||
echo "yourself using:"
|
||
echo
|
||
echo " $ curl -f \$URL/adv > adv.jws"
|
||
echo
|
||
exit 2
|
||
fi
|
||
if ! cfg="$(jose fmt -j- -Oo- <<< "$1" 2>/dev/null)"; then
|
||
echo "Configuration is malformed!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
CLEVIS_DEFAULT_THP_ALG=S256 # SHA-256.
|
||
CLEVIS_ALTERNATIVE_THP_ALGS=S1 # SHA-1.
|
||
|
||
trust=
|
||
[ -n "${2}" ] && [ "${2}" == "-y" ] && trust=yes
|
||
|
||
if ! url="$(jose fmt -j- -Og url -u- <<< "$cfg")"; then
|
||
echo "Missing the required 'url' property!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
thp="$(jose fmt -j- -Og thp -Su- <<< "$cfg")" || true
|
||
|
||
### Get the advertisement
|
||
if jws="$(jose fmt -j- -g adv -Oo- <<< "$cfg")"; then
|
||
thp="${thp:-any}"
|
||
elif jws="$(jose fmt -j- -g adv -Su- <<< "$cfg")"; then
|
||
if ! [ -f "$jws" ]; then
|
||
echo "Advertisement file '$jws' not found!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if ! jws="$(jose fmt --json="${jws}" -Oo- 2>/dev/null)"; then
|
||
echo "Advertisement file '$jws' is malformed!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
thp="${thp:-any}"
|
||
elif ! jws="$(curl -sfg "$url/adv/$thp")"; then
|
||
echo "Unable to fetch advertisement: '$url/adv/$thp'!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if ! jwks="$(jose fmt --json="${jws}" -Og payload -SyOg keys \
|
||
-AUo- 2>/dev/null)"; then
|
||
echo "Advertisement is malformed!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
### Check advertisement validity
|
||
ver="$(jose jwk use -i- -r -u verify -o- <<< "$jwks")"
|
||
if ! jose jws ver -i "$jws" -k- -a <<< "$ver"; then
|
||
echo "Advertisement is missing signatures!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
### Check advertisement trust
|
||
if [ -z "${trust}" ]; then
|
||
if [ -z "$thp" ]; then
|
||
echo "The advertisement contains the following signing keys:" >&2
|
||
echo >&2
|
||
jose jwk thp -i- -a "${CLEVIS_DEFAULT_THP_ALG}" <<< "$ver" >&2
|
||
echo >&2
|
||
|
||
elif [ "$thp" != "any" ] && \
|
||
! jose jwk thp -i- -f "${thp}" -a "${CLEVIS_DEFAULT_THP_ALG}" \
|
||
-o /dev/null <<< "$ver"; then
|
||
# Thumbprint of trusted JWK did not match the signature. Let's check
|
||
# alternative thumbprints generated with clevis supported hash
|
||
# algorithms to be sure.
|
||
for alg in ${CLEVIS_ALTERNATIVE_THP_ALGS}; do
|
||
srv="$(jose jwk thp -i- -f "${thp}" -a "${alg}" <<< "${ver}")" \
|
||
&& break
|
||
done
|
||
if [ -z "${srv}" ]; then
|
||
echo "Trusted JWK '$thp' did not sign the advertisement!" >&2
|
||
exit 1
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
### Perform encryption
|
||
if ! enc="$(jose jwk use -i- -r -u deriveKey -o- <<< "$jwks")"; then
|
||
echo "Key derivation key not available!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
jose fmt -j "$enc" -Og keys -A || enc="{\"keys\":[$enc]}"
|
||
|
||
if ! jwk="$(jose fmt -j- -Og keys -Af- <<< "$enc")"; then
|
||
echo "No exchange keys found!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
jwk="$(jose fmt -j- -Od key_ops -o- <<< "$jwk")"
|
||
jwk="$(jose fmt -j- -Od alg -o- <<< "$jwk")"
|
||
kid="$(jose jwk thp -i- -a "${CLEVIS_DEFAULT_THP_ALG}" <<< "$jwk")"
|
||
jwe='{"protected":{"alg":"ECDH-ES","enc":"A256GCM","clevis":{"pin":"tang","tang":{}}}}'
|
||
jwe="$(jose fmt -j "$jwe" -g protected -q "$kid" -s kid -UUo-)"
|
||
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tang -q "$url" -s url -UUUUo-)"
|
||
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tang -j- -s adv -UUUUo- <<< "$jwks")"
|
||
exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat)
|
||
echo -en ""
|
||
}
|
||
|
||
|
||
function decrypt_now() {
|
||
[ $# -eq 1 ] && [ "$1" == "--summary" ] && exit 2
|
||
|
||
if [ -t 0 ]; then
|
||
exec >&2
|
||
echo
|
||
echo "Usage: clevis decrypt tang < JWE > PLAINTEXT"
|
||
echo
|
||
exit 2
|
||
fi
|
||
|
||
read -r -d . hdr
|
||
|
||
if ! jhd="$(jose b64 dec -i- <<< "$hdr")"; then
|
||
echo "Error decoding JWE protected header!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$(jose fmt -j- -Og clevis -g pin -u- <<< "$jhd")" != "tang" ]; then
|
||
echo "JWE pin mismatch!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if ! clt="$(jose fmt -j- -Og epk -Oo- <<< "$jhd")"; then
|
||
echo "JWE missing required 'epk' header parameter!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if ! kid="$(jose fmt -j- -Og kid -Su- <<< "$jhd")"; then
|
||
echo "JWE missing required 'kid' header parameter!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# Tang advertisement validation.
|
||
if ! keys="$(jose fmt -j- -Og clevis -g tang -g adv -Oo- <<< "${jhd}")"; then
|
||
echo "JWE missing required 'clevis.tang.adv' header parameter!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# Check if the thumbprint we have in `kid' is in the advertised keys.
|
||
CLEVIS_DEFAULT_THP_ALG=S256 # SHA-256.
|
||
CLEVIS_DEFAULT_THP_LEN=43 # Length of SHA-256 thumbprint.
|
||
CLEVIS_ALTERNATIVE_THP_ALGS=S1 # SHA-1.
|
||
|
||
# Issue a warning if we are using a hash that has a shorter length than the
|
||
# default one.
|
||
if [ "${#kid}" -lt "${CLEVIS_DEFAULT_THP_LEN}" ]; then
|
||
echo "WARNING: tang using a deprecated hash for the JWK thumbprints" >&2
|
||
fi
|
||
|
||
if ! srv="$(jose jwk thp -i- -f "${kid}" -a "${CLEVIS_DEFAULT_THP_ALG}" \
|
||
<<< "${keys}")"; then
|
||
# `kid' thumprint not in the advertised keys, but it's possible it was
|
||
# generated using a different algorithm than the default one.
|
||
# Let us try the alternative supported algorithms to make sure `kid'
|
||
# really is not part of the advertised keys.
|
||
for alg in ${CLEVIS_ALTERNATIVE_THP_ALGS}; do
|
||
srv="$(jose jwk thp -i- -f "$kid" -a "${alg}" <<< "${keys}")" && break
|
||
done
|
||
if [ -z "${srv}" ]; then
|
||
echo "JWE header validation of 'clevis.tang.adv' failed: key thumbprint does not match" >&2
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
if ! url="$(jose fmt -j- -Og clevis -g tang -g url -Su- <<< "$jhd")"; then
|
||
echo "JWE missing required 'clevis.tang.url' header parameter!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if ! crv="$(jose fmt -j- -Og crv -Su- <<< "$clt")"; then
|
||
echo "Unable to determine EPK's curve!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if ! eph="$(jose jwk gen -i "{\"alg\":\"ECMR\",\"crv\":\"$crv\"}")"; then
|
||
echo "Error generating ephemeral key!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
xfr="$(jose jwk exc -i '{"alg":"ECMR"}' -l- -r- <<< "$clt$eph")"
|
||
|
||
rec_url="$url/rec/$kid"
|
||
ct="Content-Type: application/jwk+json"
|
||
if ! rep="$(curl -sfg -X POST -H "$ct" --data-binary @- "$rec_url" <<< "$xfr")"; then
|
||
echo "Error communicating with server $url" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if ! rep="$(jose fmt -j- -Og kty -q EC -EUUg crv -q "$crv" -EUUo- <<< "$rep")"; then
|
||
echo "Received invalid server reply!" >&2
|
||
exit 1
|
||
fi
|
||
|
||
tmp="$(jose jwk exc -i '{"alg":"ECMR"}' -l- -r- <<< "$eph$srv")"
|
||
rep="$(jose jwk pub -i- <<< "$rep")"
|
||
jwk="$(jose jwk exc -l- -r- <<< "$rep$tmp")"
|
||
(echo -n "$jwk$hdr."; /bin/cat) | exec jose jwe dec -k- -i-
|
||
echo -en ""
|
||
}
|
||
|
||
function rcloneapp()
|
||
{
|
||
if [ -z "$1" ]; then
|
||
help_rclone
|
||
fi
|
||
case "$2" in
|
||
copy)
|
||
DEST="/tmp/"
|
||
show "Copy from $3 to ${DEST}" 1
|
||
rclonemount $1 rclone
|
||
${RCLONE_CMD} -vv --config /mnt/usb/rclone/rclone.conf copy crypt:/$2 ${DEST}
|
||
;;
|
||
dryrun)
|
||
show "Dry run..." 1
|
||
echo "********* Synchonize *************" >> /var/log/rclone.log
|
||
date >> /var/log/rclone.log
|
||
rclonemount $1 rclone
|
||
excluded=$(excludepath $RCLONE_EXCLUDE)
|
||
while read src; do
|
||
dstemp=$(basename "${src^}")
|
||
dst=${dstemp^}
|
||
show "$src => $dst"
|
||
echo "$src => $dst" >> /var/log/rclone.log
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf sync -c -P --dry-run --links --checkers=20 --low-level-retries=10 --retries=10 --retries-sleep=1s --transfers=20 --delete-excluded $excluded --exclude ".zfs/**" $src crypt:/${dst}/ 2>&1 | tee -a /var/log/rclone.log
|
||
done < <(echo "$RCLONE_VOLS"|tr "," "\n")
|
||
echo "***********************************" >> /var/log/rclone.log
|
||
;;
|
||
now)
|
||
show "Synchronize..." 1
|
||
echo "********* Synchonize *************" >> /var/log/rclone.log
|
||
date >> /var/log/rclone.log
|
||
rclonemount $1 rclone
|
||
excluded=$(excludepath $RCLONE_EXCLUDE)
|
||
while read src; do
|
||
dstemp=$(basename "${src^}")
|
||
dst=${dstemp^}
|
||
show "$src => $dst"
|
||
echo "$src => $dst" >> /var/log/rclone.log
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf sync -c -P ${DRY} --links --checkers=20 --low-level-retries=10 --retries=10 --retries-sleep=1s --transfers=20 --delete-excluded $excluded --exclude ".zfs/**" $src crypt:/${dst}/ 2>&1 | tee -a /var/log/rclone.log
|
||
done < <(echo "$RCLONE_VOLS"|tr "," "\n")
|
||
echo "***********************************" >> /var/log/rclone.log
|
||
;;
|
||
view)
|
||
show "View..." 1
|
||
rclonemount $1 rclone
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf ncdu crypt:
|
||
;;
|
||
ls)
|
||
show "View files..." 1
|
||
rclonemount $1 rclone
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf lsd crypt: -R
|
||
;;
|
||
check)
|
||
show "Check..." 1
|
||
echo "************** Check **************" >> /var/log/rclone.log
|
||
date >> /var/log/rclone.log
|
||
rclonemount $1 rclone
|
||
excluded=$(excludepath $RCLONE_EXCLUDE)
|
||
while read src; do
|
||
dstemp=$(basename "${src^}")
|
||
dst=${dstemp^}
|
||
show "$src => $dst"
|
||
echo "$src => $dst" >> /var/log/rclone.log
|
||
${RCLONE_CMD} --config /mnt/usb/rclone/rclone.conf check -c -P --differ /var/log/differ_${dst}.log --links --checkers=20 --low-level-retries=10 --retries=10 --retries-sleep=1s --transfers=20 $excluded --exclude ".zfs/**" $src crypt:/${dst}/ 2>&1 | tee -a /var/log/rclone.log
|
||
done < <(echo "$RCLONE_VOLS"|tr "," "\n")
|
||
echo "***********************************" >> /var/log/rclone.log
|
||
;;
|
||
*)
|
||
help_rclone
|
||
;;
|
||
esac
|
||
nfcunmount
|
||
}
|
||
|
||
function veleroapp()
|
||
{
|
||
if [ -z "$1" ]; then
|
||
help_velero
|
||
fi
|
||
case "$1" in
|
||
init)
|
||
nfcmountonly
|
||
read -p "👣 MinIO url (with http/s) : " PATH_MINIO
|
||
read -p "🔑 MinIO Access Key: " ACCESS_KEY
|
||
read -sp "🔒 MinIO Secret Key: " SECRET_KEY
|
||
cat <<EOF > /mnt/usb/credentials-vel
|
||
[default]
|
||
aws_access_key_id=${ACCESS_KEY}
|
||
aws_secret_access_key=${SECRET_KEY}
|
||
EOF
|
||
|
||
if ! command -v velero >/dev/null 2>&1; then
|
||
echo "🔍 Velero non trouvé, téléchargement et installation de v1.16.0…"
|
||
wget -q https://github.com/vmware-tanzu/velero/releases/download/v1.16.0/velero-v1.16.0-linux-amd64.tar.gz
|
||
tar -xzf velero-v1.16.0-linux-amd64.tar.gz
|
||
chmod +x velero-v1.16.0-linux-amd64/velero
|
||
chown root:root velero-v1.16.0-linux-amd64/velero
|
||
mv velero-v1.16.0-linux-amd64/velero /usr/local/bin/velero
|
||
rm -rf velero-v1.16.0-linux-amd64 velero-v1.16.0-linux-amd64.tar.gz
|
||
echo "✅ Velero installé."
|
||
else
|
||
echo "✅ Velero déjà présent, version : $(velero version --client-only | head -n1)"
|
||
fi
|
||
echo "▶️ Déploiement Velero dans le cluster…"
|
||
velero install \
|
||
--provider aws \
|
||
--plugins velero/velero-plugin-for-aws:v1.10.0 \
|
||
--features=EnableCSI \
|
||
--use-node-agent \
|
||
--bucket velero \
|
||
--backup-location-config region=us-east-1,s3ForcePathStyle="true",s3Url=${PATH_MINIO},checksumAlgorithm="" \
|
||
--snapshot-location-config region=us-east-1 \
|
||
--secret-file /mnt/usb/credentials-vel \
|
||
--uploader-type kopia \
|
||
--kubeconfig /home/user/.kube/config \
|
||
|| echo "ℹ️ Velero est peut-être déjà installé dans le cluster."
|
||
echo "🎉 Script terminé."
|
||
;;
|
||
uninstall)
|
||
velero uninstall --kubeconfig /home/user/.kube/config
|
||
;;
|
||
backup)
|
||
NOW=$(date +"%Y%m%d-%H%M")
|
||
velero backup create backup-complet-cluster2-${NOW} \
|
||
--include-namespaces '*' \
|
||
--include-resources '*' \
|
||
--include-cluster-resources=true \
|
||
--kubeconfig /home/user/.kube/config
|
||
--default-volumes-to-fs-backup \
|
||
echo "${NOW}" > /var/log/lastfilename
|
||
;;
|
||
info)
|
||
velero backup describe $2 --kubeconfig /home/user/.kube/config
|
||
;;
|
||
restore)
|
||
BACKUP_NAME="$2"
|
||
read -p "Are you sure you want to restore from backup '$BACKUP_NAME'? [y/N] " confirm
|
||
case "$confirm" in
|
||
[Yy]* )
|
||
echo "▶️ Restoring from backup '$BACKUP_NAME'…"
|
||
#velero restore create --from-backup "$BACKUP_NAME" --kubeconfig /home/user/.kube/config
|
||
;;
|
||
* )
|
||
echo "❌ Restore aborted."
|
||
;;
|
||
esac
|
||
;;
|
||
*)
|
||
help_velero
|
||
;;
|
||
esac
|
||
nfcunmount
|
||
}
|
||
|
||
|
||
function resticapp()
|
||
{
|
||
if [ -z "$1" ]; then
|
||
help_restic
|
||
fi
|
||
export RCLONE_CONFIG
|
||
export RESTIC_PASSWORD
|
||
case "$2" in
|
||
init)
|
||
show "Initializing..." 1
|
||
echo "********* Initialiazing *************" >> /var/log/restic.log
|
||
date >> /var/log/restc.log
|
||
rclonemount $1 restic
|
||
${RESTIC_CMD} init -r rclone:crypt:/ | tee -a /var/log/restic.log
|
||
echo "***********************************" >> /var/log/restic.log
|
||
;;
|
||
unlock)
|
||
show "Unlocking..." 1
|
||
echo "********* Unlocking *************" >> /var/log/restic.log
|
||
date >> /var/log/restc.log
|
||
rclonemount $1 restic
|
||
${RESTIC_CMD} unlock -r rclone:crypt:/ | tee -a /var/log/restic.log
|
||
echo "***********************************" >> /var/log/restic.log
|
||
;;
|
||
ls)
|
||
show "View files..." 1
|
||
rclonemount $1 restic
|
||
${RESTIC_CMD} ls -r rclone:crypt:/ $3
|
||
;;
|
||
view)
|
||
show "View..." 1
|
||
rclonemount $1 restic
|
||
${RESTIC_CMD} snapshots -r rclone:crypt:/
|
||
;;
|
||
check)
|
||
show "Check..." 1
|
||
echo "************** Check **************" >> /var/log/restic.log
|
||
date >> /var/log/restic.log
|
||
rclonemount $1 restic
|
||
${RESTIC_CMD} check --read-data -r rclone:crypt:/ | tee -a /var/log/restic.log
|
||
${RESTIC_CMD} rebuild-index -r rclone:crypt:/ | tee -a /var/log/restic.log
|
||
echo "***********************************" >> /var/log/restic.log
|
||
;;
|
||
prune)
|
||
show "Cleaning..." 1
|
||
echo "************** Cleaning **************" >> /var/log/restic.log
|
||
date >> /var/log/restic.log
|
||
rclonemount $1 restic
|
||
${RESTIC_CMD} prune -r rclone:crypt:/ | tee -a /var/log/restic.log
|
||
echo "***********************************" >> /var/log/restic.log
|
||
;;
|
||
remove)
|
||
show "Removing..." 1
|
||
echo "************** Removing **************" >> /var/log/restic.log
|
||
date >> /var/log/restic.log
|
||
rclonemount $1 restic
|
||
${RESTIC_CMD} forget $2 -r rclone:crypt:/ | tee -a /var/log/restic.log
|
||
echo "***********************************" >> /var/log/restic.log
|
||
;;
|
||
now)
|
||
show "Backing up..." 1
|
||
echo "************** Backup **************" >> /var/log/restic.log
|
||
date >> /var/log/restic.log
|
||
rclonemount $1 restic
|
||
excluded=$(excludepath $RESTIC_EXCLUDE)
|
||
while read src; do
|
||
dstemp=$(basename "${src^}")
|
||
dst=${dstemp^}
|
||
show "$src => $dst"
|
||
echo "$src => $dst" >> /var/log/restic.log
|
||
${RESTIC_CMD} backup $src -r rclone:crypt:/ --verbose=1 $excluded --exclude ".zfs/**" --tag admin_command|tee -a /var/log/restic.log
|
||
${RESTIC_CMD} forget -r rclone:crypt:/ --tag admin_command --keep-monthly=$RESTIC_MONTHLY --keep-weekly=$RESTIC_WEEKLY --keep-daily=$RESTIC_DAILY
|
||
done < <(echo "$RESTIC_VOLS"|tr "," "\n")
|
||
echo "***********************************" >> /var/log/restic.log
|
||
;;
|
||
*)
|
||
help_restic
|
||
;;
|
||
esac
|
||
nfcunmount
|
||
}
|
||
|
||
function install()
|
||
{
|
||
apt install rclone restic ncdu jose clevis curl sudo zfsutils-linux -y
|
||
}
|
||
|
||
if [[ ! -f "${CONFIGURATION}" ]]; then
|
||
show "Creating configuration file..."
|
||
extract 4
|
||
fi
|
||
|
||
source "${CONFIGURATION}"
|
||
|
||
nfcunmount
|
||
COMMAND="$1"
|
||
shift
|
||
case "${COMMAND}" in
|
||
install)
|
||
install
|
||
;;
|
||
config)
|
||
configure
|
||
;;
|
||
velero)
|
||
veleroapp $1 $2 $3
|
||
;;
|
||
backup)
|
||
resticapp $1 $2 $3
|
||
;;
|
||
sync)
|
||
rcloneapp $1 $2 $3
|
||
;;
|
||
nfcunmount)
|
||
nfcunmount
|
||
;;
|
||
tangcrypt)
|
||
cat /dev/stdin|encrypt_now "{\"url\": \"${URL}\"}"
|
||
;;
|
||
tangdecrypt)
|
||
cat /dev/stdin|decrypt_now "{\"url\": \"${URL}\"}"
|
||
;;
|
||
*)
|
||
help
|
||
;;
|
||
esac
|
||
|
||
#4/home/user/scripts/admin.conf,644
|
||
#4# Hard settings
|
||
#4ALIB="--module /usr/lib/x86_64-linux-gnu/libykcs11.so.2"
|
||
#4ZFS="zfs"
|
||
#4PZFS="zfs"
|
||
#4ZPOOL="zpool"
|
||
#4PZPOOL="zpool"
|
||
#4PATH="/usr/local/bin:/usr/bin:$PATH"
|
||
#4FONT=""
|
||
#4MAXSIZE=700
|
||
#4RCLONE_CMD="rclone"
|
||
#4RESTIC_CMD="restic"
|
||
#4
|
||
#4# Settings
|
||
#4RCLONE_CONFIG="/mnt/usb/rclone/rclone.conf"
|
||
#4URL="https://tang.ia86.cc"
|
||
#4
|
||
#4# Default parameters
|
||
#4RCLONE_PATH="/hetzner/kube.ia86.cc"
|
||
#4RCLONE_EXCLUDE=""
|
||
#4RESTIC_PATH="/hetzner/restic"
|
||
#4RESTIC_MONTHLY="12"
|
||
#4RESTIC_WEEKLY="10"
|
||
#4RESTIC_DAILY="31"
|
||
#4RESTIC_EXCLUDE=""
|
||
|
||
#5/mnt/usb/rclone/rclone.conf,644
|
||
#5[pcloud]
|
||
#5type = pcloud
|
||
#5hostname = eapi.pcloud.com
|
||
#5token = {"access_token":"%TOKEN%","token_type":"bearer","expiry":"0001-01-01T00:00:00Z"}
|
||
#5[crypt]
|
||
#5type = crypt
|
||
#5remote = pcloud:%REMOTE%
|
||
#5filename_encryption = standard
|
||
#5directory_name_encryption = true
|
||
#5password = %PASSWD1%
|
||
#5password2 = %PASSWD2%
|
||
#5[sauvegarde]
|
||
#5type = crypt
|
||
#5remote = pcloud:%REMOTE%
|
||
#5filename_encryption = standard
|
||
#5directory_name_encryption = true
|
||
#5password = %PASSWD3%
|
||
#5password2 = %PASSWD4%
|