diff options
| author | Mitchell Riedstra <mitch@riedstra.dev> | 2021-10-26 22:24:57 -0400 |
|---|---|---|
| committer | Mitchell Riedstra <mitch@riedstra.dev> | 2021-10-26 22:24:57 -0400 |
| commit | fefc6c24ddf29bb29cb7d4c2713a4fde9dab0e25 (patch) | |
| tree | 92936b7c1e56272a75905f2fb7fc8c7f89099ae0 /dpw-age | |
| parent | 34d76d7d76f7554f846e59b62e4d21d76a85d970 (diff) | |
| download | dpw-fefc6c24ddf29bb29cb7d4c2713a4fde9dab0e25.tar.gz dpw-fefc6c24ddf29bb29cb7d4c2713a4fde9dab0e25.tar.xz | |
Add 'age' backend. Init functions.
Diffstat (limited to 'dpw-age')
| -rwxr-xr-x | dpw-age | 223 |
1 files changed, 223 insertions, 0 deletions
@@ -0,0 +1,223 @@ +#!/bin/sh +# Copyright 2021 Mitchell Riedstra +# +# Permission to use, copy, modify, and/or distribute this software for any purpose +# with or without fee is hereby granted, provided that the above copyright notice +# and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +# THIS SOFTWARE. +# +# Example storage plugin that wraps 'age' into a password manager. +# https://github.com/FiloSottile/age +set -e + +UMASK="${PASSWORD_STORE_UMASK:-077}" +umask "$UMASK" + +DPW_AGE_DIR="${DPW_AGE_DIR:-$HOME/.dpw-age}" +DPW_AGE_KEY="${DPW_AGE_KEY:-$HOME/.dpw-age-key}" + +USE_GIT=0 +[ -e "${DPW_AGE_DIR}/.git" ] && USE_GIT=1 + +# Helper functions + +_git_commit() { + [ $USE_GIT -eq 0 ] && return + cd "${DPW_AGE_DIR}" + git add --all + git commit -am "DPW Auto-commit: $1" +} + +_set_age_recipients() { +if [ -n "$DPW_AGE_RECIPIENTS" ] ; then + DPW_AGE_RECIPIENTS="$(echo "$DPW_AGE_RECIPIENTS" | tr ' ' '\n' \ + | sed -e's/^/ -r/' | tr '\n' ' ')" + return +fi +_pth="$1"; shift + +_pth="${DPW_AGE_DIR}/$_pth" + + +id_file="$(dirname "${_pth}")/.recipients" +while true ; do + + # Break if id_file is above our password store directory + case $id_file in + ${DPW_AGE_DIR}*) ;; + *) break ;; + esac + + if [ -e "$id_file" ] ; then + keys="" + while read -r key ; do + if [ -z "$key" ] ; then continue ; fi + keys="$keys -r $key" + done < "$id_file" + + export DPW_AGE_RECIPIENTS="$keys" + return + fi + + # Pop this up a directory level for the next time 'round + id_file="$(dirname "$id_file")" + id_file="$(dirname "$id_file")/.recipients" +done + +echo "No '.recipients' files found, is '$DPW_AGE_DIR' initialized?" +exit 1 +} + + +# Interface + +show() { +pth="$1"; shift +#shellcheck disable=SC2086 +exec age -i "${DPW_AGE_KEY}" -d < "${DPW_AGE_DIR}/${pth}.age" +} + + +insert() { +pth="$1"; shift +_set_age_recipients "$pth" +mkdir -p "$DPW_AGE_DIR/$(dirname "$pth")" +#shellcheck disable=SC2086 +age -e ${DPW_AGE_RECIPIENTS} \ + > "${DPW_AGE_DIR}/${pth}.age" +_git_commit "Insert: $pth" +} + +list() { +find "$DPW_AGE_DIR/$1" -type f -iname "*.age" \ + | while read -r line ; do + line="${line##$DPW_AGE_DIR}" + line="${line#/}" + line="${line#/}" + line="${line%.age}" + echo "$line" + done +} + +remove() { +cd "$DPW_AGE_DIR" +recursive= +force= +while [ $# -gt 0 ] ; do case $1 in + -r) recursive="-r" ; shift ;; + -f) force="-f" ; shift ;; + -rf|-fr) recursive="-r" ; force="-f" ; shift ;; + *) break ;; +esac ; done + +files= +for fn in "$@" ; do + if [ -e "${fn}.age" ] ; then + files="${fn}.age " + else + files="$fn " + fi +done + +#shellcheck disable=SC2086 +rm $recursive $force $files +_git_commit "Remove: $*" +} + +_init_help() { +cat <<EOF +Usage: $0 [--no-git] [--gen-key] [--[no-]pass] <recipients>... + +Git is the default. + +Gen key will generate a passphrase protected private key for you and place it in +\$DPW_AGE_KEY ($DPW_AGE_KEY) and the public side will be '.pub' + +No pass disables the default for a passphrase protected key file + +The key will automatically be added to the recipients list. + +Multiple recipients may be specified + +See: https://github.com/FiloSottile/age for more information on 'age' itself. +EOF +exit 1 +} + +_init() { +USE_GIT=1 +if [ -d "${DPW_AGE_DIR}" ] ; then + echo "Cannot init new password store, one exists" + exit 1 +fi + +recipients= +genkey=1 +passphrase=1 +while [ $# -gt 0 ] ; do case $1 in + --no-git) USE_GIT=0 ; shift ;; + --gen-key) genkey=1; shift ;; + --pass) passphrase=1; shift ;; + --no-pass) passphrase=0; shift ;; + -r|--recipient) recipients="$recipients:$2"; shift ; shift ;; + *) _init_help ;; +esac ; done + +[ $genkey -eq 1 ] && [ -e "${DPW_AGE_KEY}" ] \ + && echo "Cannot continue, \"${DPW_AGE_KEY}\" already exists" && exit + +echo "genkey: $genkey passphrase: $passphrase" + +if [ $genkey -eq 1 ] && [ $passphrase -eq 1 ] ; then + touch "${DPW_AGE_KEY}.pub" # Let the user's UMASK bet set + umask 077 # but override it for the key + age-keygen 2>"${DPW_AGE_KEY}.pub" | age -p > "${DPW_AGE_KEY}" 2>&2 + sed -i.bak -e's/^Public key: //g' "${DPW_AGE_KEY}.pub" + rm "${DPW_AGE_KEY}.pub.bak" + umask "$UMASK" +elif [ $genkey -eq 1 ] ; then + touch "${DPW_AGE_KEY}.pub" # Let the user's UMASK bet set + umask 077 # but override it for the key + age-keygen >"${DPW_AGE_KEY}" + sed -ne's/^# public key: //gp' < "${DPW_AGE_KEY}" > "${DPW_AGE_KEY}.pub" + umask "$UMASK" +fi + +mkdir -p "${DPW_AGE_DIR}" +cd "${DPW_AGE_DIR}" + +if [ $USE_GIT -eq 1 ] ; then + git init + cat >> .git/config <<EOF +[diff "age"] + binary = true + textconv = age --decrypt -i "${DPW_AGE_KEY}" +EOF + + echo "*.age diff=age" >> .gitattributes +fi + +cat < "${DPW_AGE_KEY}.pub" > "${DPW_AGE_DIR}/.recipients" +echo "$recipients" | tr ':' '\n' >> "${DPW_AGE_DIR}/.recipients" + +_git_commit + +echo "Age Password store initialized in ${DPW_AGE_DIR}" +} + +act="$1"; shift +case $act in + show) show "$@" ;; + list) list "$@" ;; + insert) insert "$@" ;; + rm) remove "$@" ;; + init) _init "$@" ;; + *) echo "Bad command $act"; exit 1; ;; +esac |
