Replicating ssh behavior with jupyternotebook spawn - python

OBS1: this question is duplicated here as suggested by Wayne in the comments, but still with no answer.
I have a remote machine running ubuntu where i am configuring a jupyterhub notebooks server. The server is already up and running, however, i noticed that it only works well with users that have previously logged in the machine via ssh.
For users that have never logged in the machine via ssh before, the server spawns a login screen but after the login comes the following image:
It displayed a different directory path before (i mean different than /user/john.snow), but i configured the jupyterhub spawner class to make the directory by adding the lines:
if os.path.exists('mkdir /home/FOLDER/' + env['JUPYTERHUB_USER'])!=True:
os.system('mkdir /home/FOLDER/' + env['JUPYTERHUB_USER'])
(i append the complete spawner code at the end of the question, if thats useful)
Since i dont intend to need to test every single directory that jupyter notebook looks for, my desire is to find the ssh configuration files in the computer and mimic what ssh does for that particular user with the spawner.
Is it possible? I tried looking at /etc/ssh/ssh_config and similar but almost all of the file is commented and the syntax is mysterious.
Thanks for any suggestions.
OBS: full spawner code:
import os, getpass
import yaml
from jupyterhub.spawner import Spawner, LocalProcessSpawner
class spawner(LocalProcessSpawner):
def start(self):
# get environment variables,
# several of which are required for configuring the single-user server
env = self.get_env()
ret = super(spawner, self).start()
if os.path.exists('mkdir /home/FOLDER/' + env['JUPYTERHUB_USER'])!=True:
os.system('mkdir /home/FOLDER/' + env['JUPYTERHUB_USER'])
os.system('mkdir /home/FOLDER/' + env['JUPYTERHUB_USER'] + '/notebooks')
os.system('cp -r /usr/local/scripts/notebooks/* /home/FOLDER/' + env['JUPYTERHUB_USER'] + '/notebooks/')
os.system('chmod -R 777 /home/FOLDER/' + env['JUPYTERHUB_USER'] + '/notebooks/')
return ret

I found a solution to the problem. Since the spawner code was trying to access folders created by an ssh into the machine, the lines
if os.path.exists('mkdir /home/FOLDER/' + env['JUPYTERHUB_USER'])!=True:
os.system('mkdir /home/FOLDER/' + env['JUPYTERHUB_USER'])
were trying to create this folder if it didnt exist. However, there were other mysterious configurations that ssh generated and i couldnt figure out to replicate. Instead, i found out that ssh configuration files are at /etc/skel, so i removed these two lines from the spawner and, instead, added:
os.system('su ' + env['JUPYTERHUB_USER'])
os.system('source /etc/skel/.bashrc')
os.system('source /etc/skel/.profile')
os.system('exit')
the 'su env["JUPYTERHUB_USER"]' and 'exit' lines being there because the spawner seems to be executed as root. It solved for new users, but old users who had already spawned the red bar were still dealing with it. It seems that deleting their home folders in the machine solved the problem.

Related

NRPE Python script output bug

