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.
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).
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).
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.)
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.
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.
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.
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).