2

I am setting up a captive portal on a Raspberry Pi (latest distro CLI) where my web application will change NFTables rules based on who is logged in. I have a LEMP stack set up; with Laravel 8 as the PHP framework.

Nginx/php user is www-data, this user has a sudoers file setup with www-data ALL=(ALL) NOPASSWD:/var/www/vportal.getvs.net/app/Python/vportal.py

The following ls -al for the python script: -rwxr-xr-x 1 pi www-data 765 Dec 21 11:19 vportal.py

In the Laravel Controller code:

 $process = new Process(['python3','/var/www/vportal.getvs.net/app/Python/vportal.py']); $process->run(); // executes after the command finishes if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } echo $process->getOutput(); 

The Python script:

import subprocess #import os #import pwd #print(pwd.getpwuid(os.getuid()).pw_name) subprocess.run("sudo nft add table nat", shell=True) subprocess.run("sudo nft 'add chain nat postrouting { type nat hook postrouting priority 100; }'", shell=True) subprocess.run("sudo nft add rule ip nat postrouting oifname \"wlan0\" masquerade", shell=True) subprocess.run("sudo nft add table ip filter", shell=True) subprocess.run("sudo nft 'add chain ip filter forward { type filter hook forward priority 0; policy accept; }'", shell=True) subprocess.run("sudo nft add rule ip filter forward iifname \"wlan0\" oifname \"wlx000e3b337325\" ct state related,established accept", shell=True) subprocess.run("sudo nft add rule ip filter forward iifname \"wlx000e3b337325\" oifname \"wlan0\" accept", shell=True) 

The idea is to have a route in Laravel 8 trigger the python script through the process command from Symfony on a particular route with the controller. I can run commands that don't require sudo without issues, but my scripts dont want anything to do with sudo. Is there a way to allow www-data to run a specific script "safely" with sudo privileges?

Note: this is only used on a local network and won't touch the internet. Not that it's any less of a risk that way, but I figured I would at least note this.

4
  • 1
    Fixed! Thanks for the tip!
    – vportal
    CommentedDec 21, 2021 at 16:46
  • Others have already answered the sudo question, but is there any reason why you want to use python to (clumsily) emulate a shell script? Unless your script is doing other stuff that actually requires python, you'd be better off simplifying this to just a shell script. BTW, you'll also need a #! line so that the kernel knows which interpreter to use to run the script (defaults to /bin/sh if unspecified). You want www-data to run your script as root, NOT python with your script as an argument. As @roiama mentioned, you really don't want www-data to be able to run python itself as root.
    – cas
    CommentedDec 22, 2021 at 7:39
  • I suppose it does not really need to be Python at this point. I figured there would not be much of a difference from bash to python, and since I use python for other things I would apply it here. For instance, the project is eventually going to be rather large and I may just need Python for other functionality. For now I am okay with breaking it up into bash scripts, however this does not fix my super user issue since nft requires it with these specific commands. It is being ran from a web application (Laravel) which is initiated on an endpoint hit. (after user is authenticated)
    – vportal
    CommentedJan 2, 2022 at 22:04
  • Write scripts that do what you need, break up those scripts into the parts that need to be run as root (these should be as small and simple as possible) and the parts that don't (everything else). Allow www-data to run some of those scripts as root (and mostly the root parts shouldn't be run directly by the web server, they should be run by wrapper scripts that validate and sanitise their input). Before doing that read up on web security. Understand that trusting user-supplied data is a bad idea.
    – cas
    CommentedJan 3, 2022 at 1:28

2 Answers 2

1

You can grant specific users the ability to run specific commands with sudo using sudoers files that you place under /etc/sudoers.d/.

The format for this that you could use is:

user host=(who to run as) [Options] Command

Note: You should do all of your editing in visudo.

So if you wanted to give the www-data the privilege to run all nft as root you could create the file /etc/sudoers.d/www-data with the following contents:

www-data ALL = (root) nft 

Since this is in a script, you probably do not want to be prompted for a password.

In this case you will want to add the NOPASSWD: option:

www-data ALL = (root) NOPASSWD: nft 

In the event that you only wanted to allow nft add you could do the following:

www-data ALL = (root) NOPASSWD: nft add* 

And in the event that you wanted to allow the www-data user to run more than just one command as root you can comma separate the commands:

www-data ALL = (root) NOPASSWD: nft, ls, cat 

This would allow www-data to sudo nft or ls or cat without a password.

Note: be careful when editing sudoers files. In the event that your syntax is incorrect any sudo command by any user will error out.

You can use visudo -c sudofile to validate the file. So for the example file I've been using visudo -c /etc/sudoers.d/www-data. If that commands outputs www-data: parsed OK then the syntax is correct.

5
  • This is exactly it. In my case nft is under /usr/sbin/nft. So for example the /etc/sudoers.d/www-data file reads www-data ALL = (root) NOPASSWD: /usr/sbin/nft add* Thank you!!
    – vportal
    CommentedDec 21, 2021 at 17:00
  • Perfect! Glad to help.
    – Natolio
    CommentedDec 21, 2021 at 17:01
  • 1
    Additional note for those who stumble across this post: you can use type nft to find your location.
    – vportal
    CommentedDec 21, 2021 at 17:04
  • 4
    sudo nft for the web user could allow someone to open inbound connections to unprotected services. I wouldn't recommend it myselfCommentedDec 21, 2021 at 18:51
  • The best thing is to use a wrapper sanitizing what's really allowed. sudo would call the wrapper and the wrapper would only allow some features (eg, a shell script allowing only a few possibilities present in a case ... esac and never relying on untrusted user input)
    – A.B
    CommentedDec 23, 2021 at 15:30
1

You have a major security flaw in your sudoers file, that allows your web user to run any python file anywhere on the system with root privileges. Consider if that file contained a call to connect bash to a network socket:

www-data ALL=(ALL) NOPASSWD:/var/www/vportal.getvs.net/app/Python/vportal.py, /usr/bin/python3 

Remove the /usr/bin/python3 as soon as you can.

Now you've done that, let's see why the requirement is failing. You've allowed a specific script, vportal.py to run under sudo. But you don't ever call it with sudo; instead, you have several lines in the script itself that execute sudo nft […], and nft is (correctly) not permitted to be run as root by www-data.

Resolve the problem by removing the sudo commands from inside your script, and calling the script itself with sudo. Oh, and ensure that neither the script itself nor any of the parent directories are writable by the www-user account.

1
  • 1
    I went ahead and removed that snippet in the original post, just in case someone would happen across it trying random code and not bother reading the solutions below the first one. Good call on that, I was getting desperate trying to find solutions quickly, and forgot I left that in there! As you've explained, there was no reason for it in the first place. I'm reading up more on the sudeors files and how it all works so that does not happen again, as I suggest anyone who isn't familiar do anyways!
    – vportal
    CommentedDec 21, 2021 at 18:00

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.