Kill Active SSH Session

Force close active SSH connections after a defined time interval

How to kill an active SSH session after a defined time interval ?   Every Linux admin has used the idle timeout option  in /etc/ssh/sshd_config  given by the two options, ClientAliveInterval  and ClientAliveCountMax . I won’t even bother to explain what those meant and how to use them, the internet is full of examples. But,…


How to kill an active SSH session after a defined time interval ?

 

Every Linux admin has used the idle timeout option  in /etc/ssh/sshd_config  given by the two options, ClientAliveInterval  and ClientAliveCountMax .

I won’t even bother to explain what those meant and how to use them, the internet is full of examples.

But, how about when the user is not idle?

Let’s assume you have the very improbable scenario where you need to limit your users to connect via SSH to a specific duration, e.g. 15 or 30 minutes.

What are the options?

I was surprised, but there’s none already implemented, therefore I started looking for ways of doing this. First thought was to use one of the  following commands, to identify the active sessions:

who
testuser1 pts/0 2018-03-21 06:52 (82.x.y.z)
testuser2 pts/1 2018-03-21 06:52 (82.x.y.z)

A little more information with:

w
06:56:03 up 8 days, 19:32, 2 users, load average: 0.07, 0.04, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
testuser1 pts/0 82.x.y.z 06:52 0.00s 0.27s 0.03s sshd: testuser1 [priv]
testuser2 pts/1 82.x.y.z 06:52 3:06 0.11s 0.05s sshd: testuser2 [priv]

Again, little information using last:

last | grep 'still logged in'
testuser1 pts/1 82.x.y.z Wed Mar 21 06:52 still logged in
testuser2 pts/0 82.x.y.z Wed Mar 21 06:52 still logged in

What to use from any of the above?

Well, useful information for my scenario are:

  • the username
  • the login time
  • the tty.. could be useful, will see that later
  • the [priv] tells us the user is “sudo”-ed ..
  • the I.P. address

What can be done with this information?

We can create a script to calculate the time from the login and kill the username session based on username or IP (or pts?!). So, let’s see what’s possible:

Using the username, you can kill a session by issuing the command pkill -KILL -u $user . If a user has 2 sessions, one started 1 minute ago and the other one 15 minutes ago, killing the 15 minutes session will kill the 1 minutes session too. So.. this was the 1st “no GO”

Same as the above applies for the IP,  so this is the 2nd “no GO”

Using the pts .. there’s no direct command to kill a specific tty session, however, you can use ps to get more information on that specific tty:

ps -ft pts/0 | grep bash | grep -v grep
UID        PID  PPID  C STIME TTY   TIME      CMD   # Added this myself, to understand the output meaning
testuser1 85974 85972 0 06:52 pts/0 00:00:00 -bash

So, we have a PID, an username and session start time (STIME). We can use the above information to calculate the time since the session started, and kill the PID when the time limit is reached.

Is there any other way to achieve the same, in an easier way? Is the above way of interrogating the system correct?

Well, there is! Using the following query:

ps -eo etimes,pid,cmd --sort=etimes | grep '@pts' | grep -v grep
ELAPSED PID CMD                                  # Added this myself, to understand the output meaning
2071 85972 sshd: testuser1@pts/0
2023 86106 sshd: testuser2@pts/1

Elapsed will show you the time passed since the sshd session started, therefore you don’t actually need to calculate this anymore. You also have the PID, the username and the tty.

 

So, let’s put a script together using the above system query

 

Remove “sshd:” and “@” from the output and separate the output in columns:

ps -eo etimes,pid,cmd --sort=etimes | grep '@pts' | grep -v 'grep\|root' | awk -F 'sshd:|@' '{ print $1" "$2" "$3 }'
2513 85972 testuser1 pts/0
2465 86106 testuser2 pts/1

Now, let’s store the information somehow and let’s see how it looks like:

ses_array="$(ps -eo etimes,pid,cmd --sort=etimes | grep '@pts' | grep -v 'grep\|root' | awk -F 'sshd:|@' '{ print $1" "$2" "$3 }')"
printf '%s\n' "${ses_array[*]}"
2513 85972 testuser1 pts/0 
2465 86106 testuser2 pts/1

So, let’s create some variables out of our array elements:

printf '%s\n' "${ses_array[*]}" | while read line; do

    ses_time="$(echo $line | awk '{print $1}')"
    ses_pid="$(echo  $line | awk '{print $2}')"
    ses_user="$(echo $line | awk '{print $3}')"
    ses_tty="$(echo  $line | awk '{print $4}')"

done

So, now we have the 4 elements of each line as variables.

What you can do:

  • send a message to the tty of every user before killing the session, let’s say with 1 min in advance
  • compare the session time with a predefined timeout value and kill the session when the timeout value is reached
  • set a cron to run at specific time intervals (I tested with 1 min time intervals, but is your choice)

Which information is actually essential from everything? Well, just the timeout interval, the session time and the session PID.

So, let’s put all together, using what we actually need:

# !/usr/bin/env bash

# define session timeout in minutes
ses_timeout_min=15

# Doing the math for you and transforming our session timeout value in seconds
let ses_timeout_sec=ses_timeout_min*60

# Create an array with the active sessions details
declare -a ses_array="$(ps -eo etimes,pid,cmd --sort=etimes | grep '@pts' | grep -v 'grep\|root' | awk -F 'sshd:' '{ print $1 }')"

# Iterate through each line of the array
printf '%s\n' "${ses_array[*]}" | while read line; do

    ses_time="$(echo $line | awk '{print $1}')"
    ses_pid="$(echo $line | awk '{print $2}')"

    # If the timeout value we set is less or equal to the session duration, kill the PID
    (( $ses_time>=$ses_timeout_sec )) && kill -9 $ses_pid

done

Now, save your script in a location, e.g. /opt/my_scripts/

mkdir -p /opt/my_scripts/
cd /opt/my_scripts/
vi kill_ssh_active.sh

Paste the content of the script and save with :wq  then run crontab -e  and add the following line:

* * * * * /opt/my_scripts/kill_ssh_active.sh

The above will run your script every minute.

P.S.

For a version of the script that will warn the user ~1 minute before the session being killed, check my GitHub repository.

Enjoy!