Checking if an SSH tunnel is up and running - linux

I have a perl script which, when destilled a bit, looks like this:
my $randport = int(10000 + rand(1000)); # Random port as other scripts like this run at the same time
my $localip = '192.168.100.' . ($port - 4000); # Don't ask... backwards compatibility
system("ssh -NL $randport:$localip:23 root\#$ip -o ConnectTimeout=60 -i somekey &"); # create the tunnel in the background
sleep 10; # Give the tunnel some time to come up
# Create the telnet object
my $telnet = new Net::Telnet(
Timeout => 10,
Host => 'localhost',
Port => $randport,
Telnetmode => 0,
Errmode => \&fail,
);
# SNIPPED... a bunch of parsing data from $telnet
The thing is that the target $ip is on a link with very unpredictable bandwidth, so the tunnel might come up right away, it might take a while, it might not come up at all. So a sleep is necessary to give the tunnel some time to get up and running.
So the question is: How can i test if the tunnel is up and running? 10 seconds is a really undesirable delay if the tunnel comes up straight away. Ideally, i would like to check if it's up and continue with creating the telnet object once it is, to a maximum of, say, 30 seconds.
Edit: Ping doesn't help me mouch, as the remote end of the tunnel is generally up, but with a very high amount of packetloss
Solved: Extrapolating from the tip suggested by mikebabcock, sleep 10 has been replaced with this block which works like a charm:
my $starttime = time();
while (1)
{
# Check for success
if (system("nc -dzw10 localhost $randport > /dev/null") == 0) { last }
# Check for timeout
if (time() > $starttime + 30) { &fail() }
# 250ms delay before recheck
select (undef, undef, undef, 0.25);
}

Use netcat -- often nc on Linux systems:
nc -dvzw10 ${HOSTNAME} 23
Works for me, with a response like:
Connection to ${HOSTNAME} 23 port [tcp/telnet] succeeded!
It also returns 0 on success, and is happy with a simple connection after which it goes away.
-d means not to read anything from the keyboard side
-v means to be verbose (turn this off in a script)
-z means to disconnect after making the connection
-w10 means to wait up to 10 seconds, otherwise give up

You can integrate a ping to your ssh server and if it works fine the ssh tunnel is up
# only a ping sample :-D
if ! ping -c 1 192.168.101.9; then
echo "ping does not succeed"
else
echo "ping succeed"
fi

I think fping might be better then the usual ping, more script friendly.
fping -t 60000 [your server]
should try to connect to the server 60seconds before giving up
Something like
if(fping -t 60000 [your server]) {
execute desired code;
} else {
execute this script again to rerun;;
}
I think you get the idea even if the coding isn't real.

Related

Script to check connection every 5 minutes and write result to file (without ping) in LINUX

I need to check my connection to a spesific port every 5 minutes, currently i can't use ping command, so i need other alternative to do this.I want to execute this command in shell script
Can someone help me to show some example for this case?
port=80
ip=8.8.8.8
checkIntervalSecs=5
timeoutSecs=1
while true ; do
if $(nc -z -v -w$timeoutSecs $ip $port &>/dev/null); then
echo "Server is up!"
else
echo "Server is down!"
fi
sleep $checkIntervalSecs
done
This runs until you kill it. For an explanation of the nc command, it is basically taken from SO question #IporSircer suggested.

Bash script - How to run ssh after another one is connected

