]> git.sev.monster Git - dotfiles.git/commitdiff
gpg: expand forwarding, add default-recipient-self
authorsev <git@sev.monster>
Thu, 26 Aug 2021 20:11:14 +0000 (15:11 -0500)
committersev <git@sev.monster>
Fri, 5 Apr 2024 21:27:40 +0000 (16:27 -0500)
- GPG forwarding has been expanded from simply overwriting the existing
  sockets to instead create a new environment for each connection.
- GPG SSH agent handling has been reworked.
- default-recipient-self was added to config.

base/.zlogout
base/.zshenv
base/.zshrc
bin/gpg-learn-keys
gpg/gpg.conf
ssh/config

index 9e0ed3fce7719b76a3d1e8bd1c2b7c6893b2c803..3b09ce51f1e967e453dc7a8bc7d6dba5499b89f6 100644 (file)
@@ -1,4 +1,23 @@
-echo logout
+# gpg forward cleanup
+if [[ $SHLVL == 1 && $GNUPGHOME =~ '.ssh_forward/\d+$' ]] {
+    x=$GNUPGHOME
+    # reset GNUPGHOME
+    [[ -o GLOB_ASSIGN ]]; y=$?
+    setopt GLOB_ASSIGN
+    GNUPGHOME=$GNUPGHOME/../..(:a)
+    [[ $y != 0 ]] && unsetopt GLOB_ASSIGN
+    # clean dir if it exists
+    if [[ -d $x ]] {
+        find $x -mindepth 1 -maxdepth 1 | while read -r y; do
+            unlink $y
+        done
+        rmdir -p $x 2>/dev/null
+    }
+    # clean up any remaining sockets from gpg forwarding
+    rm -f $_GNUPG_SOCK_DEST_BASE*(N=)
+}
+
+[[ -o interactive ]] && echo logout
 clear
 
 ### load site-specific
index 1f3e1982e69593bd93ad762e6ab0a0c08d5cc131..11cded74897d2df05e3a1329a9c398ca0d02d795 100644 (file)
@@ -1,3 +1,6 @@
+# WARN: not used in this repo, but gpgconf --list-dirs does not read
+#       gpg-agent.conf to get socket path, see dev.gnupg.org/T3108
+
 ### unset unwanted options that could be set in /etc/zshenv
 unsetopt SH_WORD_SPLIT KSH_ARRAYS
 
