mirror of https://github.com/lukechilds/umbrel.git
7 changed files with 252 additions and 0 deletions
@ -0,0 +1,5 @@ |
|||||
|
#!/usr/bin/env bash |
||||
|
|
||||
|
UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" |
||||
|
|
||||
|
"${UMBREL_ROOT}/scripts/backup/backup" |
@ -0,0 +1,126 @@ |
|||||
|
#!/usr/bin/env bash |
||||
|
|
||||
|
set -euo pipefail |
||||
|
|
||||
|
UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" |
||||
|
BACKUP_FOLDER="backup" |
||||
|
BACKUP_ROOT="${UMBREL_ROOT}/${BACKUP_FOLDER}" |
||||
|
BACKUP_FILE="${UMBREL_ROOT}/backup.tar.gz.pgp" |
||||
|
|
||||
|
check_dependencies () { |
||||
|
for cmd in "$@"; do |
||||
|
if ! command -v "$cmd" >/dev/null 2>&1; then |
||||
|
echo "This script requires \"${cmd}\" to be installed" |
||||
|
exit 1 |
||||
|
fi |
||||
|
done |
||||
|
} |
||||
|
|
||||
|
check_dependencies openssl tar gpg shuf curl |
||||
|
|
||||
|
# Deterministically derives 128 bits of cryptographically secure entropy |
||||
|
derive_entropy () { |
||||
|
identifier="${1}" |
||||
|
umbrel_seed=$(cat "${UMBREL_ROOT}/db/umbrel-seed/seed") || true |
||||
|
|
||||
|
if [[ -z "$umbrel_seed" ]] || [[ -z "$identifier" ]]; then |
||||
|
>&2 echo "Missing derivation parameter, this is unsafe, exiting." |
||||
|
rm -f "${UMBREL_ROOT}/statuses/backup-in-progress" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
# We need `sed 's/^.* //'` to trim the "(stdin)= " prefix from some versions of openssl |
||||
|
printf "%s" "${identifier}" | openssl dgst -sha256 -hmac "${umbrel_seed}" | sed 's/^.* //' |
||||
|
} |
||||
|
|
||||
|
# Make sure an update is not in progres |
||||
|
if [[ -f "${UMBREL_ROOT}/statuses/backup-in-progress" ]]; then |
||||
|
echo "A backup is already in progress. Exiting now." |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
echo "Creating lock..." |
||||
|
touch "${UMBREL_ROOT}/statuses/backup-in-progress" |
||||
|
|
||||
|
[[ -f "${UMBREL_ROOT}/.env" ]] && source "${UMBREL_ROOT}/.env" |
||||
|
BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet} |
||||
|
|
||||
|
[[ -d "${BACKUP_ROOT}" ]] && rm -rf "${BACKUP_ROOT}" |
||||
|
[[ -f "${BACKUP_FILE}" ]] && rm -f "${BACKUP_FILE}" |
||||
|
|
||||
|
echo "Deriving keys..." |
||||
|
|
||||
|
backup_id=$(derive_entropy "umbrel_backup_id") |
||||
|
encryption_key=$(derive_entropy "umbrel_backup_encryption_key") |
||||
|
|
||||
|
echo "Creating backup..." |
||||
|
|
||||
|
mkdir -p "${BACKUP_ROOT}" |
||||
|
|
||||
|
cp --archive "${UMBREL_ROOT}/lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/channel.backup" "${BACKUP_ROOT}/channel.backup" |
||||
|
|
||||
|
# We want to back up user settings too, however we currently store the encrypted |
||||
|
# mnemonic in this file which is not safe to backup remotely. |
||||
|
# Uncomment this in the future once we've ensured there's no critical data in |
||||
|
# this file. |
||||
|
# cp --archive "${UMBREL_ROOT}/db/user.json" "${BACKUP_ROOT}/user.json" |
||||
|
|
||||
|
echo "Adding random padding..." |
||||
|
|
||||
|
# Up to 10KB of random binary data |
||||
|
# This prevents the server from being able to tell if the backup has increased |
||||
|
# decreased or stayed the sme size. Combined with random interval decoy backups |
||||
|
# this makes a (already very difficult) timing analysis attack to correlate backup |
||||
|
# activity with channel state changes practically impossible. |
||||
|
padding="$(shuf -i 0-10240 -n 1)" |
||||
|
dd if=/dev/urandom bs="${padding}" count=1 > "${BACKUP_ROOT}/.padding" |
||||
|
|
||||
|
echo "Creating encrypted tarball..." |
||||
|
|
||||
|
tar \ |
||||
|
--create \ |
||||
|
--gzip \ |
||||
|
--verbose \ |
||||
|
--directory "${UMBREL_ROOT}" \ |
||||
|
"${BACKUP_FOLDER}" \ |
||||
|
| gpg \ |
||||
|
--batch \ |
||||
|
--symmetric \ |
||||
|
--cipher-algo AES256 \ |
||||
|
--passphrase "${encryption_key}" \ |
||||
|
--output "${BACKUP_FILE}" |
||||
|
|
||||
|
# To decrypt: |
||||
|
# cat "${BACKUP_FILE}" | gpg \ |
||||
|
# --batch \ |
||||
|
# --decrypt \ |
||||
|
# --passphrase "${encryption_key}" \ |
||||
|
# | tar \ |
||||
|
# --extract \ |
||||
|
# --verbose \ |
||||
|
# --gzip |
||||
|
|
||||
|
BACKUP_API_URL="https://pvf3ozmmfl.execute-api.us-east-1.amazonaws.com/prod/v1/upload" |
||||
|
|
||||
|
if [[ $BITCOIN_NETWORK == "testnet" ]]; then |
||||
|
BACKUP_API_URL="https://as0ot0lg7h.execute-api.us-east-1.amazonaws.com/dev/v1/upload" |
||||
|
fi |
||||
|
if [[ $BITCOIN_NETWORK == "regtest" ]]; then |
||||
|
BACKUP_API_URL="https://5fxwqbum7g.execute-api.us-east-1.amazonaws.com/dev/v1/upload" |
||||
|
fi |
||||
|
|
||||
|
echo "Uploading backup..." |
||||
|
curl --socks5 localhost:9150 -F "file=@/${BACKUP_FILE}" "${BACKUP_API_URL}/${backup_id}" |
||||
|
echo |
||||
|
|
||||
|
rm -rf "${BACKUP_ROOT}" |
||||
|
rm -f "${BACKUP_FILE}" |
||||
|
|
||||
|
echo "Removing lock..." |
||||
|
rm -f "${UMBREL_ROOT}/statuses/backup-in-progress" |
||||
|
|
||||
|
echo "=============================" |
||||
|
echo "===== Backup successful =====" |
||||
|
echo "=============================" |
||||
|
|
||||
|
exit 0 |
@ -0,0 +1,41 @@ |
|||||
|
#!/usr/bin/env bash |
||||
|
|
||||
|
set -euo pipefail |
||||
|
|
||||
|
UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" |
||||
|
|
||||
|
check_if_not_already_running() { |
||||
|
if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep |
||||
|
then |
||||
|
echo "decoy trigger is already running" |
||||
|
exit 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
check_dependencies () { |
||||
|
for cmd in "$@"; do |
||||
|
if ! command -v "$cmd" >/dev/null 2>&1; then |
||||
|
echo "This script requires \"${cmd}\" to be installed" |
||||
|
exit 1 |
||||
|
fi |
||||
|
done |
||||
|
} |
||||
|
|
||||
|
check_if_not_already_running |
||||
|
|
||||
|
check_dependencies shuf |
||||
|
|
||||
|
main () { |
||||
|
while true; do |
||||
|
minutes_in_seconds="60" |
||||
|
hours_in_seconds="$((${minutes_in_seconds} * 10))" |
||||
|
max_interval="$((8 * ${hours_in_seconds}))" |
||||
|
delay="$(shuf -i 0-${max_interval} -n 1)" |
||||
|
echo "Sleeping for ${delay} seconds..." |
||||
|
sleep $delay |
||||
|
echo "Triggering decoy backup..." |
||||
|
touch "${UMBREL_ROOT}/events/signals/backup" |
||||
|
done |
||||
|
} |
||||
|
|
||||
|
main |
@ -0,0 +1,69 @@ |
|||||
|
#!/usr/bin/env bash |
||||
|
|
||||
|
check_root () { |
||||
|
if [[ $UID != 0 ]]; then |
||||
|
echo "Error: This script must be run as root." |
||||
|
exit 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
check_if_not_already_running() { |
||||
|
if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep |
||||
|
then |
||||
|
echo "backup monitor is already running" |
||||
|
exit 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
check_dependencies () { |
||||
|
for cmd in "$@"; do |
||||
|
if ! command -v $cmd >/dev/null 2>&1; then |
||||
|
echo "This script requires \"${cmd}\" to be installed" |
||||
|
exit 1 |
||||
|
fi |
||||
|
done |
||||
|
} |
||||
|
|
||||
|
check_root |
||||
|
|
||||
|
check_if_not_already_running |
||||
|
|
||||
|
check_dependencies fswatch readlink dirname |
||||
|
|
||||
|
UMBREL_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/../.." |
||||
|
|
||||
|
monitor_file () { |
||||
|
local file_path="${1}" |
||||
|
echo "Monitoring $file_path" |
||||
|
echo |
||||
|
|
||||
|
if [[ ! -e "${file_path}" ]]; then |
||||
|
echo "$file_path doesn't exist, waiting for it to be created..." |
||||
|
echo |
||||
|
until [[ -e "${file_path}" ]]; do |
||||
|
echo "Nope, $file_path still doesn't exist..." |
||||
|
sleep 1 |
||||
|
done |
||||
|
touch "${UMBREL_ROOT}/events/signals/backup" |
||||
|
fi |
||||
|
|
||||
|
fswatch -0 --event Updated $file_path | xargs -0 -n 1 -I {} touch "${UMBREL_ROOT}/events/signals/backup" |
||||
|
} |
||||
|
|
||||
|
if [[ ! -d "${UMBREL_ROOT}" ]]; then |
||||
|
echo "Root dir does not exist '$UMBREL_ROOT'" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
[[ -f "${UMBREL_ROOT}/.env" ]] && source "${UMBREL_ROOT}/.env" |
||||
|
BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet} |
||||
|
|
||||
|
# Monitor LND channel.backup |
||||
|
monitor_file "${UMBREL_ROOT}/lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/channel.backup" & |
||||
|
|
||||
|
# Monitor db/user.json |
||||
|
# We want to back up user settings too, however we currently store the encrypted |
||||
|
# mnemonic in this file which is not safe to backup remotely. |
||||
|
# Uncomment this in the future once we've ensured there's no critical data in |
||||
|
# this file. |
||||
|
# monitor_file "${UMBREL_ROOT}/db/user.json" & |
Loading…
Reference in new issue