I have been tasked with making a custom python script (since i'm bad with Bash) to run on a remote NRPE client which recursively counts the number of files in the /tmp directory. This is my script:
#!/usr/bin/python3.5
import os
import subprocess
import sys
file_count = sum([len(files) for r, d, files in os.walk("/tmp")]) #Recursive check of /tmp
if file_count < 1000:
x = subprocess.Popen(['echo', 'OK -', str(file_count), 'files in /tmp.'], stdout=subproce$
print(x.communicate()[0].decode("utf-8")) #Converts from byteobj to str
# subprocess.run('exit 0', shell=True, check=True) #Service OK - exit 0
sys.exit(0)
elif 1000 <= file_count < 1500:
x = subprocess.Popen(['echo', 'WARNING -', str(file_count), 'files in /tmp.'], stdout=sub$
print(x.communicate()[0].decode("utf-8")) #Converts from byteobj to str
sys.exit(1)
else:
x = subprocess.Popen(['echo', 'CRITICAL -', str(file_count), 'files in /tmp.'], stdout=su$
print(x.communicate()[0].decode("utf-8")) #Converts from byteobj to str
sys.exit(2)
EDIT 1: I tried hardcoding file_count to 1300 and I got a WARNING: 1300 files in /tmp. It appears the issue is solely in the nagios server's ability to read files in the client machine's /tmp.
What I have done:
I have the script in the directory with the rest of the scripts.
I have edited /usr/local/nagios/etc/nrpe.cfg on the client machine with the following line:
command[check_tmp]=/usr/local/nagios/libexec/check_tmp.py
I have edited this /usr/local/nagios/etc/servers/testserver.cfg file on the nagios server as follows:
define service {
use generic-service
host_name wp-proxy
service_description Files in /tmp
check_command check_nrpe!check_tmp
}
The output:
correct output is: OK - 3 files in /tmp
When I run the script on the client machine as root, I got a correct output
When I run the script on the client machine as the nagios user, I get a correct output
My output on the Nagios core APPEARS to be working, but it shows there are 0 files in /tmp when I know there are more. I made 2 files on the client machine and 1 file on the nagios server.
The server output for reference:
https://puu.sh/BioHW/838ba84c3e.png
(Ignore the bottom server, any issues solved with the wp-proxy will also be changed on the wpreess-gkanc1)
EDIT 2: I ran the following on the nagios server:
/usr/local/nagios/libexec/check_nrpe -H 192.168.1.59 -c check_tmp_folder
I indeed got a 0 file return. I still don't know how this can be fixed, however.
systemd service file, maybe this var is set to true :)
PrivateTmp= Takes a boolean argument. If true, sets up a new file system namespace for the executed processes and mounts private /tmp and /var/tmp directories inside it that is not shared by processes outside of the namespace.
This is useful to secure access to temporary files of the process, but makes sharing between processes via /tmp or /var/tmp impossible. If this is enabled, all temporary files created by a service in these directories will be removed after the service is stopped. Defaults to false. It is possible to run two or more units within the same private /tmp and /var/tmp namespace by using the JoinsNamespaceOf= directive, see systemd.unit(5) for details.
This setting is implied if DynamicUser= is set. For this setting the same restrictions regarding mount propagation and privileges apply as for ReadOnlyPaths= and related calls, see above. Enabling this setting has the side effect of adding Requires= and After= dependencies on all mount units necessary to access /tmp and /var/tmp.
Moreover an implicitly After= ordering on systemd-tmpfiles-setup.service(8) is added. Note that the implementation of this setting might be impossible (for example if mount namespaces are not available), and the unit should be written in a way that does not solely rely on this setting for security.
SOLVED!
Solution:
Go to your systemd file for nrpe. Mine was found here:
/lib/systemd/system/nrpe.service
If not there, run:
find / -name "nrpe.service"
and ignore all system.slice results
Open the file with vi/nano
Find a line which says PrivateTmp= (usually second to last line)
If it is set to true, set it to false
Save and exit the file and run the following 2 commands:
daemon-reload
restart nrpe.service
Problem solved.
Short explanation: The main reason for that issue is, that with debian 9.x, some processes which use systemd forced the private tmp directories by default. So if you have any other programs which have issues searching or indexing in /tmp, this solution can be tailored to fit.

PermissionError: [Errno 13] Permission denied

I'm getting this error :
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1538, in __call__
return self.func(*args)
File "C:/Users/Marc/Documents/Programmation/Python/Llamachat/Llamachat/Llamachat.py", line 32, in download
with open(place_to_save, 'wb') as file:
PermissionError: [Errno 13] Permission denied: '/goodbye.txt'
When running this :
def download():
# get selected line index
index = films_list.curselection()[0]
# get the line's text
selected_text = films_list.get(index)
directory = filedialog.askdirectory(parent=root,
title="Choose where to save your movie")
place_to_save = directory + '/' + selected_text
print(directory, selected_text, place_to_save)
with open(place_to_save, 'wb') as file:
connect.retrbinary('RETR ' + selected_text, file.write)
tk.messagebox.showwarning('File downloaded',
'Your movie has been successfully downloaded!'
'\nAnd saved where you asked us to save it!!')
Can someone tell me what I am doing wrong?
Specs :
Python 3.4.4 x86
Windows 10 x64
This happens if you are trying to open a file, but your path is a folder.
This can happen easily by mistake.
To defend against that, use:
import os
path = r"my/path/to/file.txt"
assert os.path.isfile(path)
with open(path, "r") as f:
pass
The assertion will fail if the path is actually of a folder.
There are basically three main methods of achieving administrator execution privileges on Windows.
Running as admin from cmd.exe
Creating a shortcut to execute the file with elevated privileges
Changing the permissions on the python executable (Not recommended)
A) Running cmd.exe as and admin
Since in Windows there is no sudo command you have to run the terminal (cmd.exe) as an administrator to achieve to level of permissions equivalent to sudo. You can do this two ways:
Manually
Find cmd.exe in C:\Windows\system32
Right-click on it
Select Run as Administrator
It will then open the command prompt in the directory C:\Windows\system32
Travel to your project directory
Run your program
Via key shortcuts
Press the windows key (between alt and ctrl usually) + X.
A small pop-up list containing various administrator tasks will appear.
Select Command Prompt (Admin)
Travel to your project directory
Run your program
By doing that you are running as Admin so this problem should not persist
B) Creating shortcut with elevated privileges
Create a shortcut for python.exe
Righ-click the shortcut and select Properties
Change the shortcut target into something like "C:\path_to\python.exe" C:\path_to\your_script.py"
Click "advanced" in the property panel of the shortcut, and click the option "run as administrator"
Answer contributed by delphifirst in this question
C) Changing the permissions on the python executable (Not recommended)
This is a possibility but I highly discourage you from doing so.
It just involves finding the python executable and setting it to run as administrator every time. Can and probably will cause problems with things like file creation (they will be admin only) or possibly modules that require NOT being an admin to run.
Make sure the file you are trying to write is closed first.
Change the permissions of the directory you want to save to so that all users have read and write permissions.
You can run CMD as Administrator and change the permission of the directory using cacls.exe. For example:
cacls.exe c: /t /e /g everyone:F # means everyone can totally control the C: disc
In my case the problem was that I hid the file (The file had hidden atribute): How to deal with the problem in python:
Edit: highlight the unsafe methods, thank you d33tah
# Use the method nr 1, nr 2 is vulnerable
# 1
# and just to let you know there is also this way
# so you don't need to import os
import subprocess
subprocess.check_call(["attrib", "-H", _path])
# Below one is unsafe meaning that if you don't control the filePath variable
# there is a possibility to make it so that a malicious code would be executed
import os
# This is how to hide the file
os.system(f"attrib +h {filePath}")
file_ = open(filePath, "wb")
>>> PermissionError <<<
# and this is how to show it again making the file writable again:
os.system(f"attrib -h {filePath}")
file_ = open(filePath, "wb")
# This works
I had a similar problem. I thought it might be with the system. But, using shutil.copytree() from the shutil module solved the problem for me!
The problem could be in the path of the file you want to open. Try and print the path and see if it is fine
I had a similar problem
def scrap(soup,filenm):
htm=(soup.prettify().replace("https://","")).replace("http://","")
if ".php" in filenm or ".aspx" in filenm or ".jsp" in filenm:
filenm=filenm.split("?")[0]
filenm=("{}.html").format(filenm)
print("Converted a file into html that was not compatible")
if ".aspx" in htm:
htm=htm.replace(".aspx",".aspx.html")
print("[process]...conversion fron aspx")
if ".jsp" in htm:
htm=htm.replace(".jsp",".jsp.html")
print("[process]..conversion from jsp")
if ".php" in htm:
htm=htm.replace(".php",".php.html")
print("[process]..conversion from php")
output=open("data/"+filenm,"w",encoding="utf-8")
output.write(htm)
output.close()
print("{} bits of data written".format(len(htm)))
but after adding this code:
nofilenametxt=filenm.split('/')
nofilenametxt=nofilenametxt[len(nofilenametxt)-1]
if (len(nofilenametxt)==0):
filenm=("{}index.html").format(filenm)
It Worked perfectly
in my case. i just make the .idlerc directory hidden.
so, all i had do is to that directory and make recent-files.lst unhidden after that, the problem was solved
I got this error as I was running a program to write to a file I had opened. After I closed the file and reran the program, the program ran without errors and worked as expected.
I faced a similar problem. I am using Anaconda on windows and I resolved it as follows:
1) search for "Anaconda prompt" from the start menu
2) Right click and select "Run as administrator"
3) The follow the installation steps...
This takes care of the permission issues
Here is how I encountered the error:
import os
path = input("Input file path: ")
name, ext = os.path.basename(path).rsplit('.', 1)
dire = os.path.dirname(path)
with open(f"{dire}\\{name} temp.{ext}", 'wb') as file:
pass
It works great if the user inputs a file path with more than one element, like
C:\\Users\\Name\\Desktop\\Folder
But I thought that it would work with an input like
file.txt
as long as file.txt is in the same directory of the python file. But nope, it gave me that error, and I realized that the correct input should've been
.\\file.txt
As #gulzar said, I had the problem to write a file 'abc.txt' in my python script which was located in Z:\project\test.py:
with open('abc.txt', 'w') as file:
file.write("TEST123")
Every time I ran a script in fact it wanted to create a file in my C drive instead Z!
So I only specified full path with filename in:
with open('Z:\\project\\abc.txt', 'w') as file: ...
and it worked fine. I didn't have to add any permission nor change anything in windows.
That's a tricky one, because the error message lures you away from where the problem is.
When you see "__init__.py" of an imported module at the root of an permission error, you have a naming conflict. I bed a bottle of Rum, that there is "from tkinter import *" at the top of the file. Inside of TKinter, there is the name of a variable, a class or a function which is already in use anywhere else in the script.
Other symptoms would be:
The error is prompted immediately after the script is run.
The script might have worked well in previous Python versions.
User Mixon's long epos about administrator execution privileges has no impact at all. There would be no access errors to the files mentioned in the code from the console or other pieces of software.
Solution:
Change the import line to "import tkinter" and add the namespace to tkinter methods in the code.
Two easy steps to follow:
Close the document which is used in your script if it's open in your PC
Run Spyder from the Windows menu as "Run as administrator"
Error resolved.
This error actually also comes when using keras.preprocessing.image so for example:
img = keras.preprocessing.image.load_img(folder_path, target_size=image_size)
will throw the permission error. Strangely enough though, the problem is solved if you first import the library: from keras.preprocessing import image and only then use it. Like so:
img = image.load_img(img_path, target_size=(180,180))