I don't have a powerful hardware so I can't run multiple ssh tunnels at the same time or it'll make the CPU load go way too high, my goal is to run a ssh tunnel after another one is connected, and reconnect if one of my ssh gets disconnected, so basically it's like this:
while true; do
if (1st ssh isn't connected); then
connect the first ssh
elif (1st ssh is finally connected); then
run the second ssh
elif (2nd ssh is finally connected); then
run the 3rd ssh
fi
sleep 1
done
The problem is that the amount of ssh tunnels keeps changing, sometimes a user wants to run 3 ssh tunnels and sometimes 5, it looks like this to run the script:
mytunnel.sh -a [number of tunnels they wanna run]
I'm thinking of for loop but I just can't figure out how to write it inside a for loop. Please help me.
Here is a for loop you can use:
#!/usr/local/bin/bash
LOOP=$1
for (( c=1; c<=$LOOP; c++ ))
do
echo "$c "
done
Replace echo with your commands and LOOP with whatever command-line arg you'll be using. This example reads command-line arg 1 (i.e. $1).
Example execution:
Tricky. Unfortunately I don't think ssh returns anything when it connects a tunnel, nor does it exit immediately when the connection is broken.
Instead what you probably want to do is make a port monitor that periodically checks that the port is accepting connections and spawns a new ssh tunnel (possibly killing the old ssh process) if it isn't.

Telnet to login with username and password to mail Server

I am having some issues trying to connect through telnet to a mail server.The main problem that I am having is that I need to create a script that logs me to the destination and I can't find a way to echo the password.
What I tried:
telnet host -l username ; echo 'password'
And still it asks for my password.Is there any way to fix this or I am doing something wrong?
First of all, you can use eval:
eval "{ echo user_name; sleep 1; echo pass; sleep 1; echo '?'; sleep 5; }" | telnet host_address
Make sure to replace user_name, pass, ? which is the command you want to run and host_address where your telnet host is listening; for me it is a local IP.
It’s surprisingly easy to script a set of command and pipe them into the telnet application. All you need to do is something like this:
(echo commandname;echo anothercommand) | telnet host_address
The only problem is the nagging login that you have to get through… it doesn’t show up right away. So if you pipe in an “echo admin” and then “echo password,” it will happen too quickly and won’t be sent to the server. The solution? Use the sleep command!
Adding in a couple of sleep 3 commands, to wait three seconds, solves the problem. First we’ll echo the username and password, and then we’ll echo the reboot command, and each time we’ll wait three seconds between. The final command will reboot the server immediately:
(sleep 3;echo admin;sleep 3;echo mypassword;sleep 3;echo system reboot;sleep 3;) | telnet host_address
You can put this into a shell script and run it whenever you want. Or you can add it to your cron like this (on OS X or Linux):
crontab -e
Add this line somewhere:
1 7 * * * (sleep 3;echo admin;sleep 3;echo mypassword;sleep 3;echo system reboot;sleep 3;) | telnet host_address
This will reboot your router at 7:01 AM each morning.
AFAIK, you won't be able to automate telnet that way. But it is still possible - even if it is a very bad idea (I'll elaborate on that later).
First why does your try fail :
you launched a telnet command reading from stdin (I suppose terminal) and writing to stdout and stderr (I suppose also a terminal)
if your telnet is reasonably recent, it tries to protect your authentication and asks your password from /dev/tty (for security reasons)
when that command has ended you write password on your own terminal
What should you do instead :
launch telnet with automatic authentication disable (on mine it is telnet -X SRA)
feed its input with the commands you want to pass
wait some delay before entering input, at least for login and password, because if you don't telnet clear input before reading and discards your inputs
Here is an example that allowed me to telnet to my own machine :
sh << EOF | telnet -X SRA localhost
sleep 2
echo my_user_name
sleep 1
echo my_password
# sleep 1 # looks like it can be removed
echo echo foo and bar
sleep 1
EOF
It correctly logs me into my box, executes echo foo and bar (essential command :-) ) and disconnects
Now why you should never do that :
you write a password in clear text in a script file which is poor security practice
you use telnet to do batch processing when it is not intended to be used that way : the script may not be portable to another telnet version
If you really want to pass command in a batch way to a remote server, you should instead try to use ssh which :
has options to process authentication securely (no password in script, nothing in clear text)
is intended to be used in batch mode as well as interactively
If you cannot use ssh (some sysadmin do not like to have uncontrolled input ssh connections) you could also try to use rsh. It is older, far less secure, but at least was designed for batch usage.
Thanks to Harvix answer, I got knew that there is also expect alternative native for shell, called sexpect. Get it from here. Then create this script (I call it telnetpass):
#!/bin/bash
# This script is used for automatically pass authentication by username and password in telnet prompt
# Its goal is similar as sshpass, but for telnet, so I call it telnetpass
. ~/.private/cisco_pw # should contain PASSWORD variable
export SEXPECT_SOCKFILE=/tmp/sexpect-telnetpass-$$.sock
sexpect spawn telnet $1
sexpect expect -cstring 'Username:'
sexpect send -enter $USER
sexpect expect -cstring 'Password:'
sexpect send -enter $PASSWORD
sexpect interact
Then you can run: telnetpass Host125 and got pass the authentication automatically
Trying 198.51.100.78 ...
Connected to Host125.
Escape character is '^]'.
User Access Verification
Username: ashark
Password:
host-125>
I like this solution more than using sleep commands as suggested in another answers, because sleep solutions sometimes fail.
Have you tried using the expect command ?? You will have to create a script where you identify the 'expected' response from the server e.g. 'Password:' and then supply the password in the script. The following will explain: https://en.wikipedia.org/wiki/Expect - A good example is also shown here: http://en.kioskea.net/faq/4736-shell-script-for-telnet-and-run-commands
Try eval:
eval "{ echo;
sleep 3;
echo $user;
sleep 1;
echo $pass;
sleep 1;
echo '?';
sleep 1; }" | telnet your_host
In this example, my remote command is '?' (help).
The sleeps (maybe not all of them nor these times; trial-error...) are needed to avoid telnet misses some inputs.
The user and password are passed as variables ($user and $pass). Take into account security recommendations to store the password if you are scripting.

