Perform action when user logs in via SSH from a particular host - linux

I have a quesiton that puzzles me and I wonder if anyone has attempted to achieve the following:
Let's assume that this is the result of my 'last' command in a Linux environment:
root pts/1 192.168.1.10 Wed Feb 10 07:04 - 07:57 (00:52)
root pts/2 Tue Feb 9 22:00 - 00:13 (02:13)
How can I setup a particular action (say for example a modified MOTD or sending an email) if the the 'root' user has logged in from 192.168.1.10. Is there a way of capturing this information?
The second part of this question is that how can I make the above check a bit more robust - i.e. if I have the following:
mary pts/1 192.168.1.10 Wed Feb 10 07:04 - 07:57 (00:52)
bob pts/2 Tue Feb 9 22:00 - 00:13 (02:13)
Now I'd like to perform an action if the username is equal to 'mary' and the host is 192.168.1.10.
Any suggestions are welcomed.
Thank you in advance.

There's a special file /etc/ssh/sshrc where you can put some commands that will runs each time someone connect by ssh. I wrote that for you :
#!/bin/bash
mail=user#domain.tld
monitored_user=root
monitored_ip=x.x.x.x
hostname=$(hostname)
# add a welcome message:
printf >&2 "\nWelcome on $hostname $USER\n"
read -d " " ip <<< $SSH_CONNECTION
[[ $ip == $monitored_ip && $USER == $monitored_user ]] || exit 0
date=$(date "+%d.%m.%Y %Hh%M")
reverse=$(dig -x $ip +short)
mail -s "Connexion of $USER on $hostname" $mail <<EOF
IP: $ip
Reverse: $reverse
Date: $date
EOF
Put this script in a file, then put the full path of the script in /etc/ssh/sshrc
In man ssh :
/etc/ssh/sshrc :
Commands in this file are executed by ssh when the user
logs in, just before the user's shell (or command) is started. See the
sshd(8) manual page for more information.

