-
Notifications
You must be signed in to change notification settings - Fork 4
/
gpg-backup.sh
executable file
·140 lines (114 loc) · 3.02 KB
/
gpg-backup.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/bin/bash
set -euo pipefail
VERBOSE="${VERBOSE-}"
HOST="$HOSTNAME"
GPG_RECIP="${GPG_RECIP-0xC7090B1A5F57CDC5}"
if [ -z "$HOST" ]; then
echo >&2 "error: missing HOSTNAME"
fi
DATE="$(date +%F)"
PREFIX="$HOST.$DATE"
PASS_FILE="$PREFIX.passphrase.gpg.gpg"
run() {
echo >&2 "+ $*"
"$@"
}
gpg() {
echo >&2 "+ gpg $*"
command gpg "$@"
}
# Use colorecho command, or fall back to echo.
if which colorecho >/dev/null; then
colorecho() {
command colorecho "$@"
}
else
colorecho() {
shift
echo "$@"
}
fi
phase() {
colorecho cyan "==== $* ====" >&2
"$@"
}
generate_passphrase() {
if [ -s "$PASS_FILE" ]; then
echo "Found existing $PASS_FILE"
# detect empty inner pass file
local size
size=$(gpg -d "$PASS_FILE" | wc -c)
if [ "$size" -eq 0 ]; then
colorecho red "Invalid passphrase file: inner .gpg is empty"
return 1
fi
return
fi
echo "Generating new symmetric pass file"
echo "Don't forget to create/copy the passphrase hint!"
run pwgen -s 52 1 | gpg -c | gpg -e -r "$GPG_RECIP" -o "$PASS_FILE" || {
colorecho yellow "You should probably delete $PASS_FILE"
return 1
}
}
backup_single() {
local tar_opts
tar_opts=()
colorecho cyan >&2 "## backup_single $*"
while [ $# -ge 1 ] && [[ $1 == -* ]]; do
case "$1" in
--excl-tag)
tar_opts+=("--exclude-tag=.ab-backup-exclude")
;;
--one-file-system)
tar_opts+=("--one-file-system")
;;
*)
echo >&2 "backup_single: unknown option $1"
return 1
;;
esac
shift
done
if [ $# -ne 2 ]; then
echo >&2 "usage: backup_single [OPTS] PATH SHORTNAME"
return 1
fi
local path shortname outfile
path="$1"
shortname="$2"
outfile="$PREFIX.$shortname.tar.zst.gpg"
if [ -s "$outfile" ]; then
if [ "$(wc -c < "$outfile")" -le 16 ]; then
colorecho red "Error: backup file $outfile already exists, but looks empty"
return 1
fi
prompt_yn "Backup file $outfile already exists... OK to skip?"
return
fi
if [ -n "$VERBOSE" ]; then
tar_opts+=(-v)
fi
# be sure we have sudo working before opening pipes
run sudo --validate
run sudo tar -c "${tar_opts[@]}" "$path" \
| run zstdmt \
| gpg -c -z 0 -o "$outfile" --batch --passphrase-fd 3 \
3< <(gpg -d "$PASS_FILE" | gpg -d)
}
backups_main() {
backup_single /boot boot
backup_single --one-file-system /etc etc
backup_single --one-file-system --excl-tag / root
backup_single --one-file-system --excl-tag /home/andy home
}
checksums_compute() {
run sha256sum "$PREFIX".*.gpg > "$PREFIX.SHA256SUMS.txt"
}
checksums_sign() {
gpg --detach-sign -a "$PREFIX.SHA256SUMS.txt"
}
phase generate_passphrase
phase backups_main
phase checksums_compute
phase checksums_sign