Python - Relative Paths in os.system calls

I am building a python utility which automates sysadmin type tasks. Part of the tool involves writing scripts and then calling them with powershell from the python interface. An example of such code is this:
def remote_ps_session():
target = raw_input("Enter your target hostname: ")
print "Creating target.ps1 file to establish connection"
pstarget = open("pstarget.ps1", "w")
pstarget.write("$target = New-Pssession " + target + "\n")
pstarget.write("Enter-PSSession $target" + "\n")
pstarget.close()
print "File created. Initiating Connection to remote host..."
os.system("powershell -noexit -ExecutionPolicy Unrestricted " + "C:\path\to\my\file\pstarget.ps1")
I would like to do two things which I think can be answered with the same method, I've just yet to find out what is best (importing vs variables vs initial setup definitions and so on)
For simplicity we'll say the utility is in C:\utility and the powershell functions are in a functions folder one level deeper: C:\utility\functions
I want to be able to specify a location for 1) where the script (the file that is written) is saved to and then 2) refer to that location when the os.system call is made. I want this to be able to run on most/any modern Windows system.
My thoughts on possibilities are:
When the script launches get the current directory and save that as a variable, if I need to go back a directory take that variable and remove everything after the last \ and so on. Doesn't seem ideal.
On the first launch of the file prompt for system locations to put in variables. For instance have it prompt 'where do you want your log files?' 'where do you want your output files?' 'where do you want your generated scripts?' These could then be referred to as variables but would break if they ever moved folders and may not be easy to 'fix' for a user.
I imagine there is some way to refer to current directories and navigate to ..\parallel folder to where I am executing from. ....\2 folders up, but that also seems like it might be messy. I've yet to see what a standard/best practice for managing this is.
Edit: based on some comments I think __file__ might be the place to start looking. I'm going to dig in to this some but any examples (for example: __file__/subfoldernameor whatever the usage would be would be cool.
Python has a lib dedicated to path manipulation os.path, so anytime you need filesystem paths manipulation take a look at it.
As for your particular questions, run the following example, to see how you can use the functions from this lib:
test.py
import os
# These two should basicly be the same,
# but `realpath` resolves symlinks
this_file_absolute_path = os.path.abspath(__file__)
this_file_absolute_path1 = os.path.realpath(__file__)
print(this_file_absolute_path)
print(this_file_absolute_path1)
this_files_directory_absolute_path = os.path.dirname(this_file_absolute_path)
print(this_files_directory_absolute_path)
other_script_file_relative_path = "functions/some.ps"
print(other_script_file_relative_path)
other_script_file_absolute_path = os.path.join(this_files_directory_absolute_path,
other_script_file_relative_path)
print(other_script_file_absolute_path)
print("powershell -noexit -ExecutionPolicy Unrestricted %s" %
other_script_file_absolute_path)
You should get output similar to this:
/proj/test_folder/test.py
/home/user/projects/test_folder/test.py
/proj/test_folder
functions/some.ps
/proj/test_folder/functions/some.ps
powershell -noexit -ExecutionPolicy Unrestricted /proj/test_folder/functions/some.ps

