uwsgi is running my app as root, but shouldn't be - python

I have a Flask app run via uwsgi being served by nginx, all being controlled by supervisord
I have set my user parameter in /etc/supervisor.conf to user=webdev
and in both ../myapp/uwsgi_app.ini and /etc/uwsgi/emperor.ini, I have set uid=webdev and gid=www-data
Problem is, I am having a permissions issue within my app. with the following print statements in one of my views, I discover that the application is being run as root. This is causing issues in a function call that requires creation of a directory.
All of the following print statements are located inside the Flask view.
print 'group!! {}'.format(os.getegid())
print 'user id!! {}'.format(os.getuid())
print 'user!! {}'.format(os.path.expanduser('~'))
results in...
group!! 1000
user id!! 1000
user!! /root
EDIT: I added the following print statements:
from subprocess import call
print 'here is user',
call('echo $USER', shell=True)
print 'here is home',
call('echo $HOME', shell=True)
This prints
here is user root
here is home /root
in a terminal on the server, I type $ id, I get uid=1000(webdev) gid=1000(webdev) groups=1000(webdev)
Here is the output from $ getent group
root:x:0:
...
webdev:x:1000:
...
www-data:x:1001:nginx
nginx:x:996:nginx
...
Here are some lines from /etc/passwd
webdev:x:1000:1000::/home/webdev:/bin/bash

That's strange, because normally you wouldn't have any permissions issues when running as root (the opposite actually, you'd have more permissions than necessary in this case).
I have the feeling that you might be running the process as webdev and not root after all. Can you try calling os.getuid() instead of os.expanduser()?
The /rootdirectory is often used as a default when there is no home directory set for a user. You can also check your /etc/passwd/ for webdev's entry to see what the home directory is set to.
If you're not running as root, your permissions issue are probably related to something else (maybe webdev isn't the owner of the directory you're writing in?).
EDIT: If you want user webdev to have a proper home directory, run the following as root:
mkdir -p /home/webdev
usermod -m -d /home/webdev webdev
After that, os.expanduser() should display the correct home directory.
EDIT 2: I was wrongly assuming that webdev was not a normal user but just a minimally configured service username like www that you were using. My mistake.
In any case, as I mentioned in the comment, what matters is your uid value. You're not running as root because your uid is not 0. Nothing else matters in UNIX terms.
I think that I figured it out though. The way uWSGI works when you specify the uid & gid options is that it still runs as root originally but immediately calls setuid() to drop its privileges and switch to the uid and gid you provided. This would explain the behavior you're seeing: the environment is still configured for root and even though uWSGI is now running as webdev, $USER and $HOME must be still pointing to root.
You can try to test this by adding this line inside the Flask view:
open('/home/webdev/testfile', 'a').close()
This will create an empty file in webdev's home directory. Now log in afterwards as webdev, go to /home/webdev and do an ls -l. If the owner of testfile is webdev, you're running as webdev.
If you can establish that, then what you'll have to do is write all your code assuming that $HOME and $USER are wrongly set. I'm not sure how it will affect your code, but try, for instance, to avoid relative paths (it is possible assumed that the default destination is your wrong home directory).

Related

"/Library" directory permission denied on Mac - Python3

I'm trying to create a program that copies a directory in the library directory on mac (path : "/Library"). I use shutil which works very well in other directories but not in the Library directory...
I want to be able to compile my program, so I can't run it as root.
Here is my code :
import shutil
def copy(src_path, dir_path):
try:
shutil.copytree(src_path, dir_path)
print("Success!")
except:
print("Impossible to copy the folder...")
print("Failed!")
copy("/Users/marinnagy/Desktop/Test", "Library/Test")
I think it's because the library directory is protected and requires authentication to make changes.
Do I have to make an authentication request to the user ? Or do I need to use another method than shutil ?
Thanks for your help !
After a good deal of research and many attempts, I finally managed to copy a folder into my Library directory.
On macOS, the process of writing to a protected directory like the Library directory is blocked for python program. Once compiled (I use pyinstaller), it seems to be impossible for a python application to access this kind of folder, even if you give the app Full Disk Access in the System Preferences.
So I used some AppleScript to manage this specific copy/paste task :
on run {scr_path, dir_path} # Run with arguments
# Translate standard paths to their quoted form
set formated_scr_path to quoted form of scr_path
set formated_dir_path to quoted form of dir_path
# Run a simple shell script to copy the repertory in the other
do shell script "cp -R " & formated_scr_path & space & formated_dir_path ¬
with administrator privileges # Ask for administrator privileges
end run
Then, in my python program, I call the AppleScript program when I want to copy/past to a protected repertory like the Library repertory :
import subprocess
def copy(scr_path, dir_path):
# Use the osascript process to call the AppleScript
# Give the paths in arguments
process = subprocess.call(['osascript', "path/to/applescript",
scr_path, dir_path])
return process
copy("path/to/folder 1", "path/to/folder 2")
This method worked for me on protected repertories. The AppleScript run in the background and an authentication window pop in, asking the user to identify himself as an admin :
result screenshot

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.

Python subprocess doesn't execute if the path contains home directory tilde ~

I'm trying to detect an error and restart server from django application.
I'm using the following code:
try:
# do something
except:
print('here')
subprocess.call(['/home/my_username/restart.sh'])
restart.sh is as follows
#!/bin/sh
/home/my_username/webapps/app/apache2/bin/restart
/home/my_username/webapps/my_db/bin/cron
I'm using webfaction as hosting provider.
Aboved code prints statement, but doesn't restart the server and doesn't start mysql database which is under my_db.
Maybe I need to supply username/pass? How to do that?
The subprocess.call won't expand the ~ tilde character to your home directory, and therefore it'll look into your current working directory for a folder called ~ and then from there look for your app
You can use the os.path.expanduser function to get the desired behaviour, it'll be something like this:
try:
# do something
except:
print('here')
subprocess.call([os.path.expanduser('~/webapps/app/apache2/bin/restart')])
This script will look for /home/user/webapps/app/apache2/bin/restart and execute it.
Good luck ;)