How to check if a server is running

I want to use ping to check to see if a server is up. How would I do the following:
ping $URL
if [$? -eq 0]; then
echo "server live"
else
echo "server down"
fi
How would I accomplish the above? Also, how would I make it such that it returns 0 upon the first ping response, or returns an error if the first ten pings fail? Or, would there be a better way to accomplish what I am trying to do above?
I'ld recommend not to use only ping. It can check if a server is online in general but you can not check a specific service on that server.
Better use these alternatives:
curl
man curl
You can use curl and check the http_response for a webservice like this
check=$(curl -s -w "%{http_code}\n" -L "${HOST}${PORT}/" -o /dev/null)
if [[ $check == 200 || $check == 403 ]]
then
# Service is online
echo "Service is online"
exit 0
else
# Service is offline or not working correctly
echo "Service is offline or not working correctly"
exit 1
fi
where
HOST = [ip or dns-name of your host]
(optional )PORT = [optional a port; don't forget to start with :]
200 is the normal success http_response
403 is a redirect e.g. maybe to a login page so also accetable and most probably means the service runs correctly
-s Silent or quiet mode.
-L Defines the Location
-w In which format you want to display the response
-> %{http_code}\n we only want the http_code
-o the output file
-> /dev/null redirect any output to /dev/null so it isn't written to stdout or the check variable. Usually you would get the complete html source code before the http_response so you have to silence this, too.
nc
man nc
While curl to me seems the best option for Webservices since it is really checking if the service's webpage works correctly,
nc can be used to rapidly check only if a specific port on the target is reachable (and assume this also applies to the service).
Advantage here is the settable timeout of e.g. 1 second while curl might take a bit longer to fail, and of course you can check also services which are not a webpage like port 22 for SSH.
nc -4 -d -z -w 1 ${HOST} ${PORT} &> /dev/null
if [[ $? == 0 ]]
then
# Port is reached
echo "Service is online!"
exit 0
else
# Port is unreachable
echo "Service is offline!"
exit 1
fi
where
HOST = [ip or dns-name of your host]
PORT = [NOT optional the port]
-4 force IPv4 (or -6 for IPv6)
-d Do not attempt to read from stdin
-z Only listen, don't send data
-w timeout
If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. (In this case nc will exit 1 -> failure.)
(optional) -n If you only use an IP: Do not do any DNS or service lookups on any specified addresses, hostnames or ports.
&> /dev/null Don't print out any output of the command
You can use something like this -
serverResponse=`wget --server-response --max-redirect=0 ${URL} 2>&1`
if [[ $serverResponse == *"Connection refused"* ]]
then
echo "Unable to reach given URL"
exit 1
fi
Use the -c option with ping, it'll ping the URL only given number of times or until timeout
if ping -c 10 $URL; then
echo "server live"
else
echo "server down"
fi
Short form:
ping -c5 $SERVER || echo 'Server down'
Do you need it for some other script? Or are trying to hack some simple monitoring tool? In this case, you may want to take a look at Pingdom: https://www.pingdom.com/.
I using the following script function to check servers are online or not. It's useful when you want to check multiple servers. The function hide the ping output, and you can handle separately the server live or server down case.
#!/bin/bash
#retry count of ping request
RETRYCOUNT=1;
#pingServer: implement ping server functionality.
#Param1: server hostname to ping
function pingServer {
#echo Checking server: $1
ping -c $RETRYCOUNT $1 > /dev/null 2>&1
if [ $? -ne 0 ]
then
echo $1 down
else
echo $1 live
fi
}
#usage example, pinging some host
pingServer google.com
pingServer server1
One good solution is to use MRTG (a simple graphing tool for *NIX) with ping-probe script. look it up on Google.
read this for start.
Sample Graph:

telnet automation script fails sometimes

I am running the following simple telnet script which just logs into a machine and exits.
The same script works fine (goes through 1000 iterations) from one Linux server but fails (consistently) from another Linux server (fails after say 200 attempts).
In failure case, the number of iterations it takes to fail varies but failure is persistent.
#!/usr/bin/perl
use Net::Telnet;
my $loop = 0;
my $dumpfile = "dump.log";
my $inputfile = "input.log";
for ($loop =1; $loop <=1000; $loop++) {
print "===============Loop: $loop =====================\n";
$telnet = new Net::Telnet ( Timeout=>20, Errmode=>'die');
print "$telnet\n";
$telnet->open('sys-007');
$telnet->dump_log($dumpfile);
$telnet->input_log($inputfile);
$telnet->waitfor('/login: $/i');
$telnet->print('root');
$telnet->waitfor('/Password:$/i');
$telnet->print('007');
sleep 2;
$telnet->print('exit');
print "=================================================\n";
}
Script exits with:
pattern match read eof at ./telnettest.pl line 15 (i.e, waitfor('/login: $/i'); Line)
I tried the following to see what is going wrong:
In the client machine: (sys-007)
tcpdump -nvvv -w test.txt host <Server IP>
strings test.txt has:
Successful attempt Log:
sys-007 (ttyp0)
^Fl$4
^!^Fl$
login:
^Fl$4
^Fl$4
root
^(^Fl*
root
bP"u
^Fl*4
bP5u
^.^Fl*
Password:
^Fl*4
^Fl*4
007
^7^Fl6
^Fl64
^9^Fl6
Terminal type? [xterm]
^Fl64
^Fl64
exit
^Fl<4
^Fl<
exit
^Fl<
Failed Attempt:
sys-007 (ttyp0)
*^hn
+^hn
No login: prompt!
In server machine: (Linux Server)
[telnet#server]~% netstat --inet -a | grep telnet | grep sys-007
There are no TIME_WAIT or CLOSE_WAIT sockets.
Please tell me what should I look for to find out what is going wrong.
Are you able to login normally after this? I'm guessing this other server is cutting you off because you've made too many connections.
What I suspect is that the telnet program is waiting for the prompt. I usually use the below format to define the prompt, so that the script usually is able to find the prompt from the below given options.
test = new Net::Telnet (Timeout => 3000 , Prompt => '/[%#\$>?:] $/' );
$test->open($IPAddress1);
$test->login($Login,$Password1);
my #input=$test->cmd("uname -a\n");
print "Connected to : #input";
#input=$cmd->cmd("pwd\n");
Please let us know if you still face the problem.

Resources