Python 2.6 pxssh Password GUI spawned on login failure

Before I get into the details, I have already attempted the solution from this question with no success.
I am trying to use the pxssh module within pexpect to SSH to a remote system and collect the uptime data. However, whenever I enter the incorrect password, the script generates a GUI password prompt for openSSH.
The script will query a large number of systems and write the output to a log, so I want it to just write a note in the log whenever the SSH connection fails; I don't want it to ever pop up a window.
Here is the current code of the function:
def getUptime(ipAddr, passwd):
try:
os.unsetenv('SSH_ASKPASS')
ssh = px.pxssh.pxssh()
ssh.options={"NumberOfPasswordPrompts":"1"}
ssh.force_password=True
ssh.options = {'RequestTTY':'Force'}
ssh.login(ipAddr,"root",passwd,auto_prompt_reset=False)
ssh.sendline("uptime")
ssh.prompt()
uptime = ssh.before.split()
ssh.logout()
uptime = ' '.join(uptime[uptime.index('up'):]).split(',')[0].strip('up')
return(uptime)
except(px.pxssh.ExceptionPxssh):
return("SSH connection failed; double-check the password")
I apologize if there are typos in that code, I had to re-type it by hand since Virtualbox is not letting me copy and paste from my development environment (SLES 11 x64, if that matters), even with bidirectional clipboard enabled.
I have tried the following steps (some of which are visible above):
Set ssh.force_password=True
Set ssh.options = {'RequestTTY':'Force'} (This should be equivalent to using "-t" in the SSH CLI argument, if I understand the openSSH documentation)
Set auto_prompt_reset=False
Edit pxssh.py to set "ssh_options" from '' to '-t -x' (I tried each option separately, then both together).
Manually unset SSH_ASKPASS in the shell before running the script.
Unset SSH_ASKPASS with os.unsetenv() immediately before invoking pxssh within the script.
Set the "NumberOfPasswordPrompts" option to "1".
The observed behavior when it gets an incorrect password is that it will print "SSH connection failed..." as expected, but it will also popup the openSSH password window.
None of those listed steps has had any noticeable effect on the script's performance whatsoever, and most of my Google searches on this issue point back to that linked question. If anyone has any idea of how I can possibly 100% suppress that password window, I would very much appreciate the assistance.
Thank you all in advance for your advice.
Potential Solution
I've found that I can keep the window from popping up by taking the following steps:
In pxssh.py change ssh_options from '' to "-o 'NumberOfPasswordPrompts=1'"
In my script, catch pexpect.EOF along with pexpect.pxssh.ExceptionPxssh to determine when a password has failed.
I'm not accepting this as an answer just yet because it seems like a very poor solution to me, particularly editing pxssh.py. I attempted to set the NumberOfPasswordPrompts option within my script, but it has no effect, so I must have the incorrect syntax.
You don't need to update pxssh.py at all. Instead:
ssh = px.pxssh.pxssh()
ssh.force_password = True
ssh.SSH_OPTS = "-o 'NumberOfPasswordPrompts=1'"
I had this problem quite a while ago, see if this helps. Python PXSSH GUI spawn on login failure
If not then trying opening the pxsssh module in you python library. If I remember correctly theres an issue that's explained in the __init__ where all you need to do is comment a line and uncomment a line.
Edit:
In the pexpect package theres the pxssh.py file, open it up and look at line 115 there should be your fix. Heres what your looking for
path: /usr/lib/python2.7/dist-packages/pexpect/pxssh.py
# Comment this line
self.SSH_OPTS = ("-o'RSAAuthentication=no'"
+ " -o 'PubkeyAuthentication=no'")
# Disabling host key checking, makes you vulnerable to MITM attacks.
# + " -o 'StrictHostKeyChecking=no'"
# + " -o 'UserKnownHostsFile /dev/null' ")
# Disabling X11 forwarding gets rid of the annoying SSH_ASKPASS from
# displaying a GUI password dialog. I have not figured out how to
# disable only SSH_ASKPASS without also disabling X11 forwarding.
# Unsetting SSH_ASKPASS on the remote side doesn't disable it! Annoying!
# UNCOMMENT THE LINE BELOW
#self.SSH_OPTS = "-x -o'RSAAuthentication=no' -o 'PubkeyAuthentication=no'"
self.force_password = False
self.auto_prompt_reset = True
I haven't used the pxssh module in quite sometime. But I think you can find away to override the SSH_OPTS so that you don't need to physically modify the module.

