I am working with my Raspberry Pi and I am writing a cgi python script that creates a webpage to control my gpio out pins. My script crashes when I try to import RPi.GPIO as GPIO. This is the error that I am getting:
File "./coffee.py", line 7, in <module>
import RPi.GPIO as GPIO
RuntimeError: No access to /dev/mem. Try running as root!
My code works perfectly when I use sudo to run my script, but when I am running from a URL from my apache2 server it says that I do not have access to /dev/mem. I have already tried editing visudo and that did not work. This is what my visudo file looks like:
#includedir /etc/sudoers.d
pi ALL=(ALL) NOPASSWD: ALL
www-data ALL=(root) NOPASSWD: /usr/bin/python3 /usr/lib/cgi-bin/coffee.py *
apache2 ALL = (root) NOPASSWD: /usr/lib/cgi-bin/coffee.py
There any way that I can run my script as root from a URL call? Can anyone tell me what I am doing wrong?
I found that adding www-data to the gpio user group worked fine:
sudo usermod -aG gpio www-data
You can also add www-data to the memory user group:
sudo usermod -aG kmem www-data
As mentioned, it is a bad idea, but for me it was necessary.
Your problem is that the script is not executed as root. It is executed as the user that apache runs as.
Your apache process runs as a specific user, probably www-data
. You could change the user that apache runs as. You should be able to find this in /etc/apache2/envvars
:
# Since there is no sane way to get the parsed apache2 config in scripts, some
# settings are defined via environment variables and then used in apache2ctl,
# /etc/init.d/apache2, /etc/logrotate.d/apache2, etc.
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
If you change that to root
you should have access. Normally this would be a terrible security hole, but you are doing direct memory access already. Be very careful!
If you are uncomfortable with this then you need to update your command so it is executed as root (this is a good way, but it requires you understand what you are doing!). You can do this by altering the way you call it, or by wrapping the call in a script which itself changes the user, or by using setuid (this is very similar to the suEXEC approach mentioned earlier). Wrapping it in a script seems the best way to me, as that should allow your entry in sudoers
to correctly apply the privilieges for only that command, and it doesn't require you to understand the full implications of setuid approaches.