-
-
Save kraftb/9918106 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
BITS=2048 | |
# In one line: | |
# rm -f temp.key && ssh-keygen -t rsa -b 2048 -f temp.key -N "" -q && ssh-keygen -e -f temp.key -m PKCS8 | tr "\n" " " && echo && cat temp.key | tr "\n" " " && echo | |
# In multiple lines: | |
rm -f temp.key | |
ssh-keygen -t rsa -b $BITS -f temp.key -N "" -q | |
echo | |
ssh-keygen -e -f temp.key -m PKCS8 | tr "\n" " " | |
echo | |
echo | |
cat temp.key | tr "\n" " " | |
echo | |
echo | |
Hey, since this is the first page in google when trying to find how to generate ssh keys to stdout, I figured I share my solution:
mkfifo key key.pub && cat key key.pub & echo "y" | ssh-keygen -f key ; rm key key.pub
Explanation:
mkfifo key key.pub
creates two named pipes calledkey
andkey.pub
cat key key.pub
will read whatever is written to these pipes and print it on the screen.&
puts it to background so it runs while the next commands runs:echo "y" | ssh-keygen -f key
- this will execute ssh-keygen with output file calledkey
and ssh-keygen will automatically output the public key tokey.pub
. This is why we prepared those named pipes. The only thing is that ssh-keygen will noticekey
already exists, that's why we haveecho "y" |
in front of it to send "y" and ENTER to standard input.rm key key.pub
- clean up the pipes
This way the secret never touches the disk, it's all in-memory. The final rm
just cleans up the pipes, if you omitted it, you'd end up with named pipes but the data won't be there any way.
$ echo /tmp/stderr{,.pub} | xargs -n 1 ln -sf /dev/stderr && yes | ssh-keygen -N '' -qf /tmp/stderr > /dev/null
Here's an improvement to @mprasil's answer:
mkfifo key && ((cat key ; rm key)&) && (echo y | ssh-keygen -N '' -q -f key > /dev/null)
If you need a base64 encoded key (Ex: for a kubernetes secret) you can use it like this:
(mkfifo key && ((cat key ; rm key)&) && (echo y | ssh-keygen -N '' -q -f key > /dev/null)) | base64 -w0
Drawback: this approach probably only works in bash.
Improvements:
- If mkfifo fails (file already exists) then the ssh-keygen step won't be run (overwritting key)
- The only thing that comes out of stdout is key data suitable for being piped to another process.
- key.pub is written to a file since it's not sensitive.
Todo:
- Check if key.pub already exists in the beginning.
My contribution here: three ways to do this from Python:
cockpit-project/bots#2192 (comment)
The "pure shell" version distilled from the above goes something like this, depending on if you want the private key, the public key, or a combination of the two of them. Note that we use only fd3 in this version since there's no way to capture the variables separately in shell.
ln -sfT /proc/self/fd/3 fd3
ln -sfT /proc/self/fd/3 fd3.pub
BOTH_KEYS="$(echo y | ssh-keygen -t ed25519 -N "" -q -f fd3 3>&1 > /dev/null)"
echo "${BOTH_KEYS}"
If you don't want the public key, redirect that part to /dev/null
instead. You could also just let the public key hit the disk.
Note that this only works with a variable capture: if you try to run the command directly at the terminal it will fail. That's because:
openat(AT_FDCWD, "fd3", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 3
which will work if fd 3 is a pipe, but not if it's a terminal. If you want to inspect the output of the command manually, do it like this:
echo y | ssh-keygen -t ed25519 -N "" -q -f fd3 3>&1 > /dev/null | cat
For those looking for a simple way to get just the private key:
$ ( exec 3>&1 ; ssh-keygen -qt ed25519 -N "" -f /proc/self/fd/3 <<<y >/dev/null 2>&1 )
Unfortunately, ssh-keygen
writes to stdout and stderr. Work around this by creating fd 3.
Here is how you can output just the public key and append this output to a remote host's authorized_keys file, with a single command line
(mkfifo key key.pub && ((cat key > /dev/null ; cat key.pub ; rm key key.pub )&) && (echo y | ssh-keygen -N '' -q -f key > /dev/null)) | ssh -o "StrictHostKeyChecking no" [email protected] "cat >> ~/.ssh/authorized_keys"
At which point I realized I actually need that private key for auto login and it needed to my in my local host's ~/.ssh/id_rsa
Also you need that id_rsa to only be readable by the owner or else it will be ignored by ssh
This one line command will let your create private keys and push them to any remote host
(mkfifo key key.pub && ((cat key > ~/.ssh/id_rsa ; chmod go-rwx ~/.ssh/id_rsa ; cat key.pub ; rm key key.pub )&) && (echo y | ssh-keygen -N '' -q -f key > /dev/null)) | ssh -o "StrictHostKeyChecking no" [email protected] "cat >> ~/.ssh/authorized_keys"
But now I realize, the entire reason why I needed to output the public key to console is gone, I was trying to have useless keyfiles scattered about but I need those files....
So the command becomes
This will create keys and push them to a remote host in a single command !
(mkfifo key key.pub && ((cat key > ~/.ssh/id_rsa ; chmod go-rwx ~/.ssh/id_rsa ; cat key.pub | tee ~/.ssh/id_rsa.pub ; rm key key.pub )&) && (echo y | ssh-keygen -N '' -q -f key > /dev/null)) | ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -o "PubkeyAuthentication=no" [email protected] "cat >> ~/.ssh/authorized_keys"
At which point, I realize I could just ask ssh-keygen to output key files in the right place ..
And there's a command called ssh-copy-id for doing exactly what I'm doing
So, here's another, final, version of that
This one liner will create public/private key pair in the .ssh default folder only if you don't already have them and push them to your remote host
([ -f ~/.ssh/id_rsa.pub ] || ssh-keygen -N '' -q -f ~/.ssh/id_rsa) ;ssh-copy-id [email protected]
@allisonkarlitskaya Thanks for your contribution. Note that much of the trickery here, that is, all solution other than using openssl
or your first Python solution, will fail if you work on a read-only filesystem.
Here's a practical example of this for encrypting the ssh key with sops and age in a taskfile:
version: "3"
gen-key:
desc: Generate encrypted ssh key
silent: true
status:
- test -f ssh.sops.key
cmds:
- mkfifo key key.pub
- defer: rm key key.pub
- cat key | sops -e /dev/stdin > ssh.sops.key &
- 'printf "Your public key:\n$(cat key.pub)\n" &'
- yes | ssh-keygen -t ed25519 -f key > /dev/null
What's the point if you leave your secret material on the disk unprotected? The whole idea is to actually avoid writing it to disk. So far, I found that you can achieve that using
openssl
, e.g. (if your OpenSSL is fresh enough to support curves) the following will generate the Ed25519 private key:openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:X25519 -pkeyopt ec_param_enc:named_curve
on standard output with no files involved.