The following is for the case where you want to run a command without password only if it has a specific set of options, where a part of the options is variable. AFAIK it is not possible to use variables or value ranges in sudoers declarations, i.e. you can allow access explicitly to command option1
but not command option2
using:
user_name ALL=(root) /usr/bin/command option1
but if the structure is command option1 value1
, where value1
can vary, you would need to have explicit sudoers lines for each possible value of value1
. Shell script provides a way around it.
This answer was inspired by M. Ahmad Zafar's answer and fixes the security issue there.
- Create a shell script where you call the command without
sudo
.
- Save the script in a root-privileged folder (e.g.
/usr/local/bin/
), make the file root-owned (e.g. chown root:wheel /usr/local/bin/script_name
) with no write access for others (e.g. chmod 755 /usr/local/bin/script_name
).
Add the exception to sudoers using visudo:
user_name ALL=(root) NOPASSWD: /usr/local/bin/script_name
.
Run your script sudo script_name
.
For example, I want to change display sleep timeout on macOS. This is done using:
sudo pmset displaysleep time_in_minutes
I consider changing the sleep timeout an innocent action that doesn't justify the hassle of password typing, but pmset
can do many things and I'd like to keep these other things behind the sudo password.
So I have the following script at /usr/local/bin/ds
:
#!/bin/bash
if [ $# -eq 0 ]; then
echo 'To set displaysleep time, run "sudo ds [sleep_time_in_minutes]"'
else
if [[ $1 =~ ^([0-9]|[1-9][0-9]|1[0-7][0-9]|180)$ ]]; then
pmset displaysleep $1
else
echo 'Time must be 0..180, where 0 = never, 1..180 = number of minutes'
fi
fi
At the end of sudoers
file I have the following line:
user_name ALL=(root) NOPASSWD: /usr/local/bin/ds
To set the timeout at 3 minutes, I run my script from the ordinary user account user_name
:
sudo ds 3
PS Most of my script is input validation, which is not mandatory, so the following would also work:
#!/bin/bash
pmset displaysleep $1