The following takes place on Ubuntu 18.

I need to automate the remote seeding of a gpg-agent with a passphrase stored in a file on my local machine using SSH.

The passphrase needs to be handled in such a way so that it cannot be easily viewed by other nosey users - principally on the remote server, but also it should be secure on the local box too.

The problem is a general one, rather than being GPG specific - how do I pass a string securely into a programming running on a remote server using ssh?

I know that putting the password on the command line means is visible using ps -ef on both local (ssh) and remote machines (bash -c). So I must avoid this.

I also know that storing the password in environment variables means a process' initial env can be see as cat /proc/<pid>/environ and gdb can be used to look at current state. So details can be seen on the local machine or the remote machine if local environment variables are forwarded. So this is not ideal, especially as I can't control tightly other people's access to the SSH user account on the remote machine.

After a bit of reading and experimentation I've come-up with the below - it certainly works! You can assume seed.txt is chmod 400 so short of someone having root access on the local server, the file is secure (or at least as secure as my SSH keys).

Note - seed.txt does not exist on the remote server, only on the local server, so it's contents must be sent to the remote server.

SSH connection is using keys so no password is need for that.

ssh -T my-server <<EOSSH > /dev/null
printf '%s\n' "$(cat seed.txt)" | /usr/lib/gnupg2/gpg-preset-passphrase -c 123456789

Using ps -ef on the local machine will only yield ssh -T my-server as the actual commands to be remotely executed are piped into stdin, rather than using the ssh command line.

Likewise the cat command line displays only the filename. printf, I believe, is a built-in command so will not show-up in ps. I believe the printf is being executed on the remote machine?

On the remote server ps will only show /usr/lib/gnupg2/gpg-preset-passphrase -c 123456789 as the passphrase is piped in again using stdin.

I'm making no direct use of environment variables so there shouldn't be any issue here. I'm a little anxious that something will be exposed from the SSH environment variables?

My question is - is my method sensible/simple/safe (assuming root access is not compromised), are there any obvious issues, and if yes, what are the suggested workarounds?

The only other options I've managed to get working is to use expect but given both ssh and gpg-preset-passphrase seem to be happy to accept input as stdin rather than interactively, it seems overkill to use expect?


Some really helpful answers below, thanks! The more I think about this, I'd reframe the question ever so slightly - Perhaps I have to accept that ring-fencing stuff inside a single Linux account from other users of that account is always unachievable if the other users are determined? That seems usually the right logical premise, but if I want to spin-up a (per account) gpg-agent for other users (of that account) without them having access to the credentials to do the actual spinning-up themselves (gpg-agent is always per user I think?), I have to do that from within the account itself, right? How do I do that without users of the same account being able to peak at what I'm doing... the short answer seems to be - you can make it hard for them to peak, but probably never impossible? And as far as I can see seeding a gpg-agent from a different account for use of users of another account isn't possible?

  • 1
    why the complications of printf and cat instead of just gpg-preset-passphrase ... < seed.txt ?
    – thrig
    Jan 26, 2019 at 3:12
  • Thanks for reply - seed.txt is on the local machine, and the ($cat seed.txt) will be executed locally and the contents of seed.txt sent to the remote server. I think using < seed.txt means seed.txt would have to exist on the remote machine - which is not the case. A quick test gives seed.txt: No such file or directory if I make this change.
    – Phil
    Jan 26, 2019 at 3:19
  • I think that ssh < seed.txt my-server /usr/lib/gnupg2/gpg-preset-passphrase -c 123456789 will do just the same (-T isn't needed if ssh is invoked with a command argument).
    – user313992
    Jan 26, 2019 at 4:00
  • Yes @mosvy - I can't up vote you yet, but I've tested your suggestion it behaves identically and is neater and safer - thanks!
    – Phil
    Jan 26, 2019 at 9:34
  • In bash, $(< seed.txt) is a builtin way to read the file, so you don't even need to call out to cat. Jan 26, 2019 at 14:03

1 Answer 1


printf may not be a builtin depending on the sh on the remote system. bash and dash on Linux run it as a builtin (echo 'printf --version' | ssh host), sh on OpenBSD does not. A greater concern is that "I can't control tightly other people's access to the SSH user account on the remote machine"; those users (with sufficient skill) could do naughty things without root access:

  • if there is and they know your public key they can run something else via a command="..." entry in a ~/.ssh/authorized_keys file
  • bash does read shell rc files even with the printf-and-cat-free ssh host < seed.txt command form so they could export LD_PRELOAD=/something/naughty.so and that library might duplicate standard input elsewhere or do interesting things when something tries to execute gpg-preset-passphrase
  • Thanks - so first bullet: is this still a primary concern if bad users already have interactive login rights to the remote account. I think I follow that by hijacking authorized_keys they could cause ssh to run something very specific next time I connect; but if they already have interactive login access they can connect anyway? Second bullet - so without replacing the executables (requiring root) a bad user can cherry pick a library dependency, edit the source to spit out stdin silently whilst continuing to perform the intended function - and use .profile to export it LD_PRELOAD?
    – Phil
    Jan 26, 2019 at 8:52
  • Following up - I get bullet one now. The point is you can syphon-off stdin here too - for specific cases when you know I am sending the password using stdin. A crude (but tested as working) example is to prepend this to the relevant key: command="echo your stdin is : $(</dev/stdin)". I don't believe using expect would make any difference here - the password is still coming down the pipe as stdin? I wonder is there a way of disabling the command syntax in key files or can I chown/chmod the authorized_key files in some way so the owner only has read access to his own authorized_keys?
    – Phil
    Jan 26, 2019 at 9:51
  • you would need both SSH configuration to disable any ~/.ssh/authorized_keys tricks (and maybe also ~/.ssh/rc) and also to use a shell that does not read any rc files under $HOME at all to help avoid direct attacks. there could be other attacks if they steal the GPG files and then try brute forcing the password, etc
    – thrig
    Jan 26, 2019 at 15:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .