121

I am in the process of creating a bash script that would log into the remote machines and create private and public keys.

My problem is that the remote machines are not very reliable, and they are not always up. I need a bash script that would check if the SSH connection is up. Before actually creating the keys for future use.

Hubert Kario
  • 21,314
  • 3
  • 24
  • 44
chutsu
  • 13,612
  • 19
  • 65
  • 86
  • 3
    Typically, one runs `ssh-keygen` to generate a keypair on the local machine, then `ssh-copy-id` to copy the public key to remote machines. It seems that you are doing things differently. Why, what is your goal? – ephemient Sep 10 '09 at 19:43
  • 1
    Since you’re obviously changing how the remote machines establish connections, consider deploying *mosh*. http://mosh.mit.edu/ It is intended to supplement SSH on unstable connections. I have very good experiences with it. – Daniel Aug 13 '13 at 11:08
  • @ephemient I know it's a bit late, but it seems pretty straightforward that they key was *not* for the local machine or *not* for the local user. – Abandoned Cart Apr 27 '19 at 13:10

14 Answers14

219

You can check this with the return-value ssh gives you:

$ ssh -q user@downhost exit
$ echo $?
255

$ ssh -q user@uphost exit
$ echo $?
0

EDIT: Another approach would be to use nmap (you won't need to have keys or login-stuff):

$ a=`nmap uphost -PN -p ssh | grep open`
$ b=`nmap downhost -PN -p ssh | grep open`

$ echo $a
22/tcp open ssh
$ echo $b
(empty string)

But you'll have to grep the message (nmap does not use the return-value to show if a port was filtered, closed or open).

EDIT2:

If you're interested in the actual state of the ssh-port, you can substitute grep open with egrep 'open|closed|filtered':

$ nmap host -PN -p ssh | egrep 'open|closed|filtered'

Just to be complete.

  • 2
    To be complete, can u indicate which return code means success and which means failure to SSH? – Henley Sep 03 '15 at 13:35
  • Wondering what if the SSH attempt just hangs there? – Sibbs Gambling May 07 '17 at 02:45
  • 2
    Great answer! However, you do not mention that attempting to `ssh` into a down host fails only after a timeout of e.g. 60 seconds - which might be prohibitive for some usages. Also, if a hostname if defined in `~/.ssh/config`, the first `ssh` approach works while the second `nmap` way fails with `Failed to resolve ""`. – ssc May 17 '17 at 14:19
  • 3
    no explanation at all about the commands... or what you are actually doing.. what is `$?` ? etc – Toskan Jan 18 '18 at 21:19
  • An even more concise form of #1: `ssh -q user@downhost exit | echo $?` pipe the connection result into echo – philn5d Nov 30 '18 at 21:22
  • 1
    @ssc you can add the -o option to limit the timeout duration. The following example reduces it to 5 seconds: `ssh -o ConnectTimeout=5 -q user@downhost exit` – grim_i_am Mar 31 '20 at 09:41
  • Can `nmap` also check if user exists locally and is alowed ssh connection? – Eduardo Pignatelli Nov 16 '20 at 15:49
  • You can also use the `-B` option of grep to do multiple scans and show the scanned address `nmap 192.168.0.0-255 -PN -p ssh | grep -B 4 -i open` – Eduardo Pignatelli Nov 16 '20 at 15:57
  • How do I check a port with this? For example 192.168.0.0 -p 33001? – Matthaeus Gaius Caesar Jun 18 '21 at 16:45
  • The ssh solution is not great. Requires password. We dont want to need a password to simply check a connection. – Matthaeus Gaius Caesar Jun 23 '21 at 16:40
33

You can use something like this

$(ssh -o BatchMode=yes -o ConnectTimeout=5 user@host echo ok 2>&1)

This will output "ok" if ssh connection is ok

Adrià Cidre
  • 392
  • 3
  • 3
23
ssh -q -o "BatchMode=yes" -i /home/sicmapp/.ssh/id_rsa <ID>@<Servername>.<domain> "echo 2>&1" && echo $host SSH_OK || echo $host SSH_NOK
Littm
  • 4,923
  • 4
  • 30
  • 38
Brian Ott
  • 231
  • 2
  • 2
  • 2
    One line output: (ssh -q -o "BatchMode=yes" -o "ConnectTimeout=3" user@host.com "echo 2>&1" && echo SSH_OK || echo SSH_NOK) | tail -n1 – Xdg Jul 01 '14 at 17:36
22

Complementing the response of @Adrià Cidre you can do:

status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 user@host echo ok 2>&1)

if [[ $status == ok ]] ; then
  echo auth ok, do something
elif [[ $status == "Permission denied"* ]] ; then
  echo no_auth
else
  echo other_error
fi
iarroyo
  • 2,354
  • 24
  • 23
10

Below ssh command should have an exit code of 0 on a successful connection and a non-zero value otherwise.

ssh -q -o BatchMode=yes user@remote.com exit

if [ $? != "0" ]; then
    echo "Connection failed"
fi
matt
  • 4,614
  • 1
  • 29
  • 32
  • 2
    Great! I recomment also adding -o ConnectTimeout=5, for faster exit in cases where the destination port is filtered. – Daniel Jul 30 '20 at 07:15
7

Try:

echo quit | telnet IP 22 2>/dev/null | grep Connected
Community
  • 1
  • 1
GUESSWHOz
  • 81
  • 1
  • 1
  • 1
    A problem of this approach is it does not recognize the hosts defined in ssh_config (i.e /etc/ssh/config or ~/.ssh/config) – Ding-Yi Chen Mar 08 '13 at 07:16
4

Following @user156676, to check a range of ips:

#!/bin/sh
IP='192.168.0.'
PWD='your_password'
USR='your_usr'

for i in $(seq 229 255);do
    sshpass -p $PWD ssh -q -o ConnectTimeout=3 ${USR}@${IP}${i} exit
    let ret=$?
    if [ $ret -eq 5 ]; then
        echo $IP$i "Refused!"  $ret
    elif [ $ret -eq 255 ] ; then
        echo $IP$i "Server Down!" $ret
    elif [ $ret -eq 0 ] ; then
        echo $IP$i "Connnected!" $ret
    else
        echo $IP$i "Unknown return code!" $ret
    fi  
done
MeadowMuffins
  • 507
  • 1
  • 5
  • 20
1

Just in case someone only wishes to check if port 22 is open on a remote machine, this simple netcat command is useful. I used it because nmap and telnet were not available for me. Moreover, my ssh configuration uses keyboard password auth.

It is a variant of the solution proposed by GUESSWHOz.

nc -q 0 -w 1 "${remote_ip}" 22 < /dev/null &> /dev/null && echo "Port is reachable" || echo "Port is unreachable"

-q 0 means leave as soon as idle.

-w 1 wait for connection for 1second.

gcb
  • 13,901
  • 7
  • 67
  • 92
Mathieu C.
  • 779
  • 5
  • 10
1

To connect to a server with multiple interfaces

ssh -o ConnectTimeout=1 -q Necktwi@192.168.1.61;[ $? = 1 ] || ssh -o ConnectTimeout=1 -q Necktwi@192.168.1.51
Necktwi
  • 2,483
  • 7
  • 39
  • 62
0

If you would like to check a remote folder exists, or any other file-test really:

if [ -n "$(ssh "${user}@${server}" [ -d "$folder" ] && echo 1; exit)" ]; then
    # exists
else
    # doesn't exist
fi

Do not forget the quotes in "$(ssh ...)".

Jonathan H
  • 7,591
  • 5
  • 47
  • 80
  • This does not answer the question. The OP wants to check whether SSH connection can be established not checking a file in a remote ssh location. – Rakib Fiha Aug 23 '19 at 08:02
0

Example Using BASH 4+ script:

# -- ip/host and res which is result of nmap (note must have nmap installed)
ip="192.168.0.1"
res=$(nmap ${ip} -PN -p ssh | grep open)

# -- if result contains open, we can reach ssh else assume failure) --
if [[ "${res}" =~ "open" ]] ;then
    echo "It's Open! Let's SSH to it.."
else
    echo "The host ${ip} is not accessible!"
fi
Mike Q
  • 6,716
  • 5
  • 55
  • 62
0

https://onpyth.blogspot.com/2019/08/check-ping-connectivity-to-multiple-host.html

Above link is to create Python script for checking connectivity. You can use similar method and use:

ping -w 1 -c 1 "IP Address" 

Command to create bash script.

4b0
  • 21,981
  • 30
  • 95
  • 142
  • 2
    This will only ping the remote IP. Does not guarantee if ssh connection is possible. – R J Sep 08 '19 at 09:15
  • Thanks for pointing that out, here's the code for ssh https://xlinu.blogspot.com/2019/09/check-for-ssh-connectivity-to-multiple.html export user="username" export pass="password" export i="hostname" export SSHPASS=$pass sshpass -e ssh $user@$i -q "echo $i is Accessible" – Dheeraj Kumar Sep 13 '19 at 03:34
0

I wrote this script to check both netcat and SSH connectivity to all hosts in my servers /etc/hosts

reads /etc/hosts line by line and then tries netcat port 22, and then ssh as "sshuttle" user

quick way to check network sanity

script uses a "sshuttle" user, this is an account that has pub/priv keys on all my hosts and can ssh anywhere (non root account), we use this acct to spin up sshuttle VPN tunnels, but you can add any account that has SSH access to servers

https://gist.github.com/perfecto25/8687d563716ba4923c77162be724beda

output,

./conncheck.sh


netcat is installed, proceeding..
--------------------------------------
tm-us1 (127.0.0.1): ssh OK | nc OK
--------------------------------------
localhost (127.0.0.1): ssh OK | nc OK
--------------------------------------
atlas (192.168.142.21): ssh ERROR | nc OK
--------------------------------------
hydra (192.168.142.22): ssh OK | nc OK
--------------------------------------
nemesis (192.168.140.23): ssh OK | nc OK
--------------------------------------
vulcan (192.168.140.24): ssh OK | nc OK
--------------------------------------
athena (192.168.140.27): ssh OK | nc OK
--------------------------------------
nas1 (192.168.100.101): ssh ERROR | nc OK
--------------------------------------
tm-dev (192.10.23.71): ssh ERROR | nc ERROR
--------------------------------------
WARNING: Your password has expired.
Password change required but no TTY available.
infra01 (192.10.23.186): ssh ERROR | nc OK
--------------------------------------
ns-us1 (192.10.23.252): ssh ERROR | nc OK
--------------------------------------
ns-us2 (192.10.23.182): ssh ERROR | nc OK
--------------------------------------
proxy-us1 (192.10.23.120): ssh OK | nc OK
--------------------------------------
simtm-us1 (192.10.23.236): ssh OK | nc OK
--------------------------------------
tm-us1 (192.10.23.104): ssh OK | nc OK
--------------------------------------
tm-us2 (192.10.23.215): ssh OK | nc OK
--------------------------------------
tm-dev (192.10.23.77): ssh OK | nc OK
--------------------------------------
WARNING: Your password has expired.
Password change required but no TTY available.
tm-uat (192.10.23.225): ssh ERROR | nc OK
--------------------------------------
vpn-us1 (192.10.23.193): ssh OK | nc OK
--------------------------------------
perfecto25
  • 772
  • 9
  • 13
-7

I feel like you're trying to solve the wrong problem here. Shouldn't you be trying to make the ssh daemons more stable? Try running something like monit, which will check to see if the daemon is running and restart it if it isn't (giving you time to find the root problem behind sshd shutting down on you). Or is the network service troublesome? Try looking at man ifup. Does the Whole Damn Thing just like to shut down on you? Well, that's a bigger problem ... try looking at your logs (start with syslog) to find hardware failures or services that are shutting your boxen down (maybe a temperature monitor?).

Making your scripts fault tolerant is great, but you might also want to make your boxen fault tolerant.

Sam Bisbee
  • 4,461
  • 20
  • 25
  • 4
    Sam: there are valid use cases to have the script checking it. E.g (like me): I have a cron job running on my machine to backup my data via rsync to my home nas. Now I'm outside or even disconnected quite often and need to reschedule if the connection wasn't available. My boxen runs quite well, but as the saying goes: it is always the cable (a.k.a network) – stwissel May 22 '12 at 16:15