python execute system commands (windows)

So I have this uber script which constantly checks the system path for a program (openvpn). When you install openvpn it adds itself to the system path. I run my script in the console and, while it runs and checks, I install openvpn. In that console my script will never find openvpn in sys path. If I open a new console and run the same script it finds it.
Any idea how I can make my script a little less dumb?
import os
import time
import subprocess
def cmd( command ):
return subprocess.check_output( command, shell = True )
def program_in_path( program ):
path = cmd( "path" ).split(";")
for p in path:
if "openvpn" in p.lower():
return True
return False
if __name__ == '__main__':
while True:
print program_in_path("openvpn")
time.sleep( 2 )
I presume it's from the shell = True thing but how else would I find it if not with path or WHERE openvpn /Q ? Running with no sehll I get WindowsError: [Error 2] The system cannot find the file specified
Here's slightly the same program done in ruby which works 100%:
loop do
puts system( "WHERE openvpn /Q" )
sleep( 5 )
end
Unfortunately my project is too deep into python to switch languages now. Too bad.
It's actually because when your program starts, it has an environment configured. Part of that environment is the system path. When you start a subshell, it inherits the environment of the parent process.
I'm not a Windows programmer, and I don't have a Windows machine available to test on right now. But according to that bug report, if you import nt in your script and reload(nt) in your while True loop that it will pull down a fresh copy of the environment from the system. I don't know whether that's true or not. It might be worth a try.
For what it's worth, you can see the same behavior from the cmd window by, for instance, opening a command window, adding a program folder to the System Path, and then trying to run an exe from that program folder in your existing cmd window. It won't work -- but open a new cmd window, and it will.
The bug report you cite is about a different problem. That problem outlined there is that from within Python, if you load in one of the system DLLs and use a particular function Windows provides for manipulating your environment, Python does not reflect the change. However, if you make a change to os.environ, Python recognizes that change. The conclusion from the community was that the particular function that the reporter was using, was not the correct function to use to get the results he expected.
Perhaps this approach works for you, getting the PATH variable straight from the registry (since you're on Windows).
For instance you could do something like this:
import winreg
def PathFromReg():
loc = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
key = winreg.OpenKey(reg, loc)
n_val = winreg.QueryInfoKey(key)[1]
for i in range(n_val):
val = winreg.EnumValue(key, i)
if val[0] == 'Path':
return val[1]
path = PathFromReg()
print('openvpn' in path.lower())
I think you only need to assign the key once and then query the values inside the loop.
Note: In Python 2 the module is called _winreg.

Categories