LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Security (https://www.linuxquestions.org/questions/linux-security-4/)
-   -   When using ssh: limit the number of users, that can log on, per IP address -rate (https://www.linuxquestions.org/questions/linux-security-4/when-using-ssh-limit-the-number-of-users-that-can-log-on-per-ip-address-rate-715179/)

tuxhats 03-28-2009 06:02 PM

When using ssh: limit the number of users, that can log on, per IP address -rate
 
:scratch:
I need to limit the number of users to one(1), that can log on using ssh, per IP address. Not sure iptables lets me do this. Is there a shell script, or where can I get one written? Thanks!!!

anomie 03-30-2009 10:22 AM

I don't know of a sshd_config directive that will limit authenticated connections. You may want to check the manpages for iptables(8) -- the connlimit directive. It comes with examples.

tuxhats 04-01-2009 12:08 PM

Perhaps,.... if I know who is where, on an internal network only, ....perhaps I could add this to my iptables:
iptables -A INPUT -p tcp -m state --syn --state NEW --dport 22 -m limit --limit 3/minute --limit-burst 1 -s 10.219.2.59 -m mac --mac-source 00:e8:42:g3:ht:4d --uid-owner bubba -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

ya think it is OK?

anomie 04-01-2009 06:23 PM

I haven't specifically tested your proposed rule (though I would encourage you to do so). Keep in mind that adding MAC address and UID criteria changes things a bit. AFAIK, UID does not work for an INPUT chain. (See this post.)

blackhole54 04-02-2009 01:13 AM

Quote:

Originally Posted by tuxhats (Post 3495130)
Perhaps,.... if I know who is where, on an internal network only, ....perhaps I could add this to my iptables:
Code:

  iptables -A INPUT -p tcp -m state --syn --state NEW --dport 22 -m limit --limit 3/minute --limit-burst 1 -s 10.219.2.59 \
    -m  mac --mac-source 00:e8:42:g3:ht:4d --uid-owner bubba -j ACCEPT
  iptables -A INPUT -p tcp --dport 22 -j DROP


-m owner options can only be supplied on the OUTPUT chain

Beyond that, these rules would limit how fast new connections can be established for a particular source IP address/MAC address combination and DROP all others. But they would not limit the total number of connections. IOW, that IP/MAC could keep logging on via SSH every 20 seconds w/o limit.

anomie has some good suggestions ... I have never used -m connlimit, but looking at iptable's man page I, think it does what you want. The man page has examples about how to use it. I suggest you check it out. And "trying things out" is frequently instructive.

tuxhats 04-08-2009 09:14 PM

Thanks for the input! I have set up the following for each mac address with the ending "DROP" iptable.

iptables -A INPUT -p tcp -m mac --mac-source 00:w8:42:g5:ht:4d -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

When I try to use, " -s 192.168.2.37 " or any locally assigned IP address, I get the following when I enter "iptables -L".

firefly.yada.com -> yada reflecting my business' DNS server.

Must I turn off my local(Ubuntu 8.04.2 LTS)server's DNS resolution process or perhaps something else? ... And how? I'd like to use "-s" option utilizing the local ip address assignment so I can "-m limit --limit 3/minute --limit-burst 1". which seems to be based on the ip rather than the mac.

Thanks!

win32sux 04-08-2009 09:23 PM

You can use the -n option. Example:
Code:

iptables -nvL
From man iptables:
Quote:

-n, --numeric
Numeric output. IP addresses and port numbers will be printed
in numeric format. By default, the program will try to display
them as host names, network names, or services (whenever appli‐
cable).

tuxhats 04-08-2009 09:46 PM

Thanks. iptables -nvL works nicely. One can't help but wonder, if the "firewall" is seeing the DNS address version or the local IP address when "filtering"? When I tried using any "-s" option for ip recognition, secure shelling(ssh) in hangs when several different users are trying to log in. It appears the "burst and limit" bit got everyone blocked as their ip addresses might have been viewed as "firefly.yada.com" and not 192.168.2.34 or whatever.

Got any insight here?
-

win32sux 04-08-2009 09:51 PM

If you specified a host name when you created the rule, iptables will use whatever IP address the host name resolved to at the moment the rule creation command was executed. If you specified an IP address, it will use that.

tuxhats 04-10-2009 11:02 AM

Thank you win32sux, blackhole54, & anomie!!! I got iptables working well for me with this command for each of my LAN based users:

iptables -A INPUT -p tcp -s 10.229.1.137 -m mac --mac-source 00:23:ae:6b:6c:62 -j ACCEPT

And finally this:

iptables -A INPUT -p tcp --dport 22 -j DROP

AND... iptables -uL works nicely.

Now all I have to do is limit one LAN user's logon name per mac address. I can already limit these LAN users to say 2 or 3 logins shells thru '/etc/security/limits.conf'.
My goal here: Only allow one LAN user per mac address. I do not want 2 or more users on the same local computer terminal logging in!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

I thought about trying, if I can, using 'w' and/or 'who', etc.(maybe rhosts) to cat to a file. Then maybe work something from there, checking for the same mac address from different users. Run this script every 15 or 20 secs killing the processes that have both or more users trying this type of logon from a single terminal.

Thanks again, ahead on this one!!!

win32sux 04-10-2009 02:36 PM

Maybe look into connlimit.

tuxhats 04-11-2009 08:24 AM

Thank you! 'connlimit' pretty much does what '/etc/security/limits.conf' does.

I made this small pseudo code showing my goal. Hope this helps!

pseudo code:

Initiate this script every 15 - 20 secs or so through cron.

Gather LAN users' $info(username, mac or ipaddr, PID) -> OBJECT: Tie each username to a mac or ipaddr,
Write $info(3 pieces) for each logged-on user to a $file,
Open this $file for reading,
while not EOF,
Compare each line of $info(ipaddr or mac) looking for an ipaddr or mac match/duplication,
if a ipaddr or mac match, check to see if the $username is the same
if the $username DO NOT match
Provide a warning to these $usernames(on the same ipaddr/mac),
Kill both process of these $usernames(on the same ipaddr/mac).
rm $file
End

Thank you in advance for your help!!!

blackhole54 04-12-2009 02:06 AM

Just to clarify ... The reason connlimit does not meet your goals is you wish to allow a user on a particular machine (IP address) to run multiple instances of ssh to your server?

If you want to script, you can use ps -wwfC sshd (or other options as desired) to see each instance of who is logged on via ssh and kill to kill sshd processes. I have no idea how you can notify the offending users first.

tuxhats 04-12-2009 08:05 AM

Thanks for the reply blackhole 54! Yes is the answer, see below objective. 'ps -wwfC sshd' doesn't show ip addresses.

Unfortunately, I'm not a shell-script coder (compiled languages mostly).

ENVIRONMENT:
In fact, I am the Comp. Sci. teacher of about 140 HS students. I am running Ubuntu LTS 8.01.2. I allow my students 2 ssh logins ( /etc/security/limits.conf ). This way they can see previous work they have coded, highlight a selection, paste it in the other shell(new work). Why re-invent the wheel for every lesson?

OBJECTIVE:
What I am trying to avoid is two(2) different users sharing work via the same process!!! If two different users are using the same terminal, shut them down! I must shut this "cheating" process down!!!!

Thanks again!!!

blackhole54 04-13-2009 01:15 AM

Quote:

Originally Posted by tuxhats (Post 3506329)
Yes is the answer, see below objective. 'ps -wwfC sshd' doesn't show ip addresses.

Ah, yes. When I was originally thinking this this through I had netstat -natp in the mix, but then managed to convince myself it was redundant. That is where you can correlate PID (of sshd) with IP address. The -p option of netstat (when run as root) will show PID of each process. Assuming sshd is using priviledge separation, what you would need to do is use PPID (rather than PID) from the ps command to find the corresponding "foreign address" from netstat.

Quote:

Unfortunately, I'm not a shell-script coder (compiled languages mostly).
My first reaction was that if you can program C you should have no problem with bash. But on second thought I realized that while you would have no trouble with the logic, there are some idioms to get used to. But I am still resisting thinking hard enough about this to give you a serious start on a script. So, now that I understand the objective, I have another suggestion. What if you usage connlimit to limit one ssh session per IP address and have the student start screen. (You could even have ~/.bash_profile start it automatically for them.) The problem with this is that screen allows (I believe) up to 10 terminal sessions rather than limiting to 2. But is that really a problem? (If it is, a script to kill "excessive sessions" -- and perhaps report the offender -- would be easier than a script to track IP/user combinations.)

There is an issue however. But this would still be an issue even w/o using screen. And that is you need to make sure students cannot run su. Otherwise:

Code:

su -l coconspirator
will allow two students from one machine. If you do decide to use screen (or even not block it if it is already on the server) you need to be aware that there is a mechanism for "sharing" a screen session. While I know that exists, I have never played/worked with it.

If you still think you need the IP/username script, I might be able to put in a little time to sketch an outline for you. But probably not enough to hand you a complete, tested script.

Quote:

In fact, I am the Comp. Sci. teacher of about 140 HS students.
Boggles my aging mind! I've read about such things in HS, but still can't quite get my mind around it. When I was a senior in HS, the local "University" (which was smaller than my HS) allowed members of the math club to come out after school and use their IBM 1130 when they weren't using it for school administration. We punched up Fortran programs on punch cards and put them in the card reader!


EDIT: I had your problem gnawing at the back of my mind and realized that a script wouldn't be quite as complicated as I had first thought. Below I've sketched out what such a script might look like. With debug=true, I've verified that script doesn't blow up and at least sort of works. But don't treat it as tested. (Verifying the script has been left as an excercise for the original poster. ;) ) In fact, as I was posting this I noticed one oops, which I have noted in the comments. But if you limit the connections per IP address to two using connlimit then I think that oops won't be a problem. There is also the theoretical possibility that between the time which PIDs to kill have been determined and the actual kill commands are executed, an original (offending) process has already ended and a new one with the same PID has been created. In which case the wrong process would be killed.

To have the script actually kill processes rather than talk about it, change debug=true to debug=false.

Code:

#!/bin/bash

#  Script must be run as root!
#  With $debug true, list processes that would be killed, but don't kill anything.

#  Script to kill sshd processes if there is more than one user per IP address.
#  This script ignores all sshd processes owned by root.  I.e. no process
#  owned by root will be killed.

#  (Note that $PPID is a reserved variable name for bash.  However it
#  is OK for us to use $ppid. :-)


debug=true

temp=$(mktemp /tmp/someoldname.XXXXXXXXXXXX) || exit
temp2=$(mktemp /tmp/someoldname.XXXXXXXXXXXX) || exit
temp3=$(mktemp /tmp/someoldname.XXXXXXXXXXXX) || exit

ps -wwfC sshd | grep sshd | grep -v root > $temp
netstat -natp | grep ESTABLISHED | grep sshd > $temp2

while read user pid ppid rest; do
  echo -n "$user $pid " >> $temp3
  grep "[^[:digit:]]$ppid/sshd" $temp2 | awk '{print $5}' | \
      sed "s/:.*//" >> $temp3
done < $temp

sort -k3 $temp3 > $temp2

# $temp2 now contains lines consisting of $user $pid and $IPaddress
# sorted by $IPaddress.

if [ "$debug" == true ]; then
  echo -e "Table of (in order) IP address, username, PID\n"
  awk '{printf "%s \t%s \t%s\n", $3, $1, $2}' $temp2
  echo
fi

#  Add any PID where there is more than one user for a given $ip to $pid_list
#  (OOPS!  the logic in this loop breaks down if there are more than two sshd's
#  for a given IP address. :-(  What needs to be done is to create an $ip_list
#  and derive $pid_list from that.

while read user pid ip; do
  [ -z "$ip" ] && continue
  if [ $ip == "$last_ip" -a "$user" != "$last_user" ]; then
      echo "$pid_list" | grep -q $pid || pid_list="$pid_list $pid"
      echo "$pid_list" | grep -q $last_pid || pid_list="$pid_list $last_pid"
  fi
  last_ip=$ip
  last_pid=$pid
  last_user=$user
done < $temp2

#  Depending on $debug, either list what would be killed or actually kill
#  the PIDs in $pid_list.  (The first kill politely asks processes to shut
#  down.  Second kill is not so nice about it.)  Note that in the kill
#  commands, $pid_list should *not* be quoted.

if [ "$debug" == true ]; then
  pid_list=$(echo "$pid_list" | sed "s/ / |^[^ ]* */g; s/^ *|//")
  echo "egrep filter:  \"$pid_list\""
  echo
  echo "If \$debug had not been true, the following processes "
  echo -e "would have been killed:\n"
  ps -wwfC dummy | head -n 1
  [ -n "$pid_list" ] && egrep "$pid_list" $temp
elif [ -n "$pid_list" ]; then
  kill $pid_list &> /dev/null
  sleep 5
  kill -9 $pid_list &> /dev/null
fi

rm -f $temp $temp2 $temp3



All times are GMT -5. The time now is 08:36 PM.