ssh-keygen accepting stdin - linux

I am trying to call ssh-keygen using a variable through bash as an input instead of a file to get a fingerprint of a public key. I am aware that I could use a temp file to get around this issue, but for reasons out of scope of this question, I do not want to.
This method does not work as it says the key file is invalid (it's correct for sure)
echo $pubkey | ssh-keygen -lf /dev/stdin
This does work, but is not using a variable, rather a file.
ssh-keygen -lf alpha.pub
This does work, but is not using a variable, rather a redirected file.
ssh-keygen -lf /dev/stdin < alpha.pub
This does not work because I get an ambiguous redirect
ssh-keygen -lf /dev/stdin < $(echo $pubkey)
I would appreciate some insight as to how to get ssh-keygen to read from a variable with a public key and if possible, an explanation as to why the redirects aren't doing what I think they should be doing. In specific why the | behaves differently than the < and why the third example is an ambiguous redirect. I searched online but many of the redirect tutorials didn't seem to answer my questions.

echo $pubkey | ssh-keygen -lf /dev/stdin
/dev/stdin is not a public key file.
/dev/stdin is actually a unix pipe, not a regular file, so ssh-keygen fails to open the file
ssh-keygen -lf /dev/stdin <<<$key
1024 92:6a:3f:5c:1f:78:.....
/dev/stdin refers to a regular file, created by using a bash heredoc. You can verify this:
# ls -l /dev/stdin <<<$pubkey
lrwxrwxrwx 1 root root 15 Feb 11 08:07 /dev/stdin -> /proc/self/fd/0
# ls -l /proc/self/fd/0 <<<$pubkey
lr-x------ 1 juergen juergen 64 Apr 14 13:31 /proc/self/fd/0 -> /tmp/sh-thd-1271250023 (deleted)

Since version 7.2 (released on on 2016-02-28), this is now possible by passing - as the file name. From the release notes:
ssh-keygen(1): allow fingerprinting from standard input, e.g. ssh-keygen -lf -

If you want to redirect a string as stdin, use this syntax:
cmd <<< "some $STR here"
If you want to redirect the output of a command as if it was a file, you do it like this:
cmd <( /bin/somecmd )
And if you want to use a command as an OUTPUT file, it's more or less the same:
cmd >( /bin/othercmd )

Here is a one liner using the file /dev/stdin as described in other answers.
$ ssh-keygen -lf /dev/stdin <<< $( ssh-keygen -f ~/.ssh/keyname.pem -y )
2048 14:df:c7:b7:f1:26:7f:87:d5:e7:10:6c:ac:af:a2:03 /dev/stdin (RSA)
Note that this will break with private keys that use a passphrase. It will work with pem files generated by AWS or OpenStack which do not use passphrases.

I would recommend using a temporary file. The issue is that redirecting, BASH expects a file. By using $(echo $pubkey), bash will complain because when it's done with the substitution, it will look for a file of that name that the substitution creates.

Related

Answer "Yes" or "No" ONLY IF a command request it

I a writing a script. There is a command that colud request to answer yes or no to override a certain file.
I want to automate the script to answer YES or NO ONLY IF the command request it (i don't want to echo yes inside the command).
The command I am referring to is ssh-keygen, which requires to override the key in case already exists.
In my mind there is something like this...
if (ssh-keygen requests input) --> Sends yes to the ssh-keygen
In particular, I am using the following command:
ssh-keygen -t rsa -b 4096 -N '' -f ~/.ssh/id_rsa -q
Even if i am using -q, still asks yes or no to override the file.
Thanks in advance
i suggest using the echo command to send "yes" to the command.
echo "yes" | ssh-keygen -t rsa -b 4096 -N '' -f ~/.ssh/id_rsa -q
I would suggest you to check the yes command.
It is sending a loop of y.
If you want to force the acceptation of the command it should work.
But if the keygen request for an input (filename, etc.) it will also send 'y'.
the command would be :
yes | ssh-keygen -t rsa -b 4096 -N '' -f ~/.ssh/id_rsa -q

Get file count at remote location during FTP in shell script on linux server

Requirement : Need to get file count based on wildcard entry present on remote location(Linux server) and store it in variable for validation purpose
Tried the below code
export ExpectedFileCount=$(ftp -inv $FTPSERVER >> $FTPLOGFILE <<END_SCRIPT
user $FTP_USER $FTP_PASSWORD
passive
cd $PATH
ls -ltr ${WILDCARD}*xml| wc -l | sed 's/ *//g'
quit
END_SCRIPT)
But the code is storing the code snippet in the variable and and executing the commands every time I call the variable.
Please suggest the changes in the script to execute the script once and store the value in the variable
This seems to work (on Ubuntu, no promises about portability):
export ExpectedFileCount=`ftp -in $FTPSERVER << END_SCRIPT | tee -a $FTPLOGFILE | egrep -c '\.xml$'
user $FTP_USER $FTP_PASSWORD
passive
cd $REMOTE_PATH
ls -l
quit
END_SCRIPT`
Issues:
$REMOTE_PATH used in place of $PATH for remote directory (as $PATH has a special meaning)
only a simple ls -l is performed inside the ftp session, and the output parsed locally, as it does not support arbitrary shell commands
I can't see how to capture the output of a command with a heredoc using $(...), but it seems to work with backticks if the closing backtick is after the final delimiter

Linux terminal: pass the answer(or arg values) in advance when installing package?

When I run a command like ssh-keygen -t rsa -b 2048, it asks me:
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
I'd like to write the path in advance and tried like this: ssh-keygen -t rsa -b 2048 | cat '/root/.ssh/id_rsa.pub'.
But this doesn't work....
How can I do this?
Your attempt was in reverse, meaning you were trying to pass the output of ssh-keygen to cat. But we need to pass the output of cat to ssh-keygen:
# using multiline here doc
$ cat <<EOF | ssh-keygen -t rsa -b 2048
/tmp/id_rsa
/tmp/id_rsa.pub
password
EOF
OR
$ echo -e "/tmp/id_rsa\n/tmp/id_rsa.pub" | ssh-keygen -t rsa -b 2048
But use it with caution, this just feeds the entire input to the piped command. Depending on how that command processes input this might fail if the expected input is different from what is being piped in.
It may be fine for simple scripts/commands (will NOT work for ssh-keygen reliably in all cases [because it seems to spawn another process in some cases to ask for the passphrase]).
ref. https://tldp.org/LDP/abs/html/here-docs.html for heredoc

"stdin: is not a tty" from cronjob

I'm getting the following mail every time I execute a specific cronjob. The called script runs fine when I'm calling it directly and even from cron. So the message I get is not an actual error, since the script does exactly what it is supposed to do.
Here is the cron.d entry:
* * * * * root /bin/bash -l -c "/opt/get.sh > /tmp/file"
and the get.sh script itself:
#!/bin/sh
#group and url
groups="foo"
url="https://somehost.test/get.php?groups=${groups}"
# encryption
pass='bar'
method='aes-256-xts'
pass=$(echo -n $pass | xxd -ps | sed 's/[[:xdigit:]]\{2\}/&/g')
encrypted=$(wget -qO- ${url})
decoded=$(echo -n $encrypted | awk -F '#' '{print $1}')
iv=$(echo $encrypted | awk -F '#' '{print $2}' |base64 --decode | xxd -ps | sed 's/[[:xdigit:]]\{2\}/&/g')
# base64 decode input and save to file
output=$(echo -n $decoded | base64 --decode | openssl enc -${method} -d -nosalt -nopad -K ${pass} -iv ${iv})
if [ ! -z "${output}" ]; then
echo "${output}"
else
echo "Error while getting information"
fi
When I'm not using the bash -l syntax the script hangs during the wget process. So my guess would be that it has something to do with wget and putting the output to stdout. But I have no idea how to fix it.
You actually have two questions here.
Why it prints stdin: is not a tty?
This warning message is printed by bash -l. The -l (--login) options asks bash to start the login shell, e.g. the one which is usually started when you enter your password. In this case bash expects its stdin to be a real terminal (e.g. the isatty(0) call should return 1), and it's not true if it is run by cron—hence this warning.
Another easy way to reproduce this warning, and the very common one, is to run this command via ssh:
$ ssh user#example.com 'bash -l -c "echo test"'
Password:
stdin: is not a tty
test
It happens because ssh does not allocate a terminal when called with a command as a parameter (one should use -t option for ssh to force the terminal allocation in this case).
Why it did not work without -l?
As correctly stated by #Cyrus in the comments, the list of files which bash loads on start depends on the type of the session. E.g. for login shells it will load /etc/profile, ~/.bash_profile, ~/.bash_login, and ~/.profile (see INVOCATION in manual bash(1)), while for non-login shells it will only load ~/.bashrc. It seems you defined your http_proxy variable only in one of the files loaded for login shells, but not in ~/.bashrc. You moved it to ~/.wgetrc and it's correct, but you could also define it in ~/.bashrc and it would have worked.
in your .profile, change
mesg n
to
if `tty -s`; then
mesg n
fi
I ended up putting the proxy configuration in the wgetrc. There is now no need to execute the script on a login shell anymore.
This is not a real answer to the actual problem, but it solved mine.
If you run into this problem check if you are getting all the environment variables set as you expect. Thanks to Cyrus for putting me to the right direction.

Shell script to compare remote directories

I have a shell script that I am using to compare directory contents. The script has to ssh to different servers to get a directory listing. When I run the script below, I am getting the contents of the server that I am logged into's /tmp directory listing and not that of the servers I am trying to ssh to. Could you please tell me what I am doing wrong?
The config file used in the script is as follows (called config.txt):
server1,server2,/tmp
The script is as follows
#!/bin/sh
CONFIGFILE="config.txt"
IFS=","
while read a b c
do
SERVER1=$a
SERVER2=$b
COMPDIR=$c
`ssh user#$SERVER1 'ls -l $COMPDIR'`| sed -n '1!p' >> server1.txt
`ssh user#$SERVER2 'ls -l $COMPDIR'`| sed -n '1!p' >> server2.txt
done < $CONFIGFILE
When I look at the outputs of server1.txt and server2.txt, they are both exactly the same - having the contents of /tmp of the server the script is running on (not server1 or 2). Doing the ssh +dir listing on command line works just fine. I am also getting the error "Pseudo-terminal will not be allocated because stdin is not a terminal". Adding the -t -t to the ssh command isnt helping either
Thank you
I have the back ticks in order to execute the command.
Backticks are not needed to execute a command - they are used to expand the standard output of the command into the command line. Certainly you don't want the output of your ssh commands to be interpreted as commands. Thus, it should work fine without the backticks:
ssh user#$SERVER1 "ls -l $COMPDIR" | sed -n '1!p' >>server1.txt
ssh user#$SERVER2 "ls -l $COMPDIR" | sed -n '1!p' >>server2.txt
(provided that double quotes to allow expansion of $COMPDIR are used).
first you need to generate keys to login to remote without keys
ssh-keygen -t rsa
ssh-copy-id -i ~/.ssh/id_rsa.pub remote-host
then try to ssh without pass
ssh remote-host
then try to invoke in your script but first make sanity check
var1=$(ssh remote-host) die "Cannot connect to remote host" unless $var1;

Resources