Skip to content

Commit

Permalink
Automatic deterministic backups (getumbrel#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukechilds authored Aug 30, 2020
1 parent 64bba35 commit 473380e
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 0 deletions.
Empty file added db/umbrel-seed/.gitkeep
Empty file.
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ services:
- ${PWD}/tor/torrc:/etc/tor/torrc
- ${PWD}/tor/data:/var/lib/tor/
- ${PWD}/tor/run:/var/run/tor/
ports:
- "127.0.0.1:9150:29050"
networks:
net:
ipv4_address: 10.11.5.1
Expand Down Expand Up @@ -97,6 +99,7 @@ services:
DOCKER_COMPOSE_DIRECTORY: $PWD
DEVICE_HOST: ${DEVICE_HOST:-http://umbrel.local}
MIDDLEWARE_API_URL: "http://10.11.2.2"
UMBREL_SEED_FILE: "/db/umbrel-seed/seed"
UMBREL_DASHBOARD_HIDDEN_SERVICE_FILE: "/var/lib/tor/web/hostname"
BITCOIN_P2P_HIDDEN_SERVICE_FILE: "/var/lib/tor/bitcoin-p2p/hostname"
BITCOIN_P2P_PORT: $BITCOIN_P2P_PORT
Expand Down
5 changes: 5 additions & 0 deletions events/triggers/backup
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"
126 changes: 126 additions & 0 deletions scripts/backup/backup
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
41 changes: 41 additions & 0 deletions scripts/backup/decoy-trigger
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
69 changes: 69 additions & 0 deletions scripts/backup/monitor
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" &
8 changes: 8 additions & 0 deletions scripts/start
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ echo "Starting karen..."
echo
./karen &

echo "Starting backup monitor..."
echo
./scripts/backup/monitor &

echo "Starting decoy backup trigger..."
echo
./scripts/backup/decoy-trigger &

echo
echo "Starting Docker services..."
echo
Expand Down

0 comments on commit 473380e

Please sign in to comment.