Thanks for all your replies. Eventually I managed to find a solution which does work for the time being but it does have one flaw which I'll point out in a minute.
I have added the following to my /etc/bashrc file (or /etc/bash.bashrc whatever environment you're using):
HOST="192.168.0.1"
RHOST=`who am i | sed -n 's/.*(\([^) ]*\).*/\1/p; 1q'`
if [ "$RHOST" == "$HOST" ]; then
echo "SAY WHAT!"
#add further actions here if needed
fi
The flaw that I was talking about before may actually not be a flaw. If you're already SSH-ed into the system, and you want to SSH to a host which lives on the same IP, say ssh root#your-host who am i would then print 'your-host' but I think that's the way it should be.
Needless to say that the above sed statement can be modified so you can capture the username as well, and you can extend the if/else statement to suite your needs.
Thank you again for all your replies.

You can add something to /etc/profile or equivalent that does something depending on the value of $SSH_CLIENT.

It looks like you are using last because it reads /var/log/wtmp by default which is a record of logins. The who command also allows you to read the same file but with an interface more to your needs.
For example:
$ who --ips /var/log/wtmp | grep '^msw.*127.0.0.1'
msw pts/2 2012-10-07 15:52 127.0.0.1
msw pts/3 2012-10-07 15:55 127.0.0.1
where neither of those sessions were active, but rather historic and logged.

In ubuntu i put a script in
/etc/profile.d
and when someone(user ssh) log in, it send an email to my mail
#/etc/profile.d/run_on_loggin.sh
echo $(who i am) | mail -s 'SSH Login Notification' mymail#hotmail.com
I want to create a php file with smtp, to send email with my mail to me...
some times hotmail saved in spam...
if i have the php file i will run like this...
if i want to pass var to file php run like this...
excuse my english :3
note: i think this command run from user, be carefully if the user doen't has permission to use some command or send email.

One way would be to run a simple script periodically:
#!/bin/bash
users=$(last | sed -ne '/192\.168\.1\.10/ s/\([^ ]*\).*/\1/p')
for user in $users; do
sendmail "$user" < email.txt
done
This would pipe the last command into sed to extract a user list and save it into the variable $users. The sed command uses the -n flag so it only prints what we tell it to. First, we select lines that contain the specified IP, with the /192\.168\.1\.10/ "address". On those lines, we attempt to extract the characters before a space, and if we succeed we print the result.
Then, we can loop through the $users variable and act accordingly.
One way to call this repeatedly would be through cron, and a simpler way would be to do while true; do ./my_script.bash; sleep 60; done.

Related

Access SSH client IP address, within a screen session

Accessing the IP address of a connecting SSH client is possible via environment variables (such as SSH_CONNECTION), as described in
Find the IP address of the client in an SSH session
In a GNU screen session though, those environment variables are defined by whoever started the screen to begin with. Is there any way to also get hold of the SSH connection information, for someone who enters an already-existing screen session later, like from another host?
I can't think of a way to determine this, but this can be useful in cases where screen sessions are shared between different people, for example.
If the screen session is launched as root, you can but it won't be perfectly reliable
If two users type in the same screen window, they will both interact within the same shell. One can write a command. The other can press the <enter> key.
You have to get access to the environment variable SSH_CONNECTION (or better SSH_CLIENT) which is only possible if you are root, or if you use the same user inside the screen session.
Supposing you are root inside the screen session, you can know the last user active in a screen session by using the ps command and finding the last active session.
ps h -C screen katime -o pid,user
By using the pid, and accessing the /proc/<pid>/environ file, you can get the SSH_CLIENT variable.
sed -z '/SSH_CLIENT/p;d' /proc/`ps h -C screen katime -o pid |head -1`/environ
--> SSH_CLIENT=257.31.120.12
All of this suppose that your screen is executed as root
You can also chose to log all the active connections.
For such need, I would suggest you to store both the full list of connections and their last activity.
ps eh -C screen kstime -o pid,atime | while read pid stime; do echo -n "$stime: ";\
gawk -v 'RS=\0' -F= '$1=="SSH_CLIENT" {print $2}' /proc/$pid/environ; done
Result:
00:00:00: 257.31.120.12 61608 22
00:07:11: 258.1.2.3.4 49947 22
Note that you can also parse the result of the ps eh -C screen kstime -o args command if you find it easier.
EDIT:
This is a working Debian command to get all users currently connected to the same screen session:
find /var/run/screen/
-name $(pstree -sp $$ |sed 's/.*screen(\([0-9]*\)).*/\1/;q').*
-printf "%h\n"
| cut -f2 -d-
You can check the output of the last command that would list of all IP addresses or hostnames of all connection made if sshd is the only way to connect to server.
ec2-user]# last
ec2-user pts/0 115.250.185.183 Sun May 29 13:49 still logged in
ec2-user pts/0 115.250.140.241 Sat May 28 07:26 - 10:15 (02:48)
root pts/4 113.21.68.105 Tue May 3 10:15 - 10:15 (00:00)
Alternatively (on Linux), you can check /var/log/secure where sshd will usually log all details of all the connections made even if they don't result in successful logins.
If you're trying to support the multi-display mode ('screen -x'), then as someone said above you are likely out of luck.
One the other hand, if you could assume single-user mode, then you could create a wrapper/alias for the screen command that carries along an environment variable into screen (see 'screen -X stuff ...'); in this case you are just passing along SSH_CLIENT that will have the appropriate value.
If you can assume a given username comes from a single location (or, if more than one location, then simply choose most recent), then you can do some grep/sed on output of 'last' command.
client_ip=`last -ai | grep "still logged in" | grep "$USER " | grep -v '0.0.0.0' | tail -n 1 | sed 's/.* //g'`
echo "Hello $client_ip"
If your screen is starting usually in detached mode, then in your .screenrc, add the the following:
shell -$SHELL
Then your screen will have all the the variables.
For currently running screens that you are stuck with, simply run.
source ~/.bash_profile
Replace the path and the file name to match your environment.

