2

I want to detect if the user is connected over SSH. In a term, the "env" command shows SSH_CONNECTION line. Accessed in Python in one of two ways:

#python:
import os
print os.getenv("SSH_CONNECTION")       #works
print os.environ.get("SSH_CONNECTION")  #works

But, if the user has ran my program using SUDO (as they will need to), env$ dooesn't show SSH_CONNECTION. So Python can't see it:

#sudo python:
import os
print os.getenv("SSH_CONNECTION")       #not set
print os.environ.get("SSH_CONNECTION")  #not set

The aim is to achieve the following:

#Detect if user is over remote IP
lRemoteIP=""                                     #Is set if user on SSH
lStr=os.environ.get("SSH_CONNECTION")            #Temp var
if lStr: lRemoteIP=lStr.split()[0].split("=")[1] #Store user's lasthop IP

#Later on in the code, for multiple purposes:
if lRemoteIP: pass #Do stuff (or not) depending on if they're on SSH

How do I retrieve SSH_CONNECTION environment variable under SUDO, when its not present in env$ ?

Or more precisely: how can I detect if the current session is via SSH when sudo?

I'm not a natural at Linuxy-type things, so be gentle with me...

[EDIT:] METHOD 2: Giving up on env$, I've tried the following:

pstree -ps $$ | grep "sshd("

If it returns anything then it means that the SSH daemon sits above the session. Ergo, it's a SSH connection. And the results are showing me the PIDs of the SSH daemons. Results of the pstree cmd:

init(1)---sshd(xxx)---sshd(xxx)---sshd(xxx)---bash(xxx)-+-grep(xxx)

But I'm struggling to get a src IP from the PID. Any ideas on this avenue?

[EDIT] METHOD 3: /run/utmp contains details of SSH logins. In python:

import os
import sys

lStr=open("/var/run/utmp").read().replace('\x00','') #Remove all those null values which make things hard to read

#Get the pseudo-session ID (pts) minus the /dev/ that it starts with:
lCurSess=os.ttyname(sys.stdout.fileno()).replace('/dev/','')
#Answer is like pts/10  (pseudo-term session number 10)
#Search lStr for pts/10
lInt=lStr.find(lCurSess.replace('/dev/',''))
#Print /var/utmp starting with where it first mentions current pts:
print lStr[lInt:]

