-1

My usecase is that I have a script that removes my user from sudo group and then it adds me again on a given point in future time using the at command.

The problem is that I have to reboot the machine every time in order for the changes to take effect, the first time to stop being the sudo user and the second time (once the script added me again to sudo group) in order to be it again.

Strangely my DE (Cinnamon) and file explorer (Nemo) pick up the changes immediately, meaning that if I try to go to a fodler and "open as root", I can't do that if I runned the script and I'me removed from sudo group. But if I go on terminal and for example I do sudo apt update this will work, unless I reboot.

The goal is to make effectively reflect the removal/adding my user to sudo group without needing to reboot my machine. (I say reboot and not log out because, strangely, logging out apparently will not work).

I've searched online and one solution was to run:

sudo service sudo restart

But the problem is that I receive the error: Failed to restart sudo.service: Unit sudo.service is masked. I've tried to unmask sudo.service but nothing changed.

Info: I'm on Linux Mint 22 with Cinnamon 6.2.9. Linux kernel: 6.8.0-48-generic

Edit: Goal of the whole script I'm making a fork of a project called delayed-admin. What this does is basically removing the main user from sudo group so that you can't perform anything that requires a sudo access. There are then only two ways to execute a command that requires sudo:

  1. Waiting for the at scheduled functions that put back the user into sudo group (because when you launch the command with sudo ./abdicate.sh "now + 3 hours" you're locked out of sudo for 3 hours.
  2. Using the command sudo delayed example-command this way the command example-command will be executed even if you're not in sudo group, but it'll be exectuted with a certain delay choosen by the user trough a config file.

What I want to achieve: to being able to lose/gain sudo privileges when the script removes/adds my user to sudo group, without rebooting my machine.

1 Answers1

1

Why you need to log in anew

Your /etc/sudoers most likely contains a line like this:

%sudo   ALL=(ALL:ALL) ALL

It is this line what allows users from the sudo group to run commands with sudo. When you run sudo and it checks if you are in the sudo group, it does not consult the user/group database, it checks if its own process carries this information. Once you are in a shell (or whatever) that is in the sudo group, all its children (including sudo you run) will be in the group. There are ways to start a new process with changed or updated information about groups, but this has to be a deliberate action (like logging out and in again, or rebooting).

This is by design. By changing the user/group database you cannot make already running processes change their assignment to groups (and thus lose or gain sudo access) immediately.

Below there is an attempt to solve your underlying problem.


Another approach

This is an attempt to build a mechanics that will allow chosen users to temporarily lose their otherwise unrestricted sudo access. Experimental, barely tested. Use at your own risk.

  1. Create abdicate-until with the following content:

    #!/bin/sh
    

    dir='/etc/abdicate/sudoers.d'

    die () { >&2 printf '%s: %s Aborting.\n' "$0" "$1"; exit "$2"; }

    if [ -z "$SUDO_USER" ] || [ "$SUDO_UID" -eq 0 ] || [ "$(id -u)" -ne 0 ]; then die 'Please run this script as root by invoking `sudo '"$0' as a regular user." 1 fi

    dte="$(IFS=' '; date -d "$*" +%Y%m%d%H%M%S%z)" || die 'Invalid time specification.' 2 mkdir -p -- "$dir" || die 'Cannot create `'"$dir." 3 fil="$dir/$SUDO_USER"

    <<EOF cat >"$fil"~ && mv "$fil"~ "$fil" || die 'Unable to update `'"$fil'." 4 $SUDO_USER ALL=(ALL) /full/path/to/delayed $SUDO_USER ALL=(ALL:ALL) NOTBEFORE=$dte ALL EOF

    printf 'User %s lost sudo access until %s.\n' "$SUDO_USER" "$dte" || true

    And make it executable (chmod +x …).

    Notes

    • The script contains /full/path/to/delayed, edit it accordingly.
    • The script uses date -d, it was tested with GNU date. Make sure date you can run with sudo understands -d now and such (run sudo date -d now and see if it succeeds).
  2. I assume the user you want this functionality for is currently in the sudo group and it can run commands with sudo. As the user run:

    sudo path/to/abdicate-until now
    

    to create the relevant directory structure and a file that will later tell sudo you want to "abdicate" until now (so effectively not at all, yet).

  3. Invoke sudo visudo and add this line at the very end:

    @includedir /etc/abdicate/sudoers.d
    

    Save changes and exit the editor. If visudo tells you there is a problem, do not let it actually change the sudoers file. When prompted to review the file inside /etc/abdicate/sudoers.d, do not change this file. Proceed to the next step only if visudo accepts the change.

    Old versions of sudo do not recognize @includedir, for them use #includedir instead. (And I think even older versions of sudo do not recognize NOTBEFORE= which is absolutely crucial in our mechanics; if your sudo is that old, abort: do not change sudoers, remove /etc/abdicate/ and abdicate-until as they are not going to work, do not proceed with this answer.)

  4. Remove the user from the sudo group. The user can do this by itself by invoking:

    sudo gpasswd --delete "$USER" sudo
    

    As you already know this change requires logging in anew or a similar act. In the current shell you can still use sudo because of %sudo ALL=(ALL:ALL) ALL in the sudoers file. I advise you not to exit this shell in case there is something wrong and our mechanics does not work well for you. From this shell you will be able to gpasswd --add "$USER" sudo. Exit this shell only after verifying the mechanics works.

  5. In a separate shell you need to log in as the user anew. If this shell already belongs to the right user but id (not id "$USER") shows you it is still in the sudo group then you can do:

    exec su - "$USER"
    

    to replace the shell with a new shell where id should show you it's not in the sudo group (verify this). This is where you will test the solution.

  6. In the new shell run:

    sudo path/to/abdicate-until +2 minutes
    

    You don't need to quote +2 minutes. Our script will pass +2 and minutes to date -d as one argument anyway: +2 minutes. Use whatever syntax your date expects after -d (strictly: date available in $PATH imposed by sudo; it may or may not be the same date as in your $PATH).

    If the command succeeds then the user will not be able to use sudo in the next two minutes, except sudo delayed …, or except if there are other entries in sudoers that allow the user to use sudo. Remember sudo uses the last match. Now your jobs are:

    • Review your sudoers and make sure there are no lines that allow the user to execute sudo … he or she shall not be able to execute when in "abdicated" state. sudo -l run as the user may be useful.
    • Create /full/path/to/delayed on your own (or use the one you already have maybe). It's a separate functionality. This answer does not implement delayed, it only sets up sudoers so "abdicated" user can still run it with sudo.
  7. After verifying the mechanics works for the user, you can now close the old shell, log out, reboot or whatever. Note that until you log out completely (e.g. by rebooting), there are your old processes that still cary the old information you are in the sudo group, they may spawn children that will inherit the information. To really start using the mechanics you need to log out completely at least once.

The mechanics does not use at at all. Regaining access to sudo works thanks to sudo itself comparing the date and time to the value stored in the file inside /etc/abdicate/sudoers.d/. The user may check the value by invoking sudo -l.

The mechanics supports multiple users. Another user with sudo access may start using the mechanics by running:

sudo path/to/abdicate-until now \
&& sudo gpasswd --delete "$USER" sudo

and logging out completely, so there is no process that "remembers" his or her "sudo membership".

To revert (obviously while not in the "abdicated" state):

sudo gpasswd --add "$USER" sudo \
&& sudo rm /etc/abdicate/sudoers.d/"$USER"

After this, the user needs to log in anew to really be in the sudo group. Removing the file has consequences immediately. Removing the file without re-adding the user to the sudo group first will most likely lock the user out from sudo access; root's password (if any), another sudoer, pkexec, booting in single-user mode, booting another OS (e.g. live Linux from USB) or using another computer and mounting the disk there will be required to fix this.


How it works

Suppose a user runs something with sudo:

sudo something …

As stated, when consulting sudoers, sudo uses the last match. With @includedir /etc/abdicate/sudoers.d at the very and of the sudoers file, for a user who has a file in /etc/abdicate/sudoers.d the last line that has a chance to match will be the last line of this file (lines in other files in the directory will not match the username anyway). The line will be like:

<username> ALL=(ALL:ALL) NOTBEFORE=<timestamp> ALL

If the time is not before the <timestamp> then the line will match and it will act like %sudo ALL=(ALL:ALL) ALL acts for a user in the sudo group. For the considered user the power of ALL=(ALL:ALL) ALL is now due to this last line, not due to the %sudo … line.

But if the time is before the <timestamp> then the last match will be in some other line or there will be no match at all. We have removed the user from the sudo group to make the %sudo … line not match in this case. The user will not get the power of ALL=(ALL:ALL) ALL (unless a line not related to this answer matches and gives the power; this is why you should review your sudoers).