In one of my last articles I needed to enforce the execution of a command with certain arguments – specific for the present user. I.e., I wanted to take away the freedom of the user to set arbitrary command argument(s) as he/she liked.
This had to be done in addition to another set of rules – namely a bunch of iptables filter rules which also depended on the UID. So the command had to be run with the UID of the user him/herself and not with the UID of root.
The solution came with sudo. This may appear a bit surprising to some readers. The reason is that sudo normally is used to allow selected users to run commands with another UID than the one they have themselves. I call “the other user” the “effective user” below. In a sudo context the effective user corresponds to a SUDO_UID variable in addition to the UID environment variable. The predominant example for invoking the sudo-mechanism certainly is to allow users to run a command as root. But it can be extended to any other user (made harmless by taking away hsi/her login shell or being especially privileged due to membership in a special group).
In my case I needed to enforce command execution with an effective user being identical to the original user him/herself – but with special arguments. In such a situation you have to take take the permission to execute/read the original command completely away from the user. Otherwise he/she could use it with any argument. But sudo requires that the defined effective user is able to read and execute the command. This seemingly contradictory situation can be solved by invoking a special user-group.
Maybe the recipe described below helps some readers to enforce command execution with specific arguments in other contexts.
A simple scenario
Let us assume you have developed a program “myprog” which accesses special web-service that you have installed on some web-servers in your Intranet. Let us further assume that some specific users shall be restricted to access the service on a defined server only – and there only with certain arguments. Such parameters may reduce or give rights to access certain data the service could in principle provide. All this is regulated by 2 arguments to the myprog-program: a FQDN for the host and a “level”. “level 0” allows free access to a very basic service version. “level 1” invokes the program with personalized options and requires a login. But your people have started to play around with the Login. So, you want a group of users to issue the command with a certain “host” and “level 0” only. Let us assume that user “mark” is one of those users who should invoke the command only in the form
mark@mytux:~>myprog -h myserv.anraconc.de -L 0
How can we achieve this with sudo?
Restricting command use to specific arguments
Below I discuss modifications of the “/etc/sudoer” file. This is risky in very many ways – not only regarding security.
Disclaimer: I take no responsibility whatever for the consequences of the sudo approach described below and its application to your computers. The sudoer rules have to be tested carefully before the are used in a production environment and their setup must be supervised by an expert.
I assume that you have installed your program at the path “/usr/bin/myprog” with standard rights
-rwxr-xr-x 1 root root 334336 17. Mai 2020 /usr/bin/myprog
Then one can follow the following recipe (as root) to get “mark” under control:
- Step 1: Create a special group for the command in question, e.g. “mygroup”. Ensure that mark does not become a member of this group.
- Step 2: Change the ownership and access rights of “/
usr/bin/myprog” according to
- chown root.mygroup /usr/bin/myprog
- chmod 750 /usr/bin/myprog
- Step 3: Add some lines to “/etc/sudoers” (with visudo):
.... Defaults env_reset Defaults env_keep = "LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE LC_ATIME LC_ALL LANGUAGE LINGUAS XDG_SESSION_COOKIE" Defaults:mark env_keep += "DISPLAY" #Defaults targetpw # ask for the password of the effective target user e.g. root #ALL ALL=(ALL) ALL mark ALL=(mark:mygroup) /usr/bin/myprog -h mysrv.anraconc.de -L 0 ....
Due to step 2 user “mark” cannot read, change or execute the command directly any longer. The rest depends a bit on ensuring the “mark” never becomes a member of group “mygroup”. (But other users which you trust may become members.)
Regarding the sudoer rules I assumed that you reset the environment of a sudo user as a default. Keeping up the “DISPLAY” variable helps to get around some access problems with the present X11-screen of “mark”. I also assumed that you use the sudo-mechanism in a way which requests that the user enters his/her password. The last line allows “mark” on all hosts/terminals to execute “/usr/bin/myprog” as him/herself, but with the GID of group “mygroup” and exactly with the options “-h mysrv.anraconc.de -L 0”.
Note that sudo compares the command including arguments as one string!
User “mark” must run the command myprog from now on in the form:
sudo -u mark -g mygroup myprog -h myserv.anraconc.de -L 0
and enter his password. Any deviation will be blocked by the sudoer mechanism.
Some practical hints
After carefully evaluating security implications you can make life easier for “mark” in two ways:
- Let him execute the command (with the defined arguments) without providing a password. Use the NOPASSWD attribute; see the man pages for the sudoers file.
- Write a script which encapsulates the described sudo command with the options.
By such measures, you may save “mark” some typing time.
Another point is to keep the file permissions up in the future. This may become a problem if and when you apply the described mechanism to some standard Linux commands which are installed and updated by some package administration tool of your distribution. You have to carefully check that the installation routines do not overwrite the permission settings! A handwritten systemd service or a cron job may help you with this task.
With some reading or experience it should be easy to extend the described recipe to groups of users and to other commands.
There are multiple ways to allow other users to execute the command freely if this should be required. The sudoer file knows about a logical NOT operator (!); this helps to add a sudoer rule for all users but NOT “mark”. Another simple approach would be to add all users but “mark” to the group “mygroup”.
The sudoer-mechanism is a mighty Linux tool. We can not only allow users to execute commands as another user, but also with the permissions of another group. AND we can enforce the usage of commands with predefined arguments for selected users or user groups.
As fiddling with the sudoer mechanism is always a bit risky: Please, write me a mail if you find some major mistake or security problem of my approach.