From 2c3ccab6adf3816a7af047bbc574cddae661160e Mon Sep 17 00:00:00 2001 From: mrjk Date: Wed, 14 Feb 2018 03:01:21 -0500 Subject: [PATCH] dev: clean ssh, separate tomb from git --- bin/idmgr | 47 ++- lib/idmgr_mod_git.sh | 238 ++++++++++++ lib/idmgr_mod_gpg.sh | 31 ++ lib/idmgr_mod_ssh.sh | 155 ++++++-- lib/idmgr_mod_tomb.sh | 846 +++++++++++++----------------------------- 5 files changed, 687 insertions(+), 630 deletions(-) create mode 100644 lib/idmgr_mod_git.sh diff --git a/bin/idmgr b/bin/idmgr index f48e291..cb37aae 100755 --- a/bin/idmgr +++ b/bin/idmgr @@ -154,6 +154,12 @@ idm_log () fi } +# export PS4='+[${SECONDS}s][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'; set -x; +# export PS4='.[${SECONDS}s] \[\e[36m\] ${FUNCNAME[0]:+${FUNCNAME[0]}()[${LINENO}]: }\[\e[m\]'; set -x; +# export PS4='. $( f="${FUNCNAME[0]:+${FUNCNAME[0]//}}"; printf "%10s:%00d %00d %10s| " ${BASH_SOURCE#$HOME/} ${LINENO} ${SECONDS} "$f")' ; set -x; +# export PS4='. $(f="${FUNCNAME[0]:+${FUNCNAME[0]//}}"; s=${BASH_SOURCE#$HOME/}; l=${LINENO}; t=${SECONDS}; printf "%00d %0d %16.50s() " $l $t "$f")' ; set -x; +# export PS4=' \[\e[36m\]> $(f="${FUNCNAME[0]:+${FUNCNAME[0]//}}"; s=${BASH_SOURCE#$HOME/}; l=${LINENO}; t=${SECONDS}; printf "%00d %0d %s():" $l $t "$f")\[\e[m\]\n' ; set -x; + #export LOG="idm_log_wrap \$FUNCNAME " idm_exit () @@ -161,6 +167,9 @@ idm_exit () set +x local rc=${1:-0} local msg lvl + #[ -p /dev/stdin ] \ + # && dump="$(&2 idm_log DUMP - } -idm_exit_trap() { +idm_exit_trap () { set +x rc=$? if [[ $rc -ne 0 ]]; then - idm_trace || true idm_log ERR "The script exited with exit code: $rc" + idm_trace || true + #else + # idm_log WARN "The script exit has been trapped !" + # idm_trace || true fi exit $rc } +idm_require_bin () { + local bin=$1 + shift 1 || true + local opts=${@-} + + if command -v "$bin" &> /dev/null; then + declare -g ${bin^^}="$bin $opts" + return 0 + else + idm_log ERR "Missing '$bin'" + return 1 + fi +} + + idm__load_lib () { local lib_name=${1} @@ -332,7 +361,7 @@ idm_validate () case $type in id) [ "$value" != '_' ] && \ - [[ "$value" =~ ^[a-zA-Z0-9_-]+$ ]] && return + [[ "$value" =~ ^[a-zA-Z0-9_-]+$ ]] && return $? ;; id_config) if [[ -f "$IDM_DIR_ID/$value.env" ]]; then @@ -340,10 +369,10 @@ idm_validate () fi ;; is_enabled) - [ ! -z "${SHELL_ID}" ] && return + [ ! -z "${SHELL_ID}" ] && return $? ;; is_disabled) - [ -z "${SHELL_ID}" ] && return + [ -z "${SHELL_ID}" ] && return $? ;; *) @@ -732,12 +761,12 @@ idm_menu_main () esac # Dispatch - #idm_log DEBUG "menu=$menu action=$action id=$id opt=$opt" - if [ "$( type -t idm_${menu}_${action} )" = function ]; then - idm_${menu}_${action} $id $opt + #idm_log DEBUG "menu=$menu action=${action:-_} id=$id opt=$opt" + if [ "$( type -t idm_${menu}_${action:-_} )" = function ]; then + idm_${menu}_${action:-_} $id $opt return $? elif [ "$( type -t idm_${menu} )" = function ]; then - idm_${menu} ${action} $id $opt + idm_${menu} ${action:-_} $id $opt return $? fi diff --git a/lib/idmgr_mod_git.sh b/lib/idmgr_mod_git.sh new file mode 100644 index 0000000..1483759 --- /dev/null +++ b/lib/idmgr_mod_git.sh @@ -0,0 +1,238 @@ +#!/bin/bash + +IDM_MOD_DEPS="id" + +## Required functions +########################################## + +# Debug and shortcuts +_git () { idm_git__bin ${@-}; } +idm_git_f () { + local id=$1 + local cmd=$2 + shift 2 + local opts=${*-} + + trap '' INT TERM EXIT + idm_validate id_config $id + idm_vars_git_local $id + + set -e + idm_git_${cmd#idm_git_} $opts + rc=$? + set -e + + if [ "$rc" -eq 0 ]; then + idm_exit 0 "Returns $rc" + else + idm_exit $rc WARN "Called: 'idm_git_${cmd#idm_git_} ${opts:+$opts }'" + fi + +} + +idm_git () +{ + local action=$1 + local id=$2 + shift 2 + local opts=${*-} + idm_validate id_config $id + idm_vars_git_local $id + + idm_git__bin $action $opts +} + +idm_git_help () +{ + echo "Git" + printf " %-20s: %s\n" "git ls" "List maanged files" + printf " " +} + +idm_git_ls () +{ + local id=$1 + idm_validate id_config $id + idm_vars_git_local $id + + _git ls-files | sed 's/^/ ~\//' | idm_log DUMP - +} + +idm_git_enable () +{ + local id=$1 + idm_validate id_config $id + idm_vars_git_local $id + + cat < /dev/null 2>&1 +} + +idm_git__has_commits () +{ + if idm_git__is_repo $id; then + find "$git_dir" -type f &>/dev/null && return 0 + fi + + return 1 +} + +idm_git__is_all_commited () +{ + [ "$( _git status -s | wc -l)" -eq 0 ] +} + + +## Other internal functions +############################## + +idm_git__get_files_of_interest () +{ + local id=${1} + + find_args="-maxdepth 2 -type f " + { + find $HOME/.ssh/ $find_args -name "${id}*" 2>/dev/null + find $HOME/.ssh/known_hosts.d/ $find_args -name "${id}*" 2>/dev/null + find $GNUPGHOME/private-keys-v1.d/ $find_args 2>/dev/null + find $PASSWORD_STORE_DIR/ $find_args 2>/dev/null + find $IDM_DIR_ID/ $find_args -name "$id*" 2>/dev/null + } | sed -E "s@$HOME/?@@g" + +} + + +## User functions +############################## + +idm_git_init () +{ + local id=$1 + shift + local opts=${@-} + idm_validate id_config $id + idm_vars_git_local $id + + + if idm_git__is_repo ; then + idm_log WARN "Do you want to override the esixting repo?" + idm_cli_timeout 1 || idm_exit 1 "User cancelled" + fi + + _git init $opts + idm_log NOTICE "Repository has been created into '$git_dir'" + + # Generate + _git config --add include.path "$git_config" + idm_tomb__gen_git_config > $git_config +} + +idm_tomb__gen_git_config () +{ + ( + cat < /dev/null + if [[ $? != 0 ]]; then + idm_log ERR "${gpg_id} is not a valid key ID." + return 1 + fi + done +} + +idm_gpg__is_valid_key() { + typeset -a recipients + recipients=($@) + # At least one private key must be present + for gpg_id in "${recipients[@]}"; do + gpg --list-secret-keys "$gpg_id" &> /dev/null + if [[ $? = 0 ]]; then + return 0 + fi + done + return 1 +} diff --git a/lib/idmgr_mod_ssh.sh b/lib/idmgr_mod_ssh.sh index bfc2d99..b2e8088 100644 --- a/lib/idmgr_mod_ssh.sh +++ b/lib/idmgr_mod_ssh.sh @@ -75,24 +75,23 @@ idm_ssh_enable () local id=$1 idm_is_enabled $id - #set -x - # Source environment if [ -f "${XDG_RUNTIME_DIR}/ssh-agent/${id}/env" ] ; then . "${XDG_RUNTIME_DIR}/ssh-agent/${id}/env" else - unset SSH_AUTH_SOCK + unset SSH_AUTH_SOCK SSH_AGENT_PID fi - # Check if the socket file is available - if [ ! -S "${SSH_AUTH_SOCK-}" ]; then - rm -f "${XDG_RUNTIME_DIR}/ssh-agent/${id}/env" - idm_ssh__start $id + # Check status + if ! idm_ssh__is_agent_working $id ${SSH_AUTH_SOCK:-_} ${SSH_AGENT_PID:-0}; then + if ! idm_ssh__agent_start $id; then + idm_log WARN "Could not start ssh agent :(" + return 1 + fi fi - # Show the things to source - cat "${XDG_RUNTIME_DIR}/ssh-agent/${id}/env" - + # Display config to load + cat "${XDG_RUNTIME_DIR}/ssh-agent/${id}/env" } # LOGOUT @@ -130,32 +129,86 @@ idm_ssh_kill () { } -## Internal functions +## Agent functions ########################################## -idm_ssh__start() { + +idm_ssh__is_agent_working () +{ + local id=$1 + local socket=${2:-_} + local pid=${3:-0} + local rc= + + set +e + SSH_AUTH_SOCK=$socket SSH_AGENT_PID=$pid ssh-add -l &>/dev/null + rc=$? + set -e + + [ "$rc" -lt 2 ] && return 0 +} + +idm_ssh__agent_start() { local id=$1 local life=5d local run_dir="${XDG_RUNTIME_DIR}/ssh-agent/${id}" - if [ -z "${SSH_AUTH_SOCK-}" ] ; then + # Check if we can recover from previous instance + idm_ssh__agent_clean $id "$run_dir/socket" 0 || true - if [ ! -d "$run_dir" ]; then - mkdir -p "$run_dir" - fi + # Ensure directory are present + [ -d "$run_dir" ] || \ + mkdir -p "$run_dir" - if [ ! -S "$run_dir/socket" ]; then - ssh-agent -a "$run_dir/socket" -t $life -s | grep ^SSH_ > "$run_dir/env" - idm_log INFO "Start ssh-agent ..." + # Ensure env file is not present + [ ! -f "${run_dir}/env" ] || \ + rm -f "${run_dir}/env" + set -x - else - idm_log INFO "The ssh-agent is already started (but not managed by ourself)" - fi + # Start the agent + if ssh-agent -a "$run_dir/socket" -t $life -s | grep ^SSH_ > "$run_dir/env"; then + echo "$run_dir/env" + idm_log INFO "Start ssh-agent ..." else - idm_log INFO "The ssh-agent is already started" + idm_log WARN "Could not start ssh agent :(" + return 1 fi + } +idm_ssh__agent_clean () { + local id=$1 + local socket=$2 + local pid=${3:-0} + + # We should kill all agents .... + if [ "${pid}" == '0' ]; then + set +x + pid=$(grep -a "$socket" /proc/*/cmdline \ + | grep -a -v 'thread-self' \ + | strings -s' ' -1 \ + | sed -E 's@ /proc/@ \n/proc/@g' + ) + set -x + pid="$( sed -E 's@/proc/([0-9]*)/.*@\1@' <<<"$pid" )" + fi + set -x + + # Remove process + if [ "$pid" != '0' ]; then + kill $pid + fi + + # Remove socket + if [ -f "$socket" ]; then + rm $socket + fi + + unset SSH_AUTH_SOCK SSH_AGENT_PID + #idm_log INFO "ssh-agent env cleaned is now clean" +} + + ## Extended functions ########################################## @@ -199,3 +252,59 @@ idm_ssh_add () } +## Deprecated functions +########################################## + +# Useless at this stage i guess +idm_ssh__agent_check () +{ + set -x + local id=$1 + local socket=${2:-_} + local pid=${3:-0} + + if [ "$socket" == '_' ] && [ "$pid" == '0' ] ; then + # Parameters are not valid, we assume ssh-agent is not launched at all + return 1 + elif SSH_AUTH_SOCK=$socket SSH_AGENT_PID=$pid ssh-add -l &>/dev/null ; then + return 0 + else + idm_log WARN "ssh-agent is not working as expected" + fi + + # Is the socket valid ? + if [ "$socket" != '_' -a ! -S "$socket" ]; then + idm_log WARN "Socket '$socket' is dead, can't recover ssh-agent" + idm_ssh__agent_clean $id $socket 0 + return 1 + fi + + if [ "$pid" != '0' -a "$pid" -lt 1 ]; then + local pid="$( ps aux | grep "$socket" | grep -v 'grep' | head -n 1 | awk '{ print $2 }' )" || \ + pid="$( ps aux | grep "" | grep -v 'grep' | head -n 1 | awk '{ print $2 }' )" || \ + { + idm_log WARN "Process ssh-agent is dead, cannot recover" + idm_ssh__agent_clean $id $socket 0 + return 1 + } + + # Kill all processes + idm_log DEBUG "Multiple PID founds for ssh-agent: $pid" + q=0 + for p in $pid; do + return + idm_ssh__agent_clean $id $socket $pid || true + q=1 + done + [ "$q" -eq 0 ] || return 1 + + fi + + # Ok, now we can try to recover the things + + + # Hmm, we should not arrive here ... + idm_log WARN "ssh-agent is in a really weird state :/" + return 1 + +} diff --git a/lib/idmgr_mod_tomb.sh b/lib/idmgr_mod_tomb.sh index dcb6e53..3c3be0d 100644 --- a/lib/idmgr_mod_tomb.sh +++ b/lib/idmgr_mod_tomb.sh @@ -1,6 +1,6 @@ #!/bin/bash -IDM_MOD_DEPS="ssh gpg" +IDM_MOD_DEPS="id gpg git" IDM_MOD_TAGS="id tool" IDM_MOD_PROG="safe yadm" IDM_MOD_PREF="core id" @@ -17,652 +17,302 @@ IDM_MOD_PREF="core id" #set -x -## Dependencies +## Common functions ############################## -idm_tomb__load_safe () -{ - SOURCE_DIR= - MY_GPG_KEY=NOGPG - COMPARE_BACKUPS=false - - export SOURCE_DIR=$YADM_DIR - export MY_GPG_KEY= - #set -x - set +u - idm__load_lib safe -v 2>&1 >/dev/null || true - set -u - #set +x -} - - -## Required functions -############################## - - -idm_tomb__init () -{ - local id=${1} - idm_validate id $id - - # Module config - export IDM_TOMB_LOCAL_GIT=$IDM_DIR_CACHE/git/$id/local.git - export IDM_TOMB_LOCAL_ENC=$IDM_DIR_CACHE/git/$id/local.git.tar.gz.asc - export IDM_TOMB_ORIGIN_GIT=$IDM_DIR_CACHE/git/$id/origin.git - export IDM_TOMB_ORIGIN_ENC=$IDM_CONFIG_DIR/enc/$id.tomb -} idm_tomb_help () { + local id=$1 + idm_vars_git_tomb $id echo "tomb" - printf " %-20s: %s\n" "tomb ls" "List all tombable files" - printf " %-20s: %s\n" "tomb encrypt" "Save the current configuration" - printf " %-20s: %s\n" "tomb decrypt" "Restore a tomb" - printf " %-20s: %s\n" "tomb import [] " "Import a config" - printf " %-20s: %s\n" "tomb leave" "Remove all traces of your passage" - echo "" - echo " Tomb is completely backed by yadm, this may change later" - echo " Use 'yadm help' to get backend help ..." + echo " workflow:" + printf " %-20s: %s\n" "tomb ls" "Show tomb status" + printf " %-20s: %s\n" "tomb import [] " "Import a config" + printf " %-20s: %s\n" "tomb decrypt" "Decrypt the tomb" + printf " %-20s: %s\n" "tomb sync" "Synchronise tomb(s)" + printf " %-20s: %s\n" "tomb encrypt" "Save the current configuration into the tomb" + printf " %-20s: %s\n" "tomb leave" "Remove all traces of your passage" + echo " config:" + printf " %-20s: %s\n" "tomb_enc" "$tomb_enc" + printf " %-20s: %s\n" "git_dir" "$git_dir" + printf " %-20s: %s\n" "git_config" "$git_config" return 0 - - echo "" - echo " Yadm documentation (yadm help):" - yadm help | sed 's/^/ /' - - # printf " %-20s: %s\n" "tomb sync " "Synchronise with remote repo (how ???)" } -idm_tomb () -{ - - # Argument maangement - if [ "$#" -eq 1 ]; then - local id=$1 - idm_ssh_ls $id - return 0 - else - local action=$1 - local id=${2-} - shift 2 || true - local opt=${@-} - fi - - idm_log INFO "Forward to yadm: yadm ${action} $opt" - yadm ${action} $opt || \ - idm_log ERR "Tomb fail" - -} - - idm_tomb_ls () { - local id=${1} - idm_validate id $id - idm_tomb__init $id + local id=$1 + idm_vars_git_tomb $id + - if [ -d $IDM_TOMB_ORIGIN_GIT ]; then - if [ -f $IDM_TOMB_ORIGIN_ENC ]; then - idm_log WARN "Tomb is available and secret repo is unlocked" - else - idm_log WARN "Tomb is not present and secret repo is unlocked" - fi + echo " Info:" + printf " %-20s: %s\n" "opened" "yes" + printf " %-20s: %s\n" "last mod." "yes" + printf " %-20s: %s\n" "git" "$git_dir" + printf " %-20s: %s\n" "last date" "" - # Show secured files - yadm list -a | sed 's:^:~/:' | idm_log DUMP - + echo " Remotes:" + _git_local remote -v | sed 's/^/ /' +} +## Internal functions +############################## + +idm_git__bin_git () +{ + if idm_validate id_config $1; then + id=${1} ;shift + fi + idm_vars_git_local $id + idm_git__bin ${*-} +} + + +# A wrapper before calling git +idm_tomb__bin_git () +{ + # Ugly bugfix :( + if idm_validate id_config $1; then + id=${1} ;shift + fi + idm_vars_git_tomb $id + idm_git__bin ${*-} +} + +# Shortcuts +_git_local () { idm_git__bin_git ${*-}; } +_git_tomb () { idm_tomb__bin_git ${*-}; } +_load_local_env () { idm_vars_git_local ${*-}; } +_load_tomb_env () { idm_vars_git_tomb ${*-}; } + +## Module functions +############################## + + +idm_vars_git_tomb() +{ + var_id=git_tomb + id=${id:-$SHELL_ID} + git_work_tree=$HOME + git_dir=$IDM_DIR_CACHE/git/$id/tomb.git + git_config=${IDM_CONFIG_DIR}/git/$id/tomb_gitconfig + tomb_enc=$IDM_CONFIG_DIR/enc/$id.tomb + +} + +idm_tomb_init() +{ + local id=$1 + shift || true + + # Sanity check + idm_validate id_config $id + local old_var_id=${var_id-} + + # Check local repository state + _load_local_env + local local_git_dir=$git_dir + if ! idm_git__is_repo; then + idm_exit 1 NOTICE "You need to have a local repo first" + elif ! idm_git__has_commits ; then + idm_exit 1 NOTICE "You need to commit all your changes" + fi - # Show modified files - if ! idm_tomb__is_all_commited ; then - idm_log WARN "Some files has been modified" - yadm -c color.status=always status -s - fi - - else - if [ -f $IDM_TOMB_ORIGIN_ENC ]; then - idm_log INFO "Tomb is available and secret repo is locked" - else - idm_log INFO "Tomb is absent and secret repo is locked" - fi - # export SOURCE_DIR=$IDM_TOMB_ORIGIN_GIT - # export SAFE_TAR_ENC=$IDM_TOMB_ORIGIN_ENC - # idm_log INFO "Encrypted files in $IDM_TOMB_ORIGIN_ENC: $(safe -l | wc -l )" - - # export SOURCE_DIR=$IDM_TOMB_LOCAL_GIT - # export SAFE_TAR_ENC=$IDM_TOMB_LOCAL_ENC - # idm_log INFO "Encrypted files in $IDM_TOMB_LOCAL_ENC: $(safe -l | wc -l )" + # Load tomb environment from local + _load_tomb_env + if [ ! -d "$git_dir" ] ; then + mkdir -p "$git_dir" + _git_tomb clone --bare $local_git_dir $git_dir || \ + idm_exit 1 "Could not create tomb repo" + idm_exit 0 NOTICE "Tomb repository has been created" fi + idm_log INFO "Tomb repository alreay exists" + + # Load tomb environment from encrypted_tomb + # Load tomb environment from user@server/encrypted.tomb + + [ "$old_var_id" == "$var_id" ] || idm_vars_${old_var_id} ${id} } -## Sourced functions -############################## - -idm_tomb_enable() +idm_tomb_sync () { - local id=${1} - idm_validate id $id + local id=$1 + shift || true - #mkdir -p $IDM_DIR_CACHE/git/$id/ $IDM_CONFIG_DIR/git/ || true - - echo "export YADM_WORK=$HOME" - echo "export YADM_DIR=$IDM_CONFIG_DIR/git/$id" - echo "export YADM_OVERRIDE_REPO=$IDM_DIR_CACHE/git/$id/local.git" - - echo "export IDM_TOMB_LOCAL_GIT=$IDM_DIR_CACHE/git/$id/local.git" - echo "export IDM_TOMB_LOCAL_ENC=$IDM_DIR_CACHE/git/$id/local.git.tar.gz.asc" - echo "export IDM_TOMB_ORIGIN_GIT=$IDM_DIR_CACHE/git/$id/origin.git" - echo "export IDM_TOMB_ORIGIN_ENC=$IDM_CONFIG_DIR/enc/$id.tomb" -} - -idm_tomb_disable() -{ - # Disable internal variables - echo "unset YADM_WORK YADM_DIR" | idm_log CODE - -} - -idm_tomb_kill () { idm_tomb_disable ${@-}; } - - -## Git functions -############################## - -idm_tomb_ls_local () -{ - yadm list -a | sed 's:^: ~/:' | idm_log DUMP - -} - -idm_tomb__is_all_commited () -{ - return $( { yadm status -s || true; } | wc -l) -} - -idm_tomb_init_local () -{ - local id=${1} - idm_validate id $id + # Sanity check + idm_validate id_config $id + # Load tomb config + _load_tomb_env + local repo_url=$git_dir + local repo_name=tomb - [ -d $YADM_OVERRIDE_REPO ] || { - yadm init || true - idm_log NOTICE "New repository was created for secret in $YADM_OVERRIDE_REPO" - } - - # Regenerate configs - idm_tomb__gen_gitconfig $id > $IDM_CONFIG_DIR/git/$id/gitconfig - idm_tomb__gen_config $id > $IDM_CONFIG_DIR/git/$id/config - # idm_tomb__gen_ignore $id | sed -e '/^[^$]/ s/^/!/' > $IDM_CONFIG_DIR/git/$id/gitignore + # Work on local + _git_local remote add $repo_name $repo_url || true + _git_local fetch --all + _git_local fetch --all --tags + _git_local push -u $repo_name --all + _git_local push -u $repo_name --tags + + idm_log NOTICE "Tomb and local repository are now synced" } -idm_tomb_scan () -{ - local id=${1} - idm_validate id $id - - # We need the local repo at last - idm_log WARN "Do you want to create a local repository of your secrets?" - idm_cli_timeout 1 || idm_exit 1 "User cancelled" - idm_tomb_init_local $id - - # ajoute une liste de fichier: git add - - file=$YADM_DIR/gitignore - result=$( idm_tomb__gen_ignore $id ) - - # Add all files - ( cd && xargs yadm add -f <<<${result} ) - - # Check uncommited changes - if ! idm_tomb__is_all_commited; then - idm_log NOTICE "All those files will be added:" - yadm status -s - yadm commit -m "Initial import" - fi - -} - -idm_tomb_replace () -{ - # Restore absent files - files="$( yadm status -s )" - - ok_files="$( sed -E '/^(D|.D)/!d;s/^...//' <<<$files | xargs || true)" - fail_files="$( sed -E '/^(D|.D)/d;s/^...//' <<<$files | xargs || true )" - - if [ ! -z "$ok_files" ]; then - idm_log INFO "Will restore:" - xargs -n1 <<<"$ok_files" | sed 's:^:~/:' | idm_log DUMP - - yadm co HEAD $ok_files - else - idm_log INFO "Tracked files are:" - yadm list -a | sed 's:^:~/:' | idm_log DUMP - - fi - - if [ ! -z "$fail_files" ]; then - idm_log WARN "Cannot restore:" - #sed 's:^:~/:' <<<"$fail_files" | xargs -n1 | idm_log DUMP - - yadm status -s - fi - - -} - - -## Crypt functions -############################## - -idm_tomb__encrypt_init () -{ - local id=${1} - idm_validate id $id - idm_tomb__init $id - idm_tomb__load_safe - - env | sort | grep ^IDM | idm_log DUMP - - - # Create destination paths - [ -d "$( dirname "$IDM_TOMB_LOCAL_ENC" )" ] || \ - mkdir -p "$( dirname "$IDM_TOMB_LOCAL_ENC" )" - [ -d "$( dirname "$IDM_TOMB_ORIGIN_ENC" )" ] || \ - mkdir -p "$( dirname "$IDM_TOMB_ORIGIN_ENC" )" - - # Ensure permissions are open to be deleted later (not a good idea, shred is better) - #chmod u+w -R "$IDM_TOMB_LOCAL_GIT" - #chmod u+w -R "$IDM_TOMB_ORIGIN_GIT" - - # Auto detect what to do - # We need the local repo at last - # idm_log WARN "Do you want to create a local repository of your secrets?" - # idm_cli_timeout 1 || idm_exit 1 "User cancelled" - # idm_tomb_init_local $id - - - # Check uncommited changes: We always want to have a stable state for git (absent is good) - if ! idm_tomb__is_all_commited; then - yadm status -s - idm_exit 0 "You need to commit all you changes" - fi - -} - -# option: Use -f to force overwrite of files idm_tomb_encrypt () { - local id=${1} + local id=$1 shift || true local opt=${@-} local TOFIX_opt=$opt - idm_tomb__encrypt_init $id + local old_var_id=${var_id-} - # We need a local git repo - if [ ! -d $IDM_TOMB_LOCAL_GIT ]; then - idm_exit 1 "Git repo is not enabled ($IDM_TOMB_LOCAL_GIT)" + # Sanity check + idm_validate id_config $id + + # We load LOCAL VARS HERE + _load_local_env + idm_git__has_commits || \ + idm_exit 1 "You need to commit first" + idm_git__is_all_commited || \ + idm_exit 1 "You need to clean your stuffs" + + # Full sync both repo + idm_tomb_sync $id + + # Encrypt data + idm_tomb__encrypt_dir $git_dir $tomb_enc || \ + idm_exit 1 "Could not create tomb" + + idm_log NOTICE "Tomb has been created: $tomb_enc" +} + + + + + +## GPG functions +############################## + + + +idm_tomb__decrypt_dir () +{ + local src=$1 + local dst=${2-} + local key=${3-} + local gpg_opts="" + local tar_opts= + + set -x + + # Check required bin + idm_require_bin tar || idm_exit 1 + idm_require_bin gpg2 || idm_exit 1 + export GPG=${GPG2:-$GPG} + + tar_opts=" -C ${dst%/*} -zx " + if [ ! -z "$key" ]; then + gpg_opts+="--batch -d" + else + gpg_opts+="-d" fi - # Do we overwrite ? - if [ -f "$IDM_TOMB_LOCAL_ENC" ]; then - [[ "$TOFIX_opt" =~ -f ]] || { - idm_log WARN "Do you want to overwrite '$IDM_TOMB_LOCAL_ENC'?" - idm_cli_timeout 1 || idm_exit 1 "User cancelled" - } - fi + #set -x + $GPG $gpg_opts $src | $TAR $tar_opts || \ + idm_exit 1 ERR "Could not decrypt file: $src into $dst" - # Push all commits to local origin - if [ -d "$IDM_TOMB_ORIGIN_GIT" ]; then - repo_name=origin - yadm remote add $repo_name $IDM_TOMB_ORIGIN_GIT 2>/dev/null || true - yadm push -u $repo_name --all 2>/dev/null || true - yadm push -u $repo_name --tags 2>/dev/null || true - elif [ -f "$IDM_TOMB_ORIGIN_ENC" ]; then - local rc=$? - idm_log NOTICE "An encrypted version of origin has been found: $IDM_TOMB_ORIGIN_ENC" - idm_log WARN "Do you want to sync with it before encrypt? (Need decrypt procedure!)" - idm_cli_timeout 1 || rc=$? - - if [ "$rc" -eq 0 ]; then - echo "Launche function to decrypt origin ..." +} + + + +idm_tomb__encrypt_dir () +{ + local src=$1 + local dst=$2 + local key=${3-} + local pass= + local recipients= + + # Check required bin + idm_require_bin tar || idm_exit 1 + idm_require_bin gpg2 || idm_exit 1 + export GPG=${GPG2:-$GPG} + + #GPG_KEY="$(yadm config yadm.gpg-recipient || true )" + #GPG_KEY="${GPG_DEFAULT_ID-}" + + # Check pgp key and arguments + if idm_gpg__is_valid_key $key; then + + shift 3 + local ok=0 ko=0 + recipients=${@:-${GPG_DEFAULT_ID-}} + gpg_opts="-e -r $recipients" + + # Determine if we are looking for key or password + for r in $recipients; do + idm_gpg__is_valid_recipients $r &>/dev/null \ + && ok=$(( $ok + 1 ))\ + || ko=$(( $ko + 1 )) + + if [[ "$ok" -ne 0 && "$ko" -ne 0 ]]; then + idm_exit 1 "One of the recipients is not known: $r in '$recipients'" + fi + done + + # Act according our pattern + if [[ "$ok" -eq 0 && "$ko" -ne 0 ]]; then + pass="$@" + recipients= + gpg_opts="-c" + idm_log NOTICE "Secret will be encrypted with pass '$pass'" + else + idm_log NOTICE "Secret will be encrypted with key '$key' ${recipients:+ to '$recipients'}" + fi + + else + if [ "$key" == "_ASK" ]; then + pass=_ASK + key= + gpg_opts="--no-default-recipient -e" + idm_log NOTICE "User will be prompted for known recipients" + elif [ -z "$key" -o "$key" == "_PASS" ]; then + pass= + key= + gpg_opts="-c" + idm_log NOTICE "User will be prompted for password (symetric)" + else + # Not available yet, see stdin for password input + # To fix: passwords in clear :/ use stdout3 + pass="$key" + key= + gpg_opts="-c --passphrase $pass --batch " + idm_log NOTICE "Secret will be encrypted with pass '***' (symetric)" fi fi - - # Find out gpg author, it should be the local key of user ! - #GPG_KEY="$(yadm config yadm.gpg-recipient || true )" - GPG_KEY="${GPG_DEFAULT_ID-}" - case "${GPG_KEY:-_}" in - ASK) GPG_OPTS=("--no-default-recipient -e") ;; - _) GPG_OPTS=("-c") ;; - *) - GPG_OPTS="-e -r $GPG_KEY" - idm_log NOTICE "Local tomb will be secured with your '$GPG_KEY' id" - ;; - esac - #GPG_OPTS=("--no-default-recipient" "-e -r $GPG_KEY") + #set -x # Encrypt all the stuffs - TAR="tar -C $(dirname $IDM_TOMB_LOCAL_GIT)" - $TAR -cz $IDM_TOMB_LOCAL_GIT 2>/dev/null | gpg2 -a $GPG_OPTS --yes -o $IDM_TOMB_LOCAL_ENC || \ - idm_exit 1 ERR "Could not encrypt tomb" + $TAR -C "${src%/*}" -cz "${src##*/}" 2>/dev/null | \ + $GPG -a $gpg_opts --yes -o $dst || \ + idm_exit 1 ERR "Could not encrypt directory: $src" - idm_log INFO "Local tomb closed into $IDM_TOMB_LOCAL_ENC" - - # Shred the local files? - #find $IDM_TOMB_LOCAL_GIT -type f | xargs shred -u - #rm -fr $IDM_TOMB_LOCAL_GIT - - return - - # Origin part ! - if [ ! -d $IDM_TOMB_ORIGIN_GIT ]; then - idm_log NOTICE "Creating remote tomb repo ..." - mkdir -p "$( dirname "$IDM_TOMB_ORIGIN_GIT" )" - git clone --bare $YADM_OVERRIDE_REPO $IDM_TOMB_ORIGIN_GIT || true - fi - - export SOURCE_DIR=$IDM_TOMB_ORIGIN_GIT - export SAFE_TAR_ENC=$IDM_TOMB_ORIGIN_ENC - #idm_log INFO "Origin tomb closed into $SAFE_TAR_ENC" - echo safe -c - - idm_log NOTICE "Your tombs are secure" + # File descritor tests ... + #exec 3<> /tmp/foo + #>&3 echo "$pass" + #{ echo "$pass\n" >&3 ; $TAR -C "$(dirname $src)" -cz "$src" 2>/dev/null; } | \ + #exec 3>&- #close fd 3. } -idm_tomb_decrypt () -{ - local id=${1} - idm_tomb__encrypt_init $id - - # Check status of repos ... - if [ ! -d $IDM_TOMB_LOCAL_GIT ]; then - if [ ! -f "$IDM_TOMB_LOCAL_ENC" ]; then - idm_exit 1 "Local tomb enc is not present, please encrypt first" - fi - else - idm_exit 0 "Local tomb enc is already decrypted in $IDM_TOMB_LOCAL_GIT" - fi - if [ ! -d $IDM_TOMB_ORIGIN_GIT ]; then - if [ ! -f "$IDM_TOMB_ORIGIN_ENC" ]; then - idm_exit 1 "Remote tomb enc is not present, please encrypt first" - fi - else - idm_exit 0 "Remote tomb enc is already decrypted in $IDM_TOMB_ORIGIN_GIT" - fi - - # Decrypt all the stuffs - export SOURCE_DIR=$IDM_TOMB_LOCAL_GIT - export SAFE_TAR_ENC=$IDM_TOMB_LOCAL_ENC - safe -x - - export SOURCE_DIR=$IDM_TOMB_ORIGIN_GIT - export SAFE_TAR_ENC=$IDM_TOMB_ORIGIN_ENC - safe -x - - # Did it success ? - if [ ! -d "$IDM_TOMB_ORIGIN_GIT" ]; then - idm_exit 1 "Origin tomb could not be decrypted" - else - idm_log INFO "Origin tomb opened into $SOURCE_DIR" - fi - if [ ! -d "$IDM_TOMB_ORIGIN_GIT" ]; then - idm_exit 1 "Local tomb could not be decrypted" - else - idm_log INFO "Local tomb opened into $SOURCE_DIR" - fi - - - # Push all commits to remote - # TOFIX: warn if uncommited changes ! - repo_name=origin - yadm remote add $repo_name $IDM_TOMB_ORIGIN_GIT 2>/dev/null || true - yadm fetch -u $repo_name --all 2>/dev/null || true - yadm fetch -u $repo_name --tags 2>/dev/null || true - - idm_tomb_replace - - # replace permission: git-cache-meta --store - idm_LOG DEBUG "Implement git cache permission" - - - idm_log NOTICE "Tombs are now open" -} - -idm_tomb_leave () -{ - local id=${1} - idm_validate id $id - idm_tomb__init $id - - #if yadm list -a >&/dev/null; then - list_of_files_to_del="$( yadm list -a || true )" - #else - # idm_exit 1 "There is no local repo" - #fi - - idm_tomb_encrypt $id - - idm_log WARN "All those files has been encrypted and safely removed:" - sed 's:^:~/:' <<<"$list_of_files_to_del" | idm_log DUMP - - sed "s:^:$HOME/:" <<<"$list_of_files_to_del" | xargs rm - #for f in $list_of_files_to_del; do - #done - - idm_log INFO "Run 'i quit' to disapear" -} - - -# idm_tomb__gpg_enc -# idm_tomb__gpg_dec -# idm_tomb__gpg_rm_short -# idm_tomb__gpg_rm_long -## Import/Export functions -############################## - -# export tomb files into a dir or on a remote host -idm_tomb_export () -{ - local id=${1} - local dest=${2-} - idm_validate id $id - idm_tomb__init $id - - # Check if a local tomb is vaialable - if [ -f "$IDM_TOMB_ORIGIN_ENC" ]; then - src_file=$IDM_TOMB_ORIGIN_ENC - elif [ -f "$IDM_TOMB_LOCAL_ENC" ] ; then - idm_log WARN "Will synchronise local tomb to remote origin tomb. Are you sure they are the same ?" - idm_cli_timeout 1 || idm_exit 1 "User cancelled" - src_file=$IDM_TOMB_LOCAL_ENC - else - idm_exit 1 "No tomb file available ... :(" - fi - - # Check destination - if [ -z "$dest" ]; then - idm_exit 1 "You need to give a path or an host" - fi - local dest_host dest_dir dest_name - - # Auto detect destination - if [ -d "$dest" ]; then - dest_host=_ - dest_dir=$dest - dest_name=$id.enc - elif [ -f "$dest" ]; then - dest_host=_ - dest_dir=$( dirname $dest ) - dest_name=$( basename $dest ) - else - - # Test ssh connection - rc=0 - #result=$( idm_tomb__remote_env_detect | ssh $dest 2>/dev/null ) || rc=$? - idm_log NOTICE "Trying to ssh $dest ..." - result=$( idm_tomb__remote_env_detect | ssh $dest ) || rc=$? - - if [ "$rc" -ne 0 ]; then - idm_log DUMP - <<<"$result" - idm_exit 1 "Could not find host or dir for '$dest'" - fi - dest_host=$dest - dest_dir=$result/enc - dest_name=$id.tomb - - fi - - # Cosmetic changes - dest_dir=$(realpath --relative-to . "$dest_dir" || echo "$dest_dir" ) - src_file=$(realpath --relative-to . "$src_file" || echo "$src_file" ) - - idm_log INFO "Destination is: $dest_host:$dest_dir/$dest_name from $src_file" - if ! scp $src_file $dest_host:$dest_dir/$dest_name ; then - idm_log DUMP "scp $src_file $dest_host:$dest_dir/$dest_name " - idm_exit 1 "Cound not copy to remote host: $dest_host:$dest_dir/$dest_name" - fi - idm_log INFO "Remote $dest_host have the same tomb for '$id' " - - - - -} - -idm_tomb_import () -{ - local id=${2-} - - if [ -z "$id" ]; then - id=MY_ID - idm_exit 1 "You need to provide a file or an id (look into: $IDM_TOMB_ORIGIN_ENC)" - fi - - if ! idm_validate id $id ; then - idm_exit 1 "The id '$id' is not valid" - fi - - idm_tomb__init $id - if [ ! -f "$IDM_TOMB_ORIGIN_ENC" ]; then - idm_exit 1 "You need to provide a file to import or place it here $IDM_TOMB_ORIGIN_ENC" - fi - - - idm_tomb_decrypt $id - idm_log INFO "Run 'i $id' to enable it" - -} -## Template functions -############################## - -idm_tomb__gen_ignore () -{ - local id=${1} - idm_validate id $id - - find_args="-maxdepth 2 -type f " - conf=$( cat </dev/null ) -$( find $HOME/.ssh/known_hosts.d/ $find_args -name "${id}*" 2>/dev/null ) -$( find $GNUPGHOME/private-keys-v1.d/ $find_args 2>/dev/null ) -$( find $PASSWORD_STORE_DIR/ $find_args 2>/dev/null ) -$( find $IDM_DIR_ID/ $find_args -name "$id*" 2>/dev/null ) -EOF -) - sed -E -e "s@$HOME/?@@g" <<<"$conf" - -} - -idm_tomb__gen_gitconfig () -{ - local id=${1} - idm_validate id $id - - ( - cat <&2 echo "REMOTE: Path has been created on $dest_host: \$d" || { - >&2 echo "REMOTE: Path couln't be created on $dest_host: \$d" - exit 1 - } - } || { - >&2 echo "REMOTE: Path has been found on $dest_host: \$d" - } - echo "\$d" -} - -EOF - -}