Find the IP address of the client in an SSH session - linux

I have a script that is to be run by a person that logs in to the server with SSH.
Is there a way to find out automatically what IP address the user is connecting from?
Of course, I could ask the user (it is a tool for programmers, so no problem with that), but it would be cooler if I just found out.

Check if there is an environment variable called:
$SSH_CLIENT
OR
$SSH_CONNECTION
(or any other environment variables) which gets set when the user logs in. Then process it using the user login script.
Extract the IP:
$ echo $SSH_CLIENT | awk '{ print $1}'
1.2.3.4
$ echo $SSH_CONNECTION | awk '{print $1}'
1.2.3.4

You could use the command:
server:~# pinky
that will give to you somehting like this:
Login Name TTY Idle When Where
root root pts/0 2009-06-15 13:41 192.168.1.133

Try the following to get just the IP address:
who am i|awk '{ print $5}'

Just type the following command on your Linux machine:
who

who | cut -d"(" -f2 |cut -d")" -f1

Improving on a prior answer. Gives ip address instead of hostname. --ips not available on OS X.
who am i --ips|awk '{print $5}' #ubuntu 14
more universal, change $5 to $6 for OS X 10.11:
WORKSTATION=`who -m|awk '{print $5}'|sed 's/[()]//g'`
WORKSTATION_IP=`dig +short $WORKSTATION`
if [[ -z "$WORKSTATION_IP" ]]; then WORKSTATION_IP="$WORKSTATION"; fi
echo $WORKSTATION_IP

who am i | awk '{print $5}' | sed 's/[()]//g' | cut -f1 -d "." | sed 's/-/./g'
export DISPLAY=`who am i | awk '{print $5}' | sed 's/[()]//g' | cut -f1 -d "." | sed 's/-/./g'`:0.0
I use this to determine my DISPLAY variable for the session when logging in via ssh and need to display remote X.

netstat -tapen | grep ssh | awk '{ print $4}'

A simple command to get a list of recent users logged in to the machine is last. This is ordered most recent first, so last | head -n 1 will show the last login. This may not be the currently logged in user though.
Sample output:
root pts/0 192.168.243.99 Mon Jun 7 15:07 still logged in
admin pts/0 192.168.243.17 Mon Jun 7 15:06 - 15:07 (00:00)
root pts/0 192.168.243.99 Mon Jun 7 15:02 - 15:06 (00:03)
root pts/0 192.168.243.99 Mon Jun 7 15:01 - 15:02 (00:00)
root pts/0 192.168.243.99 Mon Jun 7 13:45 - 14:12 (00:27)
root pts/0 192.168.243.99 Mon May 31 11:20 - 12:35 (01:15)
...

You can get it in a programmatic way via an SSH library (https://code.google.com/p/sshxcute)
public static String getIpAddress() throws TaskExecFailException{
ConnBean cb = new ConnBean(host, username, password);
SSHExec ssh = SSHExec.getInstance(cb);
ssh.connect();
CustomTask sampleTask = new ExecCommand("echo \"${SSH_CLIENT%% *}\"");
String Result = ssh.exec(sampleTask).sysout;
ssh.disconnect();
return Result;
}

an older thread with a lot of answers, but none are quite what i was looking for, so i'm contributing mine:
sshpid=$$
sshloop=0
while [ "$sshloop" = "0" ]; do
if [ "$(strings /proc/${sshpid}/environ | grep ^SSH_CLIENT)" ];
then
read sshClientIP sshClientSport sshClientDport <<< $(strings /proc/${sshpid}/environ | grep ^SSH_CLIENT | cut -d= -f2)
sshloop=1
else
sshpid=$(cat /proc/${sshpid}/status | grep PPid | awk '{print $2}')
[ "$sshpid" = "0" ] && sshClientIP="localhost" && sshloop=1
fi
done
this method is compatible with direct ssh, sudoed users, and screen sessions. it will trail up through the process tree until it finds a pid with the SSH_CLIENT variable, then record its IP as $sshClientIP. if it gets too far up the tree, it will record the IP as 'localhost' and leave the loop.

I'm getting the following output from who -m --ips on Debian 10:
root pts/0 Dec 4 06:45 123.123.123.123
Looks like a new column was added, so {print $5} or "take 5th column" attempts don't work anymore.
Try this:
who -m --ips | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}'
Source:
#Yvan's comment on #AlexP's answer
#Sankalp's answer

