How to make ssh-add read passphrase from a file?
Asked Answered
P

7

50

I am trying to add a key to ssh-agent and want ssh-add to read the password from the key file I'm using. How is this possible?

How do I automate this process from the shell script?

Pastiche answered 23/10, 2012 at 15:19 Comment(4)
Why not just store a copy of the key without any password applied in the same place with the same permissions as you would store the password?Magnetochemistry
See unix.stackexchange.com/questions/90853/… and https://mcmap.net/q/12877/-start-ssh-agent-on-login-closedArri
... and unix.stackexchange.com/a/571744Heim
for macOS, see the solutions here to avoid needing to do this a combination of ssh-add -K or ssh-add --apple-use-keychain ; superuser.com/questions/1127067/…Simplicidentate
O
55

Depending on your distribution and on the version of ssh-add you may be able or not to use the -p option of ssh-add that reads the passphrase from stdin in this way:

cat passfile | ssh-add -p keyfile

If this is not working you can use Expect, a Unix tool to make interactive applications non-interactive. You'll have to install it from your package manager.

I have written a tool for you in expect. Just copy the content in a file named ssh-add-pass and set executable permissions on it (chmod +x ssh-add-pass). You can also copy it to /usr/bin or /usr/local/bin to be accessible from the $PATH search.

#!/bin/bash

if [ $# -ne 2 ] ; then
  echo "Usage: ssh-add-pass keyfile passfile"
  exit 1
fi

eval $(ssh-agent)
pass=$(cat $2)

expect << EOF
  spawn ssh-add $1
  expect "Enter passphrase"
  send "$pass\r"
  expect eof
EOF

The usage is simply: ssh-add-pass keyfile passfile

Omniumgatherum answered 23/10, 2012 at 15:45 Comment(11)
there is no -[Pp] option in ssh-add - error ssh-add: illegal option -- PPastiche
It used to have in some distributions. Which distribution are you using? I'll let the answer so people with a compatible distribution may find it useful.Omniumgatherum
They closed the question, but I still wanted to help. Try the new way I posted and let me know!Omniumgatherum
I have a one more issue related above script how do i add "ssh-agent bash" command in it. problem is it spawn a new shell and it failed.Pastiche
What do you want to do with ssh-agent?Omniumgatherum
without ssh-agent you can't run ssh-add command. check out spaces.seas.harvard.edu/display/USERDOCS/…Pastiche
No, its not working. you have to run "ssh-agent bash" option it won't work without bash argument?Pastiche
Try to change the eval $(ssh-agent) with ssh-agent bash or with eval `ssh-agent -s`Omniumgatherum
I have tried eval ssh-agent -s and it ran all commands but after that script return back to original bash and it lost that key. Its all depend on spawning bash and use same bash to run ssh-addPastiche
I suggest you to start another question because now the question is not how to pass a passphrase to ssh-add (which you know now and is the question asked here), but is how to script ssh-agent + ssh-add. I suggest you to add also your distribution version in the new question. Hope it helps :)Omniumgatherum
let us continue this discussion in chatOmniumgatherum
A
19

Similar to the answer by kenorb, but doesn't save the secret in a file:

$ SSH_ASKPASS=/path/to/ssh_give_pass.sh ssh-add $KEYFILE <<< "$KEYPASS"

where ssh_give_pass.sh is:

#!/bin/bash
# Parameter $1 passed to the script is the prompt text
# READ Secret from STDIN and echo it
read SECRET
echo $SECRET

If you have you secret in a $KEYPASSFILE, read it into a variable first with

KEYPASS=`cat $KEYPASSFILE`

Also make sure that ssh_give_pass.sh is not editable by unauthorized users - it will be easy to log all secrets passed through the script.

Almswoman answered 5/10, 2018 at 18:21 Comment(8)
Very nice! I think this could be improved even more- since the questioner wants this as part of a script, you can go: SSH_ASKPASS="$0", and then haveStressful
This is by far the most elegant- no saving passwords anywhere. Tiny shortening, replace read SECRET ; echo $SECRET with cat Also, since this is for use in a script, the script itself can double as askpass using SSH_ASKPASS=$0, then check $1 to see if is being called normally or as askpassStressful
if DISPLAY isn't set, ssh-add won't respect the SSH_ASKPASS variable. So in case the above solution isn't working, check this one first.Misteach
@Misteach DISPLAY=:0 SSH_ASKPASS=/path/to/ssh_give_pass.sh ssh-add $KEYFILE <<< "$KEYPASS"Politicking
@Misteach If we're using bash, it would be ${DISPLAY:=0}Forked
tried this and not working, still asked for passphraseGuesswork
@Misteach or even better set SSH_ASKPASS_REQUIRE=force insteadOxalis
No, @Jon, the variable is DISPLAY and the desired value is ":0", a colon followed by the number zero. In bash, ${DISPLAY:=0} will CONDITIONALLY set DISPLAY to just "0" (without the important COLON character) if DISPLAY is not already set -- this was not the intent.Langan
S
14