How to modify the default location where the python script is trying to write the log

There is a python script which I have to run in the background when performing some evaluations. However, the script tries saves some log throughout the process, and since I have to run it in a machine where I don't have sudo rights, the script doesn't get executed.
I think the line which causes the problem is:
parser.add_option("-L", "--logfile", dest="logfile", default=os.path.join(tempfile.gettempdir(), "log-name.log"), help="log messages to LOGFILE [default: %default]", metavar="LOGFILE")
I think I need to change the default location: default=os.path.join(tempfile.gettempdir(), "log-name.log"). How do I make it the output path to be a folder within my home directory. There I would not get any issue related to priviledge escalation.
Extract your home directory from the environment:
from os.path import expanduser
homedir = expanduser("~")
parser.add_option("-L", "--logfile", dest="logfile", default=os.path.join(homedir, "log-name.log"), help="log messages to LOGFILE [default: %default]", metavar="LOGFILE")
Also, you can try adding a timestamp in your logfile's name so you don't overwrite existing logs by an accident.
I suppose you could do something like this:
_OUTPATH = 'Users/username/logs'
...
parser.add_option("-L", "--logfile", dest="logfile", default=os.path.join(_OUTPATH, "log-name.log"), help="log messages to LOGFILE [default: %default]", metavar="LOGFILE")

Drop root privileges for certain operations in Python

In my Python script, I perform a few operations that need root privileges. I also create and write to files that I don't want to be owned exclusively by root but by the user who is running my script.
Usually, I run my script using sudo. Is there a way to do the above?
You can switch between uid's using os.seteuid(). This differs from os.setuid() in that you can go back to getting root privileges when you need them.
For example, run the following as root:
import os
open('file1', 'wc')
# switch to userid 501
os.seteuid(501)
open('file2', 'wc')
# switch back to root
os.seteuid(0)
open('file3', 'wc')
This creates file1 and file3 as root, but file2 as the user with uid 501.
If you want to determine which user is calling your script, sudo sets two environment variables:
SUDO_USER
SUDO_UID
Respectively the username and the uid of the user who called sudo. So you could use int(os.environ['SUDO_UID']) to use with os.seteuid().
http://linux.die.net/man/8/sudo quote:
The real and effective uid and gid are set to match those of the target user
So, your only option of knowing which user to use is to read the target user from either a config file or a cmdline option, or someway of heuristical guessing.
A good idea is the so called rights shedding: Start with root privilegs, then do what you nedd them for. Then become a less privileged user.
You would use the os module for that:
http://docs.python.org/2/library/os.html#os.setuid
I found that using os.seteuid and os.setegid didn't actually drop the root privileges. After calling them I was still able to do things that required root privileges. The solution I found that worked was to use os.setresuid and os.setresgid instead:
sudo_uid = int(os.getenv("SUDO_UID"))
sudo_gid = int(os.getenv("SUDO_GID"))
# drop root privileges
os.setresgid(sudo_gid, sudo_gid, -1)
os.setresuid(sudo_uid, sudo_uid, -1)
subprocess.call("mkdir /foo1", shell = True) # should fail
# regain root privileges
os.setresgid(0, 0, -1)
os.setresuid(0, 0, -1)
subprocess.call("mkdir /foo2", shell = True) # should succeed

Categories