netstat -tapen | grep ssh | awk '{ print $10}'
Output:
two # in my experiment
netstat -tapen | grep ssh | awk '{ print $4}'
gives the IP address.
Output:
127.0.0.1:22 # in my experiment
But the results are mixed with other users and stuff. It needs more work.

Assuming he opens an interactive session (that is, allocates a pseudo terminal) and you have access to stdin, you can call an ioctl on that device to get the device number (/dev/pts/4711) and try to find that one in /var/run/utmp (where there will also be the username and the IP address the connection originated from).

Usually there is a log entry in /var/log/messages (or similar, depending on your OS) which you could grep with the username.

netstat will work (at the top something like this)
tcp 0 0 10.x.xx.xx:ssh someipaddress.or.domainame:9379 ESTABLISHED

Linux: who am i | awk '{print $5}' | sed 's/[()]//g'
AIX: who am i | awk '{print $6}' | sed 's/[()]//g'

Search for SSH connections for "myusername" account;
Take first result string;
Take 5th column;
Split by ":" and return 1st part (port number don't needed, we want just IP):
netstat -tapen | grep "sshd: myusername" | head -n1 | awk '{split($5, a, ":"); print a[1]}'
Another way:
who am i | awk '{l = length($5) - 2; print substr($5, 2, l)}'

One thumb up for #Nikhil Katre's answer :
Simplest command to get the last 10 users logged in to the machine is last|head.
To get all the users simply use last command
The one using who or pinky did what is basically asked. But But But they don't give historical sessions info.
Which might also be interesting if you want to know someone who has just logged in and
logged out already when you start this checking.
if it is a multiuser system. I recommand add the user account you are looking for:
last | grep $USER | head
EDIT:
In my case, both $SSH_CLIENT and $SSH_CONNECTION do not exist.

Related

Combining two bash commands

If found this code
host raspberrypi | grep 'address' | cut -d' ' -f4
which gives pi Ip address
and this
wget --post-data="PiIP=1.2.3.4" http://dweet.io/dweet/for/cycy42
which sends 1.2.3.4 off to dweet.io stream
How can I get the output from 1st to replace the 1.2.3.4 in second please?
Save the output of the first command in a variable:
ip=$(host raspberrypi | grep 'address' | cut -d' ' -f4)
wget --post-data="PiIP=$ip" http://dweet.io/dweet/for/cycy42
Btw, if your raspberrypi is running raspbian,
then a much cleaner way to get the IP address:
hostname -I
Simplifying the commands to:
ip=$(hostname -I)
wget --post-data="PiIP=$ip" http://dweet.io/dweet/for/cycy42
Making that a one-liner:
wget --post-data="PiIP=$(hostname -I)" http://dweet.io/dweet/for/cycy42
UPDATE
So it seems hostname -I gives a bit different output for you.
You can use this then:
ip=$(hostname -I | awk '{print $1}')
To make it a one-liner, you can insert this into the second line just like I did in the earlier example.

list of logged in unique users in linux

I am working in linux environment as a non root user. I am using users command to get the logged in users
users
But it returns the user names multiple times because multiple shells are created with same login. Is there any way to print the unique user list using users commad.
Even i tried by
users | sort -u
Still it returns the user names multiple times.
Try with this -
who| awk '{print $1}'|sort -u
users | sort -u
Still it returns the user names multiple times.
Of course. sort is line based, and users only prints a single line.
What you want is to just look at the first word per line before sort -u in who output:
$ who|cut -f 1 -d " "|sort -u
barney
fred
wilma
or
$ who|sed 's/ .*//' |sort -u
barney
fred
wilma
However, if you are interested in some of the actual lines output by who you can also use
$ who|sort -u -k 1,1
barney pts/23 Aug 26 10:11 (:5.0)
fred pts/3 Jun 11 18:38 (:6.0)
wilma pts/0 Jul 31 07:29 (:3.0)
You can try this command: who | cut -d' ' -f1 | sort | uniq
You can use w command to get the list of logged in users and the details
This one is a bit shorter:
users | tr ' ' '\n' | sort -u

How to return substring from a linux command

I'm connecting to an exadata and want to get information about "ORACLE_HOME" variable inside them. So i'm using this command:
ls -l /proc/<pid>/cwd
this is the output:
2 oracle oinstall 0 Jan 23 21:20 /proc/<pid>/cwd -> /u01/app/database/11.2.0/dbs/
i need the get the last part :
/u01/app/database/11.2.0 (i dont want the "/dbs/" there)
i will be using this command several times in different machines. So how can i get this substring from whole output?
Awk and grep are good for these types of issues.
New:
ls -l /proc/<pid>/cwd | awk '{print ($NF) }' | sed 's#/dbs/##'
Old:
ls -l /proc/<pid>/cwd | awk '{print ($NF) }' | egrep -o '^.+[.0-9]'
Awk prints the last column of the input which is your ls command and then grep grabs the beginning of that string up the last occurrence of numbers and dots. This is a situational solution and perhaps not the best.
Parsing the output of ls is generally considered sub-optimal. I would use something more like this instead:
dirname $(readlink -f /proc/<pid>/cwd)

Little assistance needed regarding linux shell script

Actually this script is well know, DDos Deflate .
But after using, i notice im getting some emails without ip like
Banned the following ip addresses on Thu Mar 21 21:19:01 CET 2013
138 with 138 connections
From source, and from "netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr" command, i notice maybe we need to avoid first line , because looks like first line is just number of total open connections.
Can someone who know this scripting language, check if i`m right and fix it? Maybe some additional basic check like if ip == number of connections , break?
If you'd like to do what you ask :
netstat -ntu |
awk 'NR>1{sub(/:.*/, "", $5); print $5}' |
sort |
uniq -c |
sort -nr

How to specify more spaces for the delimiter using cut?

Is there any way to specify a field delimiter for more spaces with the cut command? (like " "+) ?
For example: In the following string, I like to reach value '3744', what field delimiter I should say?
$ps axu | grep jboss
jboss 2574 0.0 0.0 3744 1092 ? S Aug17 0:00 /bin/sh /usr/java/jboss/bin/run.sh -c example.com -b 0.0.0.0
cut -d' ' is not what I want, for it's only for one single space.
awk is not what I am looking for either, but how to do with 'cut'?
thanks.
Actually awk is exactly the tool you should be looking into:
ps axu | grep '[j]boss' | awk '{print $5}'
or you can ditch the grep altogether since awk knows about regular expressions:
ps axu | awk '/[j]boss/ {print $5}'
But if, for some bizarre reason, you really can't use awk, there are other simpler things you can do, like collapse all whitespace to a single space first:
ps axu | grep '[j]boss' | sed 's/\s\s*/ /g' | cut -d' ' -f5
That grep trick, by the way, is a neat way to only get the jboss processes and not the grep jboss one (ditto for the awk variant as well).
The grep process will have a literal grep [j]boss in its process command so will not be caught by the grep itself, which is looking for the character class [j] followed by boss.
This is a nifty way to avoid the | grep xyz | grep -v grep paradigm that some people use.
awk version is probably the best way to go, but you can also use cut if you firstly squeeze the repeats with tr:
ps axu | grep jbos[s] | tr -s ' ' | cut -d' ' -f5
# ^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^
# | | |
# | | get 5th field
# | |
# | squeeze spaces
# |
# avoid grep itself to appear in the list
I like to use the tr -s command for this
ps aux | tr -s [:blank:] | cut -d' ' -f3
This squeezes all white spaces down to 1 space. This way telling cut to use a space as a delimiter is honored as expected.
I am going to nominate tr -s [:blank:] as the best answer.
Why do we want to use cut? It has the magic command that says "we want the third field and every field after it, omitting the first two fields"
cat log | tr -s [:blank:] |cut -d' ' -f 3-
I do not believe there is an equivalent command for awk or perl split where we do not know how many fields there will be, ie out put the 3rd field through field X.
Shorter/simpler solution: use cuts (cut on steroids I wrote)
ps axu | grep '[j]boss' | cuts 4
Note that cuts field indexes are zero-based so 5th field is specified as 4
http://arielf.github.io/cuts/
And even shorter (not using cut at all) is:
pgrep jboss
One way around this is to go:
$ps axu | grep jboss | sed 's/\s\+/ /g' | cut -d' ' -f3
to replace multiple consecutive spaces with a single one.
Personally, I tend to use awk for jobs like this. For example:
ps axu| grep jboss | grep -v grep | awk '{print $5}'
As an alternative, there is always perl:
ps aux | perl -lane 'print $F[3]'
Or, if you want to get all fields starting at field #3 (as stated in one of the answers above):
ps aux | perl -lane 'print #F[3 .. scalar #F]'
If you want to pick columns from a ps output, any reason to not use -o?
e.g.
ps ax -o pid,vsz
ps ax -o pid,cmd
Minimum column width allocated, no padding, only single space field separator.
ps ax --no-headers -o pid:1,vsz:1,cmd
3443 24600 -bash
8419 0 [xfsalloc]
8420 0 [xfs_mru_cache]
8602 489316 /usr/sbin/apache2 -k start
12821 497240 /usr/sbin/apache2 -k start
12824 497132 /usr/sbin/apache2 -k start
Pid and vsz given 10 char width, 1 space field separator.
ps ax --no-headers -o pid:10,vsz:10,cmd
3443 24600 -bash
8419 0 [xfsalloc]
8420 0 [xfs_mru_cache]
8602 489316 /usr/sbin/apache2 -k start
12821 497240 /usr/sbin/apache2 -k start
12824 497132 /usr/sbin/apache2 -k start
Used in a script:-
oldpid=12824
echo "PID: ${oldpid}"
echo "Command: $(ps -ho cmd ${oldpid})"
Another way if you must use cut command
ps axu | grep [j]boss |awk '$1=$1'|cut -d' ' -f5
In Solaris, replace awk with nawk or /usr/xpg4/bin/awk
I still like the way Perl handles fields with white space.
First field is $F[0].
$ ps axu | grep dbus | perl -lane 'print $F[4]'
My approach is to store the PID to a file in /tmp, and to find the right process using the -S option for ssh. That might be a misuse but works for me.
#!/bin/bash
TARGET_REDIS=${1:-redis.someserver.com}
PROXY="proxy.somewhere.com"
LOCAL_PORT=${2:-6379}
if [ "$1" == "stop" ] ; then
kill `cat /tmp/sshTunel${LOCAL_PORT}-pid`
exit
fi
set -x
ssh -f -i ~/.ssh/aws.pem centos#$PROXY -L $LOCAL_PORT:$TARGET_REDIS:6379 -N -S /tmp/sshTunel$LOCAL_PORT ## AWS DocService dev, DNS alias
# SSH_PID=$! ## Only works with &
SSH_PID=`ps aux | grep sshTunel${LOCAL_PORT} | grep -v grep | awk '{print $2}'`
echo $SSH_PID > /tmp/sshTunel${LOCAL_PORT}-pid
Better approach might be to query for the SSH_PID right before killing it, since the file might be stale and it would kill a wrong process.

Resources