forked from getumbrel/umbrel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatic deterministic backups (getumbrel#188)
- Loading branch information
1 parent
64bba35
commit 473380e
Showing
7 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/usr/bin/env bash | ||
|
||
UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" | ||
|
||
"${UMBREL_ROOT}/scripts/backup/backup" |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" & |
This file contains 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