Here is some workaround for systems not supporting -p:

$ PASS="my_passphrase"
$ install -vm700 <(echo "echo $PASS") "$PWD/ps.sh"
$ cat id_rsa | SSH_ASKPASS="$PWD/ps.sh" ssh-add - && rm -v "$PWD/ps.sh"

where ps.sh is basically your script printing your passphrase. See: man ssh-add.

To make it more secure (to not keep it in the same file), use mktemp to generate a random private file, make it executable (chmod) and make sure it prints the passphrase to standard output once executed.

Stans answered 11/10, 2015 at 18:3 Comment(4)
Very creative way to fake-out interactive input in this special case where ssh-add allows you to swap-out the SSH_ASKPASS binary per-call like that.Undercroft
Correct, but ps.sh is a security risk, and a rm is a quite weak way to hide it, you should at least do a shred -u, but even shred is inefficient on modern fs like ext4 and a lot more on btrfs. So create your file in shared memory /dev/shm or /run/user/<uid>, and shred it after use. An other option is to put ps.sh on an encrypted filesystem.Endurant
This solution works great for me, but only if I set DISPLAY=:0 as the man page for ssh-add suggests (Ubuntu 16.04)Anguine
On my Bionic, I had to redirect ssh-add's stdin from /dev/null, as the man page suggested.Windflower
L
13

On my Ubuntu system, none of the answers worked:

  • ssh-add did not support the -p option.
  • ssh-add ignored SSH_ASKPASS, insisting on prompting for the passphrase on the controlling terminal.
  • I wanted to avoid installing additional packages, especially expect.

What worked in my case was:

password_source | SSH_ASKPASS=/bin/cat setsid -w ssh-add keyfile

password_source isn't really a program: it just represents whatever feeds the passphrase to ssh-add. In my case, it is a program that executes setsid and writes the passphrase to its stdin. If you keep your passphrase in a file, you are responsible for making the simple modifications: I will not enable you to hurt yourself.

setsid was already installed, and detaches the controlling terminal so that ssh-add will not try to use it to prompt for the passphrase. -w causes setsid to wait for ssh-add to exit and make its return code available. /bin/cat has the same effect as the script Ray Shannon provided, but uses a standard tool instead of replicating its functionality with a script.

Lymphatic answered 21/12, 2020 at 22:27 Comment(2)
Works perfectly on a relatively clean Debian; in my specific case it was: source | setsid -w ssh-keygen -p -f keyfileMelodramatic
Did you try to also provide SSH_ASKPASS_REQUIRE=force? I had to use that as soon had access to either the tty or DISPLAYParity
S
0

Came here looking for a specific solution, to load ssh-add from pass the unix password manager. Thanks to the great answers here, I put together a script that does this for me:

#!/usr/bin/env bash
# Get password from pass

set -euo pipefail

keyfile=$(echo "$1" | /usr/bin/env sed -n 's/Enter passphrase for \(.*\):\s*$/\1/p');
echo "Extracted key filename $keyfile" >&2
[[ -z "${keyfile}" ]] && exit 1

comment=$(/usr/bin/env ssh-keygen -l -f "$keyfile" | /usr/bin/env awk '{print $3}')
echo "Comment from keyfile $keyfile is $comment" >&2
[[ -z "${comment}" ]] && exit 1

passphrase=$(/usr/bin/env pass show "ssh/$comment")
[[ -z "${passphrase}" ]] && exit 1
echo "got passphrase for comment $comment: XXX" >&2

echo "$passphrase"

Invoked with this:

env SSH_ASKPASS=/usr/local/bin/pass-ssh-askpass.sh SSH_ASKPASS_REQUIRE=force ssh-add

This wouldn't be possible without the answers here, so thanks for that, making this as community wiki for the greater good.

Once my ssh-add supports the -p option, I'll look into a different solution, but until then this will have to do.

Shameless plug: https://github.com/YarekTyshchenko/pass-ssh-askpass

Saleable answered 23/10, 2012 at 15:19 Comment(0)
H
0

With this minimal changes worked for me this bash script of @enrico.basis

#!/bin/bash

if [ $# -ne 2 ] ; then
  echo "Usage: ssh-add-pass passfile keyfile"
  exit 1
fi

eval 'ssh-agent -s'
passwordToFileSSH=$1
pathFileSSH=$2

expect << EOF
  spawn ssh-add $pathFileSSH
  expect "Enter passphrase"
  send "$passwordToFileSSH\r"
  expect eof
EOF

Hensley answered 18/3, 2022 at 15:23 Comment(0)
R
-10

The best way is to generate a key without a passphrase

Rabi answered 17/10, 2022 at 13:52 Comment(1)
If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From ReviewExpressly

© 2022 - 2024 — McMap. All rights reserved.