Running bash script in cron

I have the following bash script:
!/bin/bash
# script to send simple email
#Wait 5 seconds
#sleep 05
# email subject
SUBJECT="SUBJECT"
# Email To ?
EMAIL="EMAIL"
# Email text/message
MYIP=$(curl http://ipecho.net/plain)
LASTIP=$(head -n 1 /home/ubuntu/myip.txt)
echo "Last IP:" $LASTIP
echo "Current IP:" $MYIP
#Check if IPs are the same
if [[ "$MYIP" == "$LASTIP" ]]; then
echo "IP hasn't changed. Do nothing."
else
sendemail -f $EMAIL -t $EMAIL -u $SUBJECT -m $MYIP -s smtp.gmail.com -o tls=yes -xu username -xp password
echo $MYIP > myip.txt
fi
When I try to run it in the command line, it works perfectly. The problem starts when I include it in "crontab -e" like this: "* * * * * /home/ubuntu/myip.sh".
Then it does not work. Seem to be that the sendmail is not functioning properly.
When I do a: tail -f /var/log/syslog
Sep 18 21:48:02 gpuserver sendmail[18665]: r8J1m1gO018665: from=ubuntu, size=314, class=0, nrcpts=1, msgid=<201309190148.r8J1m1gO018665#gpuserver>, relay=ubuntu#localhost
Sep 18 21:48:02 gpuserver sendmail[18665]: r8J1m1gO018665: to=ubuntu, ctladdr=ubuntu (1000/1000), delay=00:00:01, xdelay=00:00:00, mailer=relay, pri=30314, relay=[127.0.0.1] [127.0.0.1], dsn=4.0.0, stat=Deferred: Connection refused by [127.0.0.1]
Any ideas?
When you run a script from the command line you are utilizing your current environment. Cron doesn't have that info ... So what you should do from the command line is:
env > me
Then edit the script and include ./me at the start of the script. That way your cron task will run with the same environment.
When a cron prints something to stdout or stderr, cron itself tries to send a report by e-mail. Since you probably don't have system-wide e-mail configured, this is probably where the error message in syslog is coming from.
To prevent cron from sending any mail, make sure your script and any commands it spawns don't output anything to stdout or stderr. An easy way to do this is to add "&>/dev/null" to your cron line.
You can also add 'MAILTO=""' at the beginning of your crontab.
This all documented in the man pages for cron/crontab (man -k cron).

Run scripts remotely via SSH

I need to collect user information from 100 remote servers. We have public/private key infrastructure for authentication, and I have configured ssh-agent command to forward key, meaning i can login on any server without password prompt (auto login).
Now I want to run a script on all server to collect user information (how many user account we have on all servers).
This is my script to collect user info.
#!/bin/bash
_l="/etc/login.defs"
_p="/etc/passwd"
## get mini UID limit ##
l=$(grep "^UID_MIN" $_l)
## get max UID limit ##
l1=$(grep "^UID_MAX" $_l)
awk -F':' -v "min=${l##UID_MIN}" -v "max=${l1##UID_MAX}" '{ if ( $3 >= min && $3 <= max && $7 != "/sbin/nologin" ) print $0 }' "$_p"
I don't know how to run this script using ssh without interaction??
Since you need to log into the remote machine there is AFAICT no way to do this "without ssh". However, ssh accepts a command to execute on the remote machine once logged in (instead of the shell it would start). So if you can save your script on the remote machine, e.g. as ~/script.sh, you can execute it without starting an interactive shell with
$ ssh remote_machine ~/script.sh
Once the script terminates the connection will automatically be closed (if you didn't configure that away purposely).
Sounds like something you can do using expect.
http://linux.die.net/man/1/expect
Expect is a program that "talks" to other interactive programs according to a script. Following the script, Expect knows what can be expected from a program and what the correct response should be.
If you've got a key on each machine and can ssh remotehost from your monitoring host, you've got all that's required to collect the information you've asked for.
#!/bin/bash
servers=(wopr gerty mother)
fmt="%s\t%s\t%s\n"
printf "$fmt" "Host" "UIDs" "Highest"
printf "$fmt" "----" "----" "-------"
count='awk "END {print NR}" /etc/passwd' # avoids whitespace problems from `wc`
highest="awk -F: '\$3>n&&\$3<60000{n=\$3} END{print n}' /etc/passwd"
for server in ${servers[#]}; do
printf "$fmt" "$server" "$(ssh "$server" "$count")" "$(ssh "$server" "$highest")"
done
Results for me:
$ ./doit.sh
Host UIDs Highest
---- ---- -------
wopr 40 2020
gerty 37 9001
mother 32 534
Note that this makes TWO ssh connections to each server to collect each datum. If you'd like to do this a little more efficiently, you can bundle the information into a single, slightly more complex collection script:
#!/usr/local/bin/bash
servers=(wopr gerty mother)
fmt="%s\t%s\t%s\n"
printf "$fmt" "Host" "UIDs" "Highest"
printf "$fmt" "----" "----" "-------"
gather="awk -F: '\$3>n&&\$3<60000{n=\$3} END{print NR,n}' /etc/passwd"
for server in ${servers[#]}; do
read count highest < <(ssh "$server" "$gather")
printf "$fmt" "$server" "$count" "$highest"
done
(Identical results.)
ssh remoteserver.example /bin/bash < localscript.bash
(Note: the "proper" way to authenticate without manually entering in password is to use SSH keys. Storing password in plaintext even in your local scripts is a potential security vulnerability)
You can run expect as part of your bash script. Here's a quick example that you can hack into your existing script:
login=user
IP=127.0.0.1
password='your_password'
expect_sh=$(expect -c "
spawn ssh $login#$IP
expect \"password:\"
send \"$password\r\"
expect \"#\"
send \"./$remote_side_script\r\"
expect \"#\"
send \"cd /lib\r\"
expect \"#\"
send \"cat file_name\r\"
expect \"#\"
send \"exit\r\"
")
echo "$expect_sh"
You can also use pscp to copy files back and forth as part of a script so you don't need to manually supply the password as part of the interaction:
Install putty-tools:
$ sudo apt-get install putty-tools
Using pscp in your script:
pscp -scp -pw $password file_to_copy $login#$IP:$dest_dir
maybe you'd like to try the expect command as following
#!/usr/bin/expect
set timeout 30
spawn ssh -p ssh_port -l ssh_username ssh_server_host
expect "password:"
send "your_passwd\r"
interact
the expect command will catch the "password:" and then auto fill the passwd your send by above.
Remember that replace the ssh_port, ssh_username, ssh_server_host and your_passwd with your own configure

Using the passwd command from within a shell script

I'm writing a shell script to automatically add a new user and update their password. I don't know how to get passwd to read from the shell script instead of interactively prompting me for the new password. My code is below.
adduser $1
passwd $1
$2
$2
from "man 1 passwd":
--stdin
This option is used to indicate that passwd should read the new
password from standard input, which can be a pipe.
So in your case
adduser "$1"
echo "$2" | passwd "$1" --stdin
[Update] a few issues were brought up in the comments:
Your passwd command may not have a --stdin option: use the chpasswd
utility instead, as suggested by ashawley.
If you use a shell other than bash, "echo" might not be a builtin command,
and the shell will call /bin/echo. This is insecure because the password
will show up in the process table and can be seen with tools like ps.
In this case, you should use another scripting language. Here is an example in Perl:
#!/usr/bin/perl -w
open my $pipe, '|chpasswd' or die "can't open pipe: $!";
print {$pipe} "$username:$password";
close $pipe
The only solution works on Ubuntu 12.04:
echo -e "new_password\nnew_password" | (passwd user)
But the second option only works when I change from:
echo "password:name" | chpasswd
To:
echo "user:password" | chpasswd
See explanations in original post: Changing password via a script
Nowadays, you can use this command:
echo "user:pass" | chpasswd
Read the wise words from:
http://mywiki.wooledge.org/BashFAQ/078
I quote:
Nothing you can do in bash can possibly work. passwd(1) does not read from standard input. This is intentional. It is for your protection. Passwords were never intended to be put into programs, or generated by programs. They were intended to be entered only by the fingers of an actual human being, with a functional brain, and never, ever written down anywhere.
Nonetheless, we get hordes of users asking how they can circumvent 35 years of Unix security.
It goes on to explain how you can set your shadow(5) password properly, and shows you the GNU-I-only-care-about-security-if-it-doesn't-make-me-think-too-much-way of abusing passwd(1).
Lastly, if you ARE going to use the silly GNU passwd(1) extension --stdin, do not pass the password putting it on the command line.
echo $mypassword | passwd --stdin # Eternal Sin.
echo "$mypassword" | passwd --stdin # Eternal Sin, but at least you remembered to quote your PE.
passwd --stdin <<< "$mypassword" # A little less insecure, still pretty insecure, though.
passwd --stdin < "passwordfile" # With a password file that was created with a secure `umask(1)`, a little bit secure.
The last is the best you can do with GNU passwd. Though I still wouldn't recommend it.
Putting the password on the command line means anyone with even the remotest hint of access to the box can be monitoring ps or such and steal the password. Even if you think your box is safe; it's something you should really get in the habit of avoiding at all cost (yes, even the cost of doing a bit more trouble getting the job done).
Here-document works if your passwd doesn't support --stdin and you don't want to (or can't) use chpasswd for some reason.
Example:
#!/usr/bin/env bash
username="user"
password="pass"
passwd ${username} << EOD
${password}
${password}
EOD
Tested under Arch Linux. This passwd is an element of shadow-utils and installed from the core/filesystem package, which you usually have by default since the package is required by core/base.
You could use chpasswd
echo $1:$2 | chpasswd
For those who need to 'run as root' remotely through a script logging into a user account in the sudoers file, I found an evil horrible hack, that is no doubt very insecure:
sshpass -p 'userpass' ssh -T -p port user#server << EOSSH
sudo -S su - << RROOT
userpass
echo ""
echo "*** Got Root ***"
echo ""
#[root commands go here]
useradd -m newuser
echo "newuser:newpass" | chpasswd
RROOT
EOSSH
I stumbled upon the same problem and for some reason the --stdin option was not available on the version of passwd I was using (shipped in Ubuntu 14.04).
If any of you happen to experience the same issue, you can work it around as I did, by using the chpasswd command like this:
echo "<user>:<password>" | chpasswd
Tested this on a CentOS VMWare image that I keep around for this sort of thing. Note that you probably want to avoid putting passwords as command-line arguments, because anybody on the entire machine can read them out of 'ps -ef'.
That said, this will work:
user="$1"
password="$2"
adduser $user
echo $password | passwd --stdin $user
This is the definitive answer for a teradata node admin.
Go to your /etc/hosts file and create a list of IP's or node names in a text file.
SMP007-1
SMP007-2
SMP007-3
Put the following script in a file.
#set a password across all nodes
printf "User ID: "
read MYUSERID
printf "New Password: "
read MYPASS
while read -r i; do
echo changing password on "$i"
ssh root#"$i" sudo echo "$MYUSERID":"$MYPASS" | chpasswd
echo password changed on "$i"
done< /usr/bin/setpwd.srvrs
Okay I know I've broken a cardinal security rule with ssh and root
but I'll let you security folks deal with it.
Now put this in your /usr/bin subdir along with your setpwd.srvrs config file.
When you run the command it prompts you one time for the User ID
then one time for the password. Then the script traverses all nodes
in the setpwd.srvrs file and does a passwordless ssh to each node,
then sets the password without any user interaction or secondary
password validation.
For me on Raspbian it works only this way (old password added):
#!/usr/bin/env bash
username="pi"
password="Szevasz123"
new_ps="Szevasz1234"
passwd ${username} << EOD
${password}
${new_ps}
${new_ps}
EOD
Have you looked at the -p option of adduser (which AFAIK is just another name for useradd)? You may also want to look at the -P option of luseradd which takes a plaintext password, but I don't know if luseradd is a standard command (it may be part of SE Linux or perhaps just an oddity of Fedora).
Sometimes it is useful to set a password which nobody knows. This seems to work:
tr -dc A-Za-z0-9 < /dev/urandom | head -c44 | passwd --stdin $user
echo 'yourPassword' | sudo -S yourCommand
if -S doesnt work try with -kS
You can use the expect utility to drive all programs that read from a tty (as opposed to stdin, which is what passwd does). Expect comes with ready to run examples for all sorts of interactive problems, like passwd entry.

SSH "Login monitor" for Linux

I'm trying to write a script that informs the user when someone has logged in on the machine via ssh.
My current idea is to parse the output of "w" using grep in intervals.
But that's neither elegant nor performant. Has anyone got a better idea how to implement such a program?
Any help would really be appreciated!
Paul Tomblin has the right suggestion.
Set up logging in your sshd_config to point to a syslog facility that you can log separately:
=> see man 3 syslog for more facilities. Choose one like e.g.
# Logging
SyslogFacility local5
LogLevel INFO
Then set up your syslog.conf like this:
local5.info |/var/run/mysshwatcher.pipe
Add the script you're going to write to /etc/inittab so it keeps running:
sw0:2345:respawn:/usr/local/bin/mysshwatcher.sh
then write your script:
#!/bin/sh
P=/var/run/mysshwatcher.pipe
test -p $P || mkfifo $P
while read x <$P; do
# ... whatever, e.g.:
echo "ssh info: $x" | wall
done;
Finally, restart your syslogd and get your inittab reloaded (init q) and it should work. If other variantes of these services are used, you need to configure things accordingly (e.g. newsyslogd => /etc/newsyslog.conf; Ubuntu: /etc/event.d isntead of inittab)
This is very rudimentary and lacking, but should be enough to get you started ...
more info: man sshd_config for more logging options/verbosity.
On Ubuntu (and I'd guess all other Debian distros, if not all Linuces), the file /var/log/auth.log records successful (and unsuccessful) login attempts:
sshd[XXX]: pam_unix(sshd:session): session opened for user XXX
You could set up a very simple monitor using this command (note that you have to be root to see the auth log):
sudo tail -F /var/log/auth.log | grep sshd
If you do not care how they logged in (telnet/ssh), the 'last' Unix command line utility shows you the last few logins in the machine. Remote users will show the IP address
[root#ex02 www]# last
foo pts/1 81.31.x.y Sun Jan 18 07:25 still logged in
foo pts/0 81.31.x.y Sun Jan 18 01:51 still logged in
foo pts/0 81.31.x.y Sat Jan 17 03:51 - 07:52 (04:00)
bar pts/5 199.146.x.y Fri Jan 16 08:57 - 13:29 (04:32
Set up a named pipe, and set up a log file parser to listen to it, and send the ssh messages to it. The log file parser can do what you want, or signal to a daemon to do it.
Redirecting the log file is done in a config file in /etc/ whose name escapes me right now. /etc/syslog.conf, I think.
I have made a program (which i call Authentication Monitor) that solves the task described in the question.
If you wanted to, you are more than welcome to download it to investigate how I solve this problem (using log-files).
You can find Authentication Monitor freely available here: http://bwyan.dk/?p=1744
We had the same problem, so we wrote our own script.
It can be downloaded from the github.
Hope it helps :)
cheers!
Ivan

Resources