How do I programmatically implement super user privileges in a script? - python

I've written a python script. One of the functions opens a port to listen on. To open a port to listen on I need to do it as super user. I don't want to run the script with sudo or with root permissions, etc. I saw an answer here regarding sub-process using sudo. It's not a sub-process I want as far as I know. It's just a function within the application.
Question: How do I programmatically open a port with super user permissions?

You can't do that. If you could then malicious code would have free access to any system as root at any time!
If you want super user privileges you need to run the script from the root account or use sudo and type in the password - this is the whole point of having user accounts.
EDIT
It is worth noting that you can run bash commands from within a python script - for example using the subprocess module.
import subprocess
subprocess.run(['sudo', 'blah'])
This essentially creates a new bash process to run the given command.
If you do this your user will be prompted to enter their password in the same way as you would expect, and the privileges will only apply to the subprocess that is being created - not to the script that you are calling it from (which may have been the original question).

You could use sudo inside your python script like this, so you don't have to run the script with sudo or as root.
import subprocess
subprocess.call(["sudo", "cat", "/etc/shadow"])

Related

How to properly assign cap_net_raw to a Python script

I have a Python script which periodically pings machines in the network by using aioping to do it.
It requires raw socket access for the ICMP messages and one way to do this is to run the script as root, which I don't want to do.
What I've resorted to is the following:
sudo setcap cap_net_raw+ep /usr/bin/python3.8
python aio_pinger.py
where among the first lines in aio_pinger.py I call the following in order to remove cap_net_raw from the binary, but let the script still have the permissions:
import os
os.system('sudo setcap cap_net_raw-ep /usr/bin/python3.8')
This works.
The issue is that in this case, even if the script is no longer run as root, it still has root access via os.system. I need to run it as a user which can't use sudo.
When using protocols like TCP or UDP for listening, I can use something like authbind to grant normal users access to privileged ports. Is something like this possible with ICMP?
Ideally I'd just issue a command like authicmp python aio_pinger.py where I do some configuration beforehand.
An additional note: python in this case is python3 from a virtual environment. I've tried to sudo setcap cap_net_raw+ep venv/p3-pinger-2022-03-01/bin/python3, but this fails with the message
Failed to set capabilities on file
`venv/p3-pinger-2022-03-01/bin/python3' (Invalid argument)
The value of
the capability argument is not permitted for a file. Or the file is
not a regular (non-symlink) file
Is my only option to copy the python executable from /usr/bin/python3.8 into venv/p3-pinger-2022-03-01/bin/ and use setcap on that executable there, or is there some proper way of doing this?

using dbus and polkit to run a root privilege python service that calls a root script

I have written a python script that downloads deb files from launchpad, and then calls out to a shell script to use alien to convert the debs to rpms.
The shell script uses alien, and so it needs to be run as root.
The program uses a thread pool to download the deb files asynchronously, using threadpool.apply_async, and then uses a processing pool to call the shell script asynchronously, so the whole thing happens reasonably quickly.
It all works well, but the shell script that calls alien needs to be root otherwise packages don't get built properly. When I first finished the script, I would use pkexec to call alien, after using sudo. In both cases, I had a couple of problems.
The first was that in starting in root, I lost the environment of the user, and so lost the pip installed python libraries. I could, perhaps, have used sudo -s or similar, but the second problem was that I had to enter my root password for every package that was built.
What I want to do, is to run the python script, qt gui and all, as a normal user, select which files to convert, and then hit the install button and only enter my superuser password once.
I decided to filter out the install parts of the python, which include the threaded download, and threaded call to the shell script, and then try and run those parts as root/superuser.
I created a dbus service, for this install part, and, after a steep dbus learning curve, managed to get the service working. However, I had no joy getting the script authenticated, and raising its privileges.
I have been able to use polkit to show the password dialog and authorise the super user, but I do not know how to use the return value from polkit
`authority.CheckAuthorization(subject, action_id, details, flags, cancellation_id)`
which shows the password dialog, for authorisation, but does not handle elevating the scripts privileges.
I have set the python install service as 0500 perms, so that hopefully, once I have figured out how to elevate privileges, the root user has the ability to read and execute the service, which is currently created on the session bus.
How can I elevate permissions, and, at the same time, keep the environment variables of the user, so that I don't have to install python modules into the root account?
Many thanks for your help in advance...
ps. I have written a polkit action file, and a polkit rule, but in each case I am not sure how the action id relates to the elevation of privileges.
pps. Can I/should I use pam?
I eventually ran the process as root, using pkexec to obtain a password dialog.

Creating raw socket in Python without root privileges

Is it possible to create a raw socket without root privileges? If not, can a script elevate its privileges itself?
I wrote a Python script using a raw socket:
#!/usr/bin/env python
import socket
rawSocket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))
print "Worked!"
Running it with root privileges prints Worked!. However, it gives an error when run with normal user privileges.
I want to execute my script as a normal user, and it should create a raw socket without asking for anything. Is it possible?
As you noted raw sockets require higher privilege than a regular user have.
You can circumvent this issue in two ways:
Activating the SUID bit for the file with a command like chmod +s file and set its owner to root with chown root.root file. This will run your script as root, regardless of the effective user that executed it. Of course this could be dangerous if your script has some flaw.
Setting the CAP_NET_RAW capability on the given file with a command like setcap cap_net_raw+ep file. This will give it only the privileges required to open a raw socket and nothing else.
EDIT:
As pointed out by #Netch the given solutions will not work with any interpreted language (like Python). You will need some "hack" to make it work. Try googling for "Python SUID", you should find something.
There is not a way for an unprivileged process (Python or otherwise) to elevate their own privileges. It's kind of the cornerstone of having this whole privileged/unprivileged users thinga-ma-jig. In regards to raw sockets, from manual page raw(7):
Only processes with an effective user ID of 0 or the CAP_NET_RAW capability are allowed to open raw sockets.
User ID of 0 means root. See here for info about raw sockets on linux.
As pointed out in Faust's answer/comments you won't be able to directly set the CAP_NET_RAW capability for your python program, due to it being a script that gets executed by the Python interpreter, but there may be solutions out on the web that can get around this limitation.
You can also go another path and set up the application using the sudoers file, then run it via sudo
Or my preferred option is just to compile it with Nuitka, then it runs like any other executable and can be assigned run as root etc

python change privileges to restart external application

Hi I have a python script which modifies an application configuration file. To applied this, I need to restart the application. To do that I call a init.d file. But I need to be root when I do this action otherwise the application cannot bind her on the port. Also I dont want execute all my python script with the root's privileges. How can I execute the restart with the root privileges and then remove them.
I set the user permission at the beginning with:
if __name__ == "__main__":
uid = pwd.getpwnam('ubuntu')[2]
os.setuid(uid)
app.run(host='0.0.0.0', port=5001, debug=True)
and at the end of my script I need to execute:
commands.getoutput('/etc/init.d/webapplication restart')
webapplication binds on the port 80.
If I execute my script with this configuration, webapplication cant start and return a message, "cannot bind socket on the 80".
Any idea? to have a clean solution to execute only one external command with the root privileges on a Debian server under a python script?
Thansk in advance.
P.S: I have tried to use the same method like in my main function and I have replaced the user "ubuntu" by "root" but it's not work.
You can use either of two approaches:
Create a program whose only job is to run the init.d script, and make it setuid root. (Make sure to write it securely!). The main script runs with ordinary user permissions and calls the runner.
There's no way for a program to escalate its own privileges (except by running sudo, which is at least as expensive as approach 1), but a program running as root can de-escalate itself. So you can do some steps as root and then continue as the normal user by setting your uid down to the real uid. This won't be any help if you need root privileges for the last thing the program does, though.
Finaly to reach my goal, I have use the sudo solution. To do that on a debian server:
apt-get install sudo
Edit: /etc/sudoers
Add line: my_user(uses for the setuid) ALL = NOPASSWD : /etc/init.d/webapplication
And in my python script:
commands.getoutput('sudo /etc/init.d/webapplication restart')
And that works.

How can you automate terminal commands?

I'm tired of doing this.
ssh me#somehost.com
input my password
sudo su - someuser
input my password
cd /some/working/directory
<run some commands>
Is there anyway to automate this? Do I need a special shell? or a shell emulator? can I programmatically drive the shell up to certain point then run manual commands on it?
Bonus points of it's programmed in python for extra hacking goodness
edit: All the answers below focus on the "full automation" part of the question: Where the hard part is what I highlighted above. Here is another example to see if I can capture the essence.
ssh me#somehost.com
<get a shell because keys are setup>
sudo su - user_that_deploys_the_app
<input password, because we don't want to give passwordless sudo to developers>
cd env; source bin/activate
cd /path/where/ur/app/is/staging
<edit some files, restart the server, edit some more, check the logs, etc.>
exit the term
For the ssh/authentication piece, you can setup passwordless authentication by using keys. Then you can simply use ssh and a bash script to execute a series of commands in an automated fashion.
You could use Python here, but if you are executing a series of shell commands, it's probably a better idea to use a shell script, as that's precisely what they do.
Alternately, look into Fabric for your automation needs. It's Python-based, and your "recipes" are written in Python.
I'm not quite sure what you're asking, but what you're probably asking about is getting SSH working in password-less mode using public keys. The general idea is you generate an SSH keypair:
ssh-keygen -t rsa
which gives you id_rsa and id_rsa.pub. You append the contents of id_rsa.pub to the ~/.ssh/authorized_keys file of your target user, and SSH from that point on will not ask for credentials. In your example, this will work out to:
Only once
# On your source machine
ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub
# Copy this to clip board
# On somehost.com
su - someuser
# edit ~/.ssh/authorized_keys and paste what you had copied from previous step
From now on, you can now just run
ssh someuser#somehost.com "sh -c 'cd /some/dir; command.sh'"
and not be prompted for credentials.
fabric is a fine choice, as others have pointed out. there is also pexpect which might be more what you're looking for.
You can play with autoexpect. It creates expect script (script language intended to handle interaction with user). Run
autoexpect ssh me#somehost.com
followed by rest of commands. Script script.exp will be created.
Please note, that exact results of input and output will be recorded by the script. If output may differ from execution to execution, you'll need to modify a bit generated script.
As Daniel pointed out you need to have a secure way of doing ssh and sudo on the boxes. Those items are universal to dealing with linux/unix boxes. Once you've tackled that you can use fabric. It's a python based tool to do automation.
You can set stuff up in your ~/.ssh/config
For example:
Host somehost
User test
See ssh_config(5) for more info.
Next, you can generate a SSH key using ssh-keygen(1), run ssh-agent(1), and use that for authentication.
If you want to run a command on a remote machine, you can just use something like:
$ ssh somehost "sh myscript.sh ${myparameter}".
I hope this at least points you in the right direction :)
If you need sudo access, then there are obvious potential security issues though ... You can use ChrootDirectory on a per user basis inside a Match block though. See sshd_config(5) for info.
try module paramiko. This can meet your requirement.

Categories

Resources