@@ -13,8 +16,11 @@ if [[ -v _sev_reset_shell || $SHLVL == 1 ]] {
     export LC_CTYPE=$LANG
 
     ## path
+    # path and fpath should already be linked to PATH and FPATH
+    # we don't want duplicates so turn on unique mode if it isn't already
     typeset -U path fpath
-    if [[ $SHLVL  == 1 ]] {
+    # do not run more than once per session, even if resetting shell
+    if [[ $SHLVL == 1 ]] {
         # take a backup before any customizations
         export _sev_sys_PATH=$PATH
         export _sev_sys_FPATH=$FPATH
@@ -24,13 +30,14 @@ if [[ -v _sev_reset_shell || $SHLVL == 1 ]] {
           /usr/local/{s,}bin /usr/games)
     PATH=$PATH:$_sev_sys_PATH
     fpath=(${ZDOTDIR:-$HOME/.zsh}/functions/{*,Completions/*}(N))
-    #fpath is not exported by default
+    # fpath is not exported by default
     export FPATH=$FPATH:$_sev_sys_FPATH
     # take another backup, explained in .zprofile
     typeset -U _backup_path
     _backup_path=("${path[@]}")
 
     ## xdg
+    # TODO: check for and merge existing XDG env vars
     export XDG_CONFIG_HOME=~/etc
     export XDG_CONFIG_DIRS=~/.config:/usr/pkg/etc/xdg:/usr/local/etc/xdg:/etc/xdg
     export XDG_DATA_HOME=~/share
@@ -42,17 +49,124 @@ if [[ -v _sev_reset_shell || $SHLVL == 1 ]] {
     }
 
     ## create tmp link
-    t=${TMPDIR:-/tmp}/home-$LOGNAME
+    t=${TMPDIR:-/tmp}/.home-$LOGNAME
     if [[ ! -e $t ]] {
-        mkdir -m 700 $t >/dev/null 2>&1
-        # TODO: check if dir exists after mkdir
+        mkdir -m 700 $t 2>/dev/null
+        if [[ ! -d $t ]] {
+            [[ -o interactive ]] &&
+              print -P "%F{red}!!! Can't create temp folder $t%f"
+            [[ -h $XDG_RUNTIME_DIR ]] && unlink $XDG_RUNTIME_DIR 2>/dev/null
+            [[ ! -e $XDG_RUNTIME_DIR ]] && mkdir $XDG_RUNTIME_DIR 2>/dev/null
+        }
     }
     # allow opaque entries to override link creation
-    if [[ ! -e $XDG_RUNTIME_DIR ]] {
-        ln -sf $t $XDG_RUNTIME_DIR >/dev/null 2>&1
-    }
+    [[ ! -e $XDG_RUNTIME_DIR ]] && ln -sf $t $XDG_RUNTIME_DIR 2>/dev/null
     unset t
 
+    ## gpg forwarding
+    # NOTE: while ssh automatically sets SSH_AUTH_SOCK with the ForwardSsh
+    #       directive, GPG must be forwarded manually. to support this, we
+    #       forward the restricted gpg-agent extra socket to the remote host
+    #       with a RemoteForward rule in ~/.ssh/config that uses the
+    #       _GNUPG_SOCK_* env vars.
+    #       to avoid conflicts with other ssh sessions where the same user is
+    #       connecting to the same host from different machines, gpg in each
+    #       environment should utilize its own forwarded socket, rather than
+    #       replace the sockets in GNUPGHOME which will be overridden on the
+    #       next connection. previously, you could provide a path to the agent
+    #       socket in GPG_AGENT_INFO, but that was deprecated in GPG v2.1.
+    #       instead, we must clone GNUPGHOME and replace the agent sockets
+    #       there with the forwarded one.
+    # HACK: without SendEnv, which is disabled by default in most sshd configs,
+    #       there is no foolproof way to prevent race conditions or filename
+    #       collisions, pass the forward path to the remote host environment,
+    #       or even know if the forward path exists and is writable. we just
+    #       have to guess this path is good on the desination host, and assume
+    #       the newest matching socket is the correct one after connecting. in
+    #       theory we could occlude the ssh binary on PATH with an alias or
+    #       script that communicates with the remote host before opening a
+    #       shell, but that would open up too many edge cases where it wouldn't
+    #       work to make it worth the effort and extra overhead.
+    # do not run more than once per session, even if resetting shell
+    if [[ $SHLVL == 1 && -v commands[gpg] ]] {
+        export _GNUPG_SOCK_DEST_BASE=/tmp/.gpg-agent-forward
+        export _GNUPG_SOCK_DEST_EXT=$(date +%s).$RANDOM
+        export _GNUPG_SOCK_DEST=$_GNUPG_SOCK_DEST_BASE.$_GNUPG_SOCK_DEST_EXT
+        _sev_gpg_forward_dir=${GNUPGHOME:-~/.gnupg}/.ssh_forward
+        s=($_GNUPG_SOCK_DEST_BASE*(N=oc[1]))
+        # clean up forwards if its session is dead or we ask for it
+        if [[ -d $_sev_gpg_forward_dir ]] {
+            find $_sev_gpg_forward_dir -type d -mindepth 1 -maxdepth 1 |
+              while read -r x; do
+                # NOTE: the only way we can get here is if we are SHLVL 1. if
+                #       our own pid already has a dir, it is most likely stale,
+                #       or something is very broken—assume the former.
+                p=$(basename $x)
+                if [[ -v _sev_gpg_forward_clean || $$ == $p ]] ||
+                      ! kill -0 $p 2>/dev/null; then
+                    find $x -mindepth 1 -maxdepth 1 | while read -r y; do
+                        unlink $y
+                    done
+                    rmdir $x
+                fi
+            done
+            unset x p y
+        }
+        # create new forward dir
+        if [[ -n $s && -v SSH_CLIENT ]] {
+            export _sev_gpg_forwarded=
+            mkdir -pm700 $_sev_gpg_forward_dir
+            h=$_sev_gpg_forward_dir/$$
+            mkdir -pm700 $h
+            # XXX: is it safe to link scdaemon socket? can its name be changed?
+            for x in S.scdaemon gpg.conf gpg-agent.conf sshcontrol \
+                     pubring.kbx trustdb.gpg private-keys-v1.d crls.d; do
+                ln -s ${GNUPGHOME:-~/.gnupg}/$x $h
+            done
+            export GNUPGHOME=$h
+            unset h
+            for x in $(gpgconf --list-dirs | grep 'agent-.*-\?socket:'); do
+                # dirs are prefixed and percent-encoded—strip and decode
+                # https://stackoverflow.com/a/64312099
+                x=${${x/#agent-*socket:/}//(#b)%([[:xdigit:]](#c2))/${(#):-0x$match[1]}}
+                if [[ ! -v orig ]] {
+                    mv $s $x
+                    orig=$x
+                } else {
+                    ln -s $orig $x
+                }
+            done
+            unset x orig
+        }
+        unset s
+
+        # what we will forward if we start a new ssh connection
+        # NOTE: do this after setting up GNUPGHOME to pick up new socket path;
+        #       if already connected over SSH, extra should be the remote one
+        export _GNUPG_SOCK_SRC=$(gpgconf --list-dirs agent-extra-socket)
+    } else {
+        # required for RemoteForward to not error out if the vars are unset
+        export _GNUPG_SOCK_SRC=/nonexistent
+        export _GNUPG_SOCK_DEST=/nonexistent
+    }
+
+    ## gpg agent
+    # always try to start agent during setup
+    if [[ SHLVL == 1 ]] {
+        gpg-connect-agent /bye >/dev/null 2>&1
+        [[ $? -ne 0 && -o interactive ]] &&
+          print -P "%F{red}!!! Can't communicate with GPG agent%f"
+    }
+    # set up tty if it isn't, and we're interactive or in xorg & not forwarded
+    # do not run more than once per session, even if resetting shell
+    if [[ -v commands[gpg-connect-agent] &&
+          ! -v _sev_gpg_forward && ! -v GPG_TTY &&
+          ( -o interactive || -v DISPLAY ) ]] {
+        export GPG_TTY=$(tty)
+        export PINENTRY_USER_DATA=USE_TTY=$((!${+DISPLAY}))
+        gpg-connect-agent UPDATESTARTUPTTY /bye >/dev/null 2>&1
+    }
+
     ## ssh agents
     # NOTE: preferred order of agents to check: okcagent, gnupg, openssh
     #       first block takes care of okcagent and openssh, second gnupg
@@ -67,7 +181,7 @@ if [[ -v _sev_reset_shell || $SHLVL == 1 ]] {
             IFS=$'\0' read -r sock pid <$agentfile
         }
         if [[ -S $sock && $pid > 0 ]] && kill -0 $pid; then
-            [[ -o interactive ]] && echo "Reusing agent pid $pid"
+            [[ -o interactive ]] && echo "Reusing agent PID $pid"
             export SSH_AUTH_SOCK=$sock
             export SSH_AGENT_PID=$pid
         else
@@ -83,45 +197,25 @@ if [[ -v _sev_reset_shell || $SHLVL == 1 ]] {
             echo -n $SSH_AUTH_SOCK$'\0'$SSH_AGENT_PID >!$agentfile
         fi
         unset okc agentfile sock pid
-    } elif [[ -v commands[gpg] && ! -S $_GNUPG_SOCK_DEST && \
-              ( ! -v SSH_AUTH_SOCK || -v DISPLAY ) ]] {
-        export GPG_TTY=$(tty)
-        export PINENTRY_USER_DATA=USE_TTY=$((!${+DISPLAY}))
-        gpg-connect-agent UPDATESTARTUPTTY /bye >/dev/null 2>&1
-        [[ -o interactive ]] && gpg-connect-agent /subst /serverpid \
-            '/echo GPG agent pid ${get serverpid}' /bye
-        [[ ! -v SSH_AUTH_SOCK ]] && \
-            export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
-    } else {
-        [[ -o interactive ]] && echo "Pre-existing or remote agent"
-    }
-
-    ## gpg ssh forwarding
-    # ssh automatically tunnels SSH_AUTH_SOCK with the right config, but GPG
-    # doesn't—we use a RemoteForward rule in ~/.ssh/config that uses these env
-    # vars to push the gpg extra socket through when connecting via ssh
-    # HACK: this entire thing sucks but there is no other easy way that works
-    #       out of the box with other systems
-    if [[ -v commands[gpgconf] ]] {
-        # if already connected over SSH, reuse forwarded socket for future
-        # connections; else use extra socket
-        sock=${SSH_CLIENT:+agent-socket}
-        export _GNUPG_SOCK_SRC=$(gpgconf --list-dirs ${sock:-agent-extra-socket})
-        unset sock
-        # XXX: multiple SSH sessions to the same host will overwrite this
-        #      socket, no way to send unique paths without configuring explicit
-        #      SendEnv and AcceptEnv exclusions on client and host respectively
-        export _GNUPG_SOCK_DEST=/tmp/.gpg-agent-forward
-        # if socket exists already, we are on a RemoteForwarded client, so copy
-        # it over so that GPG sees it
-        # XXX: race condition if connecting multiple terminals at once
-        if [[ -S $_GNUPG_SOCK_DEST ]] {
-            unlink $_GNUPG_SOCK_SRC >/dev/null 2>&1
-            mv $_GNUPG_SOCK_DEST $_GNUPG_SOCK_SRC >/dev/null
+    } elif [[ ! -v SSH_AUTH_SOCK && -v commands[gpg] ]] {
+        # since gpg agent was started above, we just have to export and notify
+        if [[ -o interactive ]] {
+            if [[ -v _sev_gpg_forwarded ]] {
+                echo 'Remote GPG agent'
+            } else {
+                gpg-connect-agent /subst /serverpid \
+                  '/echo GPG agent PID ${get serverpid}' /bye
+            }
         }
+        export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
+    } elif [[ -v SSH_AUTH_SOCK ]] {
+        [[ -o interactive ]] && echo "Preconfigured agent"
+    } else {
+        [[ -o interactive ]] && print -P "%F{red}No agent available"
     }
 }
 
+
 ### load site-specific
 if [[ -f ~/.zshenv.local ]] { source ~/.zshenv.local }
 
index 0da8429827cef321e1530c35267f884d8444ecaa..7890dad50a8c7deb67f479731babfd4e590a362a 100644 (file)
@@ -184,6 +184,11 @@ function sev_preexec {
     # save last exec time for bell
     # XXX: does not run for blank cmdline
     _sev_exectime=$SECONDS
+    # update gpg forward, to always have unique filename and avoid clashes
+    if [[ -v _GNUPG_SOCK_DEST_EXT ]] {
+        export _GNUPG_SOCK_DEST_EXT=$(date +%s).$RANDOM
+        export _GNUPG_SOCK_DEST=$_GNUPG_SOCK_DEST_BASE.$_GNUPG_SOCK_DEST_EXT
+    }
 }
 add-zsh-hook preexec sev_preexec
 function sev_precmd {
index fc48cdb4ba7ec2159a32ceb87a10b868cad98d74..54852b4b90e6fad054e0882732bb4e906390462e 100755 (executable)
@@ -2,4 +2,5 @@
 gpg-connect-agent <<EOF
 SCD SERIALNO
 LEARN --FORCE
+/bye
 EOF
index dd983eddd65355dcca6464717aa6c78ab2358f01..a883cf586f597cfd6cfc5f9a4fccfd62e7b6271d 100644 (file)
@@ -15,6 +15,7 @@ charset utf-8
 use-agent
 default-key 0x747327ED5BA43ED5
 trusted-key 0x747327ED5BA43ED5
+default-recipient-self
 
 # visual
 no-greeting
index 350df4beb46bc41fb70ad7758aba35f48359693a..1b364322a57b61506a124dd4d8e300824c99d2fb 100644 (file)
@@ -1,4 +1,3 @@
-Host *
-       ForwardAgent    yes
-       ForwardX11      yes
-       RemoteForward   ${_GNUPG_SOCK_DEST} ${_GNUPG_SOCK_SRC}
+ForwardAgent   yes
+ForwardX11     yes
+RemoteForward  ${_GNUPG_SOCK_DEST} ${_GNUPG_SOCK_SRC}
This page took 0.056591 seconds and 4 git commands to generate.