diff options
| author | Robert Günzler <r@gnzler.io> | 2022-07-01 16:50:25 +0200 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2022-08-02 22:13:49 +0200 |
| commit | dd31339e49c28b9c719b60425c5f7ae31a0e45f4 (patch) | |
| tree | ad84e975ec2a1259e11cefa006c502a47e3f6f7e /bin | |
| parent | ca1bfeaab7613c210f31775d0636cfcd7caa694c (diff) | |
os: finish implementing including encryption using sops
Diffstat (limited to 'bin')
| -rwxr-xr-x | bin/os-conf | 136 |
1 files changed, 94 insertions, 42 deletions
diff --git a/bin/os-conf b/bin/os-conf index 94f60a4..8ee143d 100755 --- a/bin/os-conf +++ b/bin/os-conf @@ -1,56 +1,108 @@ #!/bin/sh +set -e +[ -n "$DEBUG" ] && set -x + OS_DIR=${OS_DIR:-$HOME/os} OS_MANIFEST=$OS_DIR/manifest -noop="echo " -total=0 +target=/tmp/os/ +restore= +list= +while getopts r:lh opt; do + case "$opt" in + r) + restore=1 + target="$OPTARG" + ;; + l) list=1 ;; + h | ?) + printf "usage: %s [options]\n" "$(basename "$0")" + printf "\n" + printf " -l list files that would be saved\n" + printf " -r TARGET restore to TARGET (default: %s)\n" "${target}" + printf "\n" + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) -copy_file() { - path=$1 - if [ -f "$path" ]; then - # check that the file in the manifest matches - # skip if it does - if cmp "$path" "$OS_DIR$path" >/dev/null; then - return - fi - # mkdir base path - mkdir -p "$(dirname "$OS_DIR$path")" - # copy - cp -v "$path" "$OS_DIR$path" - # tally - total=$((total + 1)) - elif [ -d "$path" ]; then - # handle dir - for file in "$path"/*; do - copy_file "$file" +tmp_prefix=os-secret- +# cleanup +trap '{ rm -f "/tmp/${tmp_prefix}"*; }' EXIT + +handle_secret() { + # NOTE: we can't print anything to stdout here, only the final encrypted file + # to be included in the os archive + case "$1" in + encrypt) + # read the first line from input and use it as path + IFS=$(printf '\r') read -r filepath + archive=/tmp/"$tmp_prefix"$(printf "%s" "$filepath" | md5sum | cut -d' ' -f1) + # create tar archive from input, descends into directory + ( + printf "%s\n" "$filepath" + cat - + ) | + doas sh -c "( + tar -cz -f $archive -T - 2>/dev/null; + chown 1000:1000 $archive; + )" + # encrypt the tarfile and amend some information that makes + # reconstructing easier + ( + sops --config /dev/null --encrypt "$archive" 2>/dev/null | + jq -r '.sops.data_extension |= "tar.gz"' + ) >"${archive}.enc" + printf "%s\n" "${archive}.enc" + ;; + decrypt) + while IFS=$(printf '\r') read -r line; do + # if $(jq -r '.sops.data_extension' "$line") == "tar.gz" + sops --config /dev/null exec-file "$line" "tar xzf - -C $target < {}" done - else - # noop - echo "noop: $path" - return - fi + ;; + esac } -main() { - while IFS="\r" read -r line - do - [ -z "$line" ] && continue - echo "$line" | awk '/#.*/{exit 1}' || continue - - # check that the file in the manifest exists - if [ ! -e "$line" ]; then - echo "error: $line not found" >&2 - continue +handle_rules() { + while IFS=$(printf '\r') read -r line; do + if printf "%s" "$line" | grep -qe '#secret$'; then + printf "%s\n" "$line" | sed -e 's/\s#secret//' | handle_secret encrypt + else + # shellcheck disable=SC2086 + find "$(printf "%s\n" $line)" -print 2>/dev/null || true fi - copy_file "$line" - done + } -uniq "$OS_MANIFEST" \ - | grep -v '^$' \ - | grep -v '^#' \ - | main +_rsync() { + extra= + [ -n "$list" ] && extra="--dry-run" + rsync -v -hhh $extra \ + --archive \ + --links \ + "$@" +} -echo "$total files." +if [ -n "$restore" ]; then + printf "os-conf: restoring %s to %s\n" "${OS_DIR}" "${target}" >&2 + mkdir -p "${target}" || true + _rsync \ + --exclude '/manifest' \ + --exclude '/README.md' \ + --exclude 'colors.todo' \ + --exclude 'os-secret*.enc' \ + "${OS_DIR}/" "${target}" + find "${OS_DIR}" -type f -name 'os-secret*.enc' -print | + handle_secret decrypt +else + printf "os-conf: saving to %s\n" "${OS_DIR}" >&2 + uniq "$OS_MANIFEST" | + grep -v '^$' | + grep -v '^#' | + handle_rules | + _rsync --files-from=- "/" "${OS_DIR}/" +fi |