added user_cull
This commit is contained in:
parent
262d10f55d
commit
6cea3c012e
122
sys/user_cull.py
Executable file
122
sys/user_cull.py
Executable file
@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Because:
|
||||
# - SSH timeout doesn't work with byobu/screen/tmux
|
||||
# - SSH timeout can be overridden client-side
|
||||
# - $TMOUT can be overridden user-side
|
||||
# we need to actually kill the sshd process attached to the SSH session.
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import psutil
|
||||
import subprocess
|
||||
|
||||
# in seconds. 5 minutes = 300 seconds.
|
||||
# if "auto", we'll try checking $TMOUT in the system bashrc and sshd_config, in that order.
|
||||
timeout = 'auto'
|
||||
# only apply to ssh connections instead of ssh + local.
|
||||
# THIS WILL KILL SCREEN/TMUX CONNECTIONS. USE WITH CAUTION.
|
||||
only_ssh = True
|
||||
# send a closing message.
|
||||
goodbye = True
|
||||
# the message to send to the user if goodbye == True.
|
||||
# can use the following for substitution:
|
||||
# pid - The PID if the user's login process.
|
||||
# terminal - The terminal they're logged in on.
|
||||
# loginlength - How long they've been logged in (in minutes).
|
||||
# logintime - When they logged in.
|
||||
# timeout - The allowed length of time for inactivity until a timeout.
|
||||
goodbye_mesg = ('You have been logged in for {loginlength} seconds (since {logintime}) on '
|
||||
'{terminal} ({pid}).\n'
|
||||
'However, as per security policy, you have exceeded the allowed idle timeout ({timeout}).\n'
|
||||
'As such, your session will now be terminated. Please feel free to reconnect.')
|
||||
# exclude these usernames
|
||||
exclude_users = []
|
||||
|
||||
|
||||
# Get the SSHD PIDs.
|
||||
ssh_pids = [p for p in psutil.process_iter() if p.name() == 'sshd']
|
||||
# If the timeout is set to auto, try to find it.
|
||||
if timeout == 'auto':
|
||||
import re
|
||||
#tmout_re = re.compile('^\s*#*(export\s*)?TMOUT=([0-9]+).*$')
|
||||
tmout_re = re.compile('^\s*(export\s*)?TMOUT=([0-9]+).*$')
|
||||
# We don't bother with factoring in ClientAliveCountMax.
|
||||
# sshd_re = re.compile('^\s*#*ClientAliveCountMax\s+([0-9+]).*$')
|
||||
sshd_re = re.compile('^\s*ClientAliveInterval\s+([0-9+]).*$')
|
||||
for f in ('/etc/bashrc', '/etc/bash.bashrc'):
|
||||
if not os.path.isfile(f):
|
||||
continue
|
||||
with open(f, 'r') as fh:
|
||||
conf = f.read()
|
||||
for line in conf.splitlines():
|
||||
if tmout_re.search(line):
|
||||
try:
|
||||
timeout = int(tmout_re.sub('\g<2>', line))
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
if not isinstance(timeout, int): # keep going; check sshd_config
|
||||
with open('/etc/ssh/sshd_config', 'r') as f:
|
||||
conf = f.read()
|
||||
for line in conf.splitlines():
|
||||
if sshd_re.search(line):
|
||||
try:
|
||||
timeout = int(tmout_re.sub('\g<1>', line))
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
# Finally, set a default. 5 minutes is sensible.
|
||||
timeout = 300
|
||||
|
||||
|
||||
def kill_user(user):
|
||||
pass
|
||||
|
||||
def get_idle(user):
|
||||
idle_time = None
|
||||
pty = user.terminal
|
||||
for sssn in subprocess.run(['who', '-u'], stdout = subprocess.PIPE).stdout.decode('utf-8').splitlines():
|
||||
session = sssn.split()
|
||||
# This is probably overkill, but.
|
||||
if not all((
|
||||
(session[0] != user.name),
|
||||
(session[1] != user.terminal),
|
||||
(session[5] != user.pid))):
|
||||
continue
|
||||
# https://unix.stackexchange.com/a/332704/284004
|
||||
last_used = datetime.datetime.fromtimestamp(os.stat('/dev/{0}'.format(user.terminal)).st_atime)
|
||||
idle_time = datetime.datetime.utcnow() - last_used
|
||||
break
|
||||
return(idle_time)
|
||||
|
||||
|
||||
for user in psutil.users():
|
||||
if user.name in exclude_users:
|
||||
continue
|
||||
login_pid = user.pid
|
||||
login_length = (datetime.datetime.utcnow() - datetime.datetime.fromtimestamp(user.started))
|
||||
if login_length.total_seconds() < timeout:
|
||||
continue # they haven't even been logged in for long enough yet.
|
||||
idle_time = get_idle(user)
|
||||
if idle_time.total_seconds() >= timeout:
|
||||
fmtd_goodbye = goodbye_mesg.format({'pid': user.pid,
|
||||
'terminal': user.terminal,
|
||||
'loginlength': login_length,
|
||||
'logintime': datetime.datetime.fromtimestamp(user.started),
|
||||
'timeout': timeout})
|
||||
if only_ssh:
|
||||
if user.pid in ssh_pids:
|
||||
if goodbye:
|
||||
subprocess.run(['write',
|
||||
user.name,
|
||||
user.terminal],
|
||||
input = fmtd_goodbye.encode('utf-8'))
|
||||
psutil.Process(user.pid).terminate()
|
||||
else:
|
||||
if goodbye:
|
||||
subprocess.run(['write',
|
||||
user.name,
|
||||
user.terminal],
|
||||
input = fmtd_goodbye.encode('utf-8'))
|
||||
psutil.Process(user.pid).terminate()
|
Loading…
Reference in New Issue
Block a user