So far, so good. This gives the following results (I've changed the IP and username to USERNAME)

pts/10/10USERNAME\x9e\x958Ym\xb2\x05\x0 74\x14pts/10s/10USERNAME192.168.1.1\xbf\x958Y\xf8\xa3\r\xc0\xa88\x01

So, when it comes to extracting the IP from the file, there's some bumf inbetween the occurances of pts/10 and the IP. What's the best way to parse it, given that (I reckon) the precise distance from the match to the IP will be different under different circumstances?

Vexen Crabtree
  • 339
  • 3
  • 16
  • I've read other articles on similar (I think) issues. https://stackoverflow.com/questions/8633461/how-to-keep-environment-variables-when-using-sudo but the answers don't involve Python, and I don't know enough to actually apply what might be an answer... and the HTTP-GET (etc) stuff doesn't apply. https://stackoverflow.com/questions/8633461/how-to-keep-environment-variables-when-using-sudo is another one that doesn't seem to match my situation... along with a few others. – Vexen Crabtree Jun 07 '17 at 22:12
  • 1
    `sudo -E` should help. – phd Jun 07 '17 at 22:16
  • I've seen another thread mention sudo -E. But, in my Python program, how can I use it to detect the src IP of the (possible) SSH connection? – Vexen Crabtree Jun 07 '17 at 22:17
  • You cannot if sudo clears environment. So first make sudo to keep environment. – phd Jun 07 '17 at 22:18
  • So assuming that there's no access to the full env$ variables, how can I detect if the current session is via SSH when sudo? I am fairly sure my program can't exit-itself, make sure sudo keeps the environment, and then re-run itself. (Unless that's possible... but I doubt it!) – Vexen Crabtree Jun 07 '17 at 22:23
  • If you sudo visudo, you can add SSH_CONNECTION to the env_keep attributes. – Barend Jun 07 '17 at 22:31
  • My program might be being ran on a system where SSH_CONNECTION hasn't been added to env_keep variables. – Vexen Crabtree Jun 07 '17 at 22:35

2 Answers2

2

The OpenSSH daemon writes an entry to /var/run/utmp with the current terminal, the IP and the name of the user. Check the output of the w or who commands that parse /var/run/utmp.

It's just a question of getting the current terminal (similar to the tty command) and extracting the information you want.

Use pyutmp like this:

from pyutmp import UtmpFile
import os
import sys

for utmp in UtmpFile():
    if os.ttyname(sys.stdout.fileno()) == utmp.ut_line:
        print '%s logged from %s on tty %s' % (utmp.ut_user, utmp.ut_host, utmp.ut_line)

Then filter by using ut_pid field to parse the /proc/ut_pid/cmdline file which should contain:

sshd: ut_user [priv]

Ricardo Branco
  • 5,740
  • 1
  • 21
  • 31
  • pyutmp isn't available in the distro I'm using. I've echoed that file... not pleasing to the eye to read manually! I'm working on writing some code to parse the file. If other users have since logged on over SSH since my program started, won't there be additional entries in /var/run/utmp ? I'm presuming that sys.stdout.fileno is the resolution to that problem... investigating. – Vexen Crabtree Jun 07 '17 at 23:19
  • pyutmp is just a `pip install pyutmp` install away... You may use sys.stderr.fileno or sys.stdin.fileno as well. But it all depends on the chosen one not being redirected to another file. – Ricardo Branco Jun 07 '17 at 23:21
  • Can't install away. What's not available will remain not available, as my program will be run in a distro that doesn't include pyutmp, and I don't have permissions to go install anything at all. – Vexen Crabtree Jun 07 '17 at 23:28
  • Currently I'm working on using this type of thing: getting all the lines from open("/var/run/utmp","r").readlines(), iterating, doing if fLine=os.ttyname(sys.stdout.fileno()) : IP=.... stuff... but when doing .read() on the file, it's in some utf-format that I'm struggling to decode. .decode('utf-16-le') isn't working... – Vexen Crabtree Jun 07 '17 at 23:31
  • I see; a bit too complicated to properly dig into with plain eyes. However... in Python when I run os.ttyname(sys.stdout.fileno()) I get the answer of '/dev/pts/8' ... is this specific to the current session, which might be over an SSH connection? If so, then in /var/run/utmp there is a bit which starts with [pts/8ts/XXXXXXlocalhost2Z8Y ... (XXXX replaces my username)... is it translating 127.0.0.1 into "localhost" for me? Or is the 2Z8Y the bytes of the remote IP... investigating... – Vexen Crabtree Jun 07 '17 at 23:43
  • 2Z8Y doesn't obviously translate into the correct IP using any sensible encoding system for a 4-byte IPv4. Hmpf. – Vexen Crabtree Jun 07 '17 at 23:46
0

GOT IT AT LAST!!!

The "last" command has list of users and their IPs!! So simple.

It has "still logged in" marked against sessions. Filter by these And then filter by current pts ID

To get the IP for the current SSH session in Python, do this:

import os,sys,subprocess
(out, err) = subprocess.Popen(['last | grep "still logged in" | grep "' + os.ttyname(sys.stdout.fileno()).replace('/dev/','') + '"'], stdout=subprocess.PIPE, shell=True).communicate()
RemoteIP=out.split()[2].replace(":0.0","") #Returns "" if not SSH

For readability, across multiple lines:

import os,sys,subprocess
pseudoTermID = os.ttyname(sys.stdout.fileno()).replace('/dev/','')
cmdStr       = 'last | grep "still logged in" | grep "'+pseudoTermID+'"'
sp           = subprocess.Popen([cmdStr], stdout=subprocess.PIPE, shell=True)
(out, err)   = sp.communicate()
RemoteIP     = out.split()[2].replace(":0.0","")   #Returns "" if not SSH
Vexen Crabtree
  • 339
  • 3
  • 16