14

The other day I was doing some maintenance tasks on my web server. I was in hurry and sleepy, so I did everything using sudo command.

And then, I accidentally pressed Ctrl+V, sending this command to my web server:

sudo rm -rf /*

For those wondering what above command does: This deleted my whole web server

Luckily, I had backups and sadly, I had to spend two more hours being awake to fix this awesome error. But since then, I have been wondering:

Is there a way to always enforce sudo password for specific command?

If the server asked me for a password, I would save myself from lot of trouble. It did not, because I ran about 5 sudo commands before this majestic error.

So, is there a way to do it? I just need the password with the rm command to always be enforced. Other commands I am using are usually nano or cp which both are (to some extent) revertable.

Dan
  • 13,119
  • 1
  • I thought --preserve-root would prevent that ? But it seems to work only for rm -rf / and rm -rf /* ?? – solsTiCe May 11 '18 at 11:38
  • 2
    @solsTiCe /* is expanded before it is passed to the rm command. So the command doesn't see a single argument, but a list of arguments (/bin /boot /cdrom /dev /etc /home ...) – Dan May 11 '18 at 11:41
  • 3
    One important lesson to take away from this is that you shouldn't run things with sudo unless you really need to. I know that doesn't answer what you asked here, but the overarching question is how to (hopefully) prevent yourself from deleting your server again, and avoiding sudo should be your first line of defense against that. – David Z May 11 '18 at 11:52
  • 2
  • 2
    @WinEunuuchs2Unix That is not a duplicate question at all, the op here doesn't want the rm command to have a password. They just want sudo to prompt for one every time the rm command is used. The solution to the question you linked would become slightly annoying when your first command is sudo rm. As it will ask you for two passwords, one for sudo one for rm. – Dan May 11 '18 at 13:14
  • I strongly second David Z: never use sudo without need. Even if you reckon a command might need sudo, better first run it without, and only if it doesn't work hit HOME and add the sudo. Furthermore, also never use rm -rf unless you've determined for sure it is needed. Stuff that might need deletion should normally not be write-protected. – leftaroundabout May 11 '18 at 16:18
  • @leftaroundabout Even easier with history expansion: To run the previous command line with a prefixed sudo, just execute sudo !!. – dessert May 11 '18 at 23:12
  • su -c also always asks for a password. – Mr Lister May 12 '18 at 13:35
  • @MrLister Yes, but the root account is usually disabled (has no password) in Ubuntu and you cannot su to that account. – PerlDuck May 12 '18 at 15:44
  • Another thing is to question why you are using -f (--force)? On the very rare occasion when I feel that I need to use that option, I first run rm without --force, and only then, if needed, with it. Any prompts highlight unexpected problems. I also have the habit of using the long options particularly on potentially destructive commands, which forces me to think twice about what I'm doing. – Paddy Landau May 14 '18 at 15:24

4 Answers4

19

You can set the timestamp_timeout to 0 for particular commands in /etc/sudoers. Create a file visudo -f /etc/sudoers.d/pduck with the following content:

Cmnd_Alias DANGEROUS = /bin/rm

Defaults!DANGEROUS timestamp_timeout=0

pduck ALL = (ALL:ALL) DANGEROUS

Now the user pduck is always asked for a password when running sudo rm (no matter what additional parameters are given) even though the user is member of the sudo group and sudo remembers his password for other commands.

The downside is that you cannot easily add parameters to the /bin/rm line in the file to further restrict this. Well… you can, like:

Cmnd_Alias DANGEROUS = /bin/rm -f

but then you just get prompted for exactly sudo rm -f and not (again) for sudo rm -rf, for example.

PerlDuck
  • 13,335
9

One method would be to use safe-rm. This will ONLY adress the usage of "rm" and preventing specific versions of "rm" to be run. That includes removing your root system but can also be used to prevent removing of system related directories like "/usr/" or "/var/". From the link:

Reventing the accidental deletion of important files

Safe-rm is a safety tool intended to prevent the accidental deletion of important files by replacing /bin/rm with a wrapper, which checks the given arguments against a configurable blacklist of files and directories that should never be removed.

Users who attempt to delete one of these protected files or directories will not be able to do so and will be shown a warning message instead:

$ rm -rf /usr   
Skipping /usr

(Protected paths can be set both at the site and user levels.)

Recovering important files you deleted by mistake can be quite hard. Protect yourself today by installing safe-rm and reduce the likelihood that you will need to contact a data recovery service!

enter image description here

Rinzwind
  • 299,756
4

sudo provides an option -k, --reset-timestamp, see man sudo:

When used in conjunction with a command or an option that may require a password, this option will cause sudo to ignore the user's cached credentials. As a result, sudo will prompt for a password (if one is required by the security policy) and will not update the user's cached credentials.

You could write a simple wrapper for sudo testing for rm -rf /* and running

sudo -k rm -rf /*

instead, e.g. like this:

sudo ()                                                                                                                                                 
{ 
    [[ "$*" == "rm -rf /*" ]] && opt="-k";
    /usr/bin/sudo $opt "$@"
}

Example usage

Testing with echo a here.

$ sudo echo
[sudo] password for dessert: 

$ sudo echo

$ sudo echo a
[sudo] password for dessert: 
a
$ sudo echo a
[sudo] password for dessert: 
a

If you want to be asked every time you run rm in general, you can adapt the above function as follows:

sudo () 
{ 
    [[ "$1" == "rm" ]] && opt="-k";
    /usr/bin/sudo $opt "$@"
}

If you want to combine both general command calls and specific command lines I recommend using case, e.g.:

sudo () 
{ 
    case "$*" in 
        rm*)            opt="-k" ;;
        "mv /home"*)    opt="-k" ;;
        "ln -s /usr/bin/fish /bin/sh") opt="-k" && echo "Don't you dare!" ;;
        *)              opt="" ;;
    esac;
    /usr/bin/sudo $opt "$@"
}

Note that these approaches won't work if you run sudo with options – possible solutions are [[ "$*" =~ " rm " ]] to check for the string “rm” surrounded by spaces or *" rm "*) with case to catch any command line containing “ rm ”.

dessert
  • 39,982
3

This answer doesn't address the sudo part of your question but on the other hand it addresses a way to mitigate the danger of accidental rm invocations for any user.

You can alias rm to rm -I which

  • asks for confirmation as soon as it would delete a directory or more than 3 files
  • as long as you leave out -f which overrides previous -I options.

--one-file-system is another possible safeguard against unintended deletion that I use in my rm alias.

Additionally, you need to ask Bash to expand aliases after sudo with a trailing space in the alias definition (see below).

Set-up

To create such an alias use the command:

alias rm='rm -I --one-file-system'
alias sudo='sudo '

You can put it into your ~/.bashrc or even /etc/bash.bashrc.

Usage

$ sudo rm -r /etc/apt/sources.list.d /*
rm: remove all arguments?

To confirm type yes or its translation into your locale or the first letter of either word and press Enter. Any other input including none aborts the operation.

David Foerster
  • 36,264
  • 56
  • 94
  • 147