bash: how to avoid a command from being used and run - bash

Is there a way to prevent a bash or zsh terminal to run a specific command?
Let's say I want to prevent running by mistake a rm on directory ~/foo. So I want to prevent the following command to be run:
rm -r ~/foo
Another example much broader:
mycustomcommand -param1 foo
In the example above, foo can change, but every time one tries to run:
mycustomcommand -param1 anything
It should block and fail.
Therefore, if I run the above command in my local terminal (or a server, etc) the command is blocked (and even better with an error/warning message).
How can we achieve such behavior?

Linux permissions
Linux has users, groups, file permissions and file attributes to restrict what may be done with the each file and this is the simple and reliable way.
E.g. When file cannot be deleted, even by root (without resetting the attributes), set:
$ sudo chattr +i cannot_delete_me.txt
$ rm cannot_delete_me.txt
rm: cannot remove 'cannot_delete_me.txt': Operation not permitted
To disallow removing the file without a question by non-root account, it is enough to set corret file permissions:
$ chmod a-w cannot_delete_me.txt
$ rm cannot_delete_me.txt
rm: remove write-protected regular empty file 'cannot_delete_me.txt'?
# Now it is possible to delete it when typing y + enter
To disallow deleting a file by non-root account without changing the permissions, change parent directory write permission:
$ mkdir foo && cd foo
$ touch cannot_delete_me.txt
$ chmod a-w .
$ rm cannot_delete_me.txt
rm: cannot remove 'cannot_delete_me.txt': Permission denied
Shadowing the command name
Any command may be called in many ways, different paths, so it is not secure to shadow command name:
$ function rm { echo "I am dummy rm function. I will not remove: $1"; }
$ export -f rm
$ rm foo
I am dummy rm function. I will not remove: foo
/bin/rm foo # It will delete foo
Use Docker
When you don't trust the script you are calling, call it inside the Docker container. It allows binding directories from host system with e.g only read permissions, so you are sure that given script will access things you allowed.
Inside the Docker image, you also may replace unwanted Linux commands.
Let's see the example:
$ mkdir my_script_environment_image && cd my_script_environment_image
Create Dockerfile inside my_script_environment_image directory:
FROM debian:latest
RUN echo '#/bin/bash \n\
echo "This is a dummy rm. I will not remove: $1" \n\
' > /bin/rm
Build the image with defined name:
sudo docker build --tag my_script_environment .
Call a temporary (with --rm) container which will run shell or your script.
sudo docker run -it --rm my_script_environment /bin/bash
root#f6d62a754b38:/# touch my_file.txt
root#f6d62a754b38:/# rm my_file.txt
This is a dummy rm. I will not remove: my_file.txt
So your command has been replaced by custom command and whole Docker environment.
In practice, I'm using Docker to control the scope of script/application effects.

Related

Delete Everything in a Directory Except One FIle - Using SSH

In BASH, the following command removes everything in a directory except one file:
rm -rf !(filename.txt)
However, in SSH the same command changes nothing in the directory and it returns the following error: -jailshell: !: event not found
So, I escaped the ! with \, (the parentheses also require escaping) but it still doesn't work:
rm -rf \!\(filename.txt\)
It returns no error and nothing in the directory changed.
Is it even possible to run this command in SSH? I found a workaround but if this is possible it would expedite things considerably.
I connect to the ssh server using the alias below:
alias devssh="ssh -p 2222 -i ~/.ssh/private_key user#host"
!(filename.txt) is an extglob, a bash future that might have to be enabled. Make sure that your ssh server runs bash and that extglob is enabled:
ssh user#host "bash -O extglob -c 'rm -rf !(filename.txt)'"
Or by using your alias:
devssh "bash -O extglob -c 'rm -rf !(filename.txt)'"
If you are sure that the remote system uses bash by default, you can also drop the bash -c part. But your error message indicates that the ssh server runs jailshell.
ssh user#host 'shopt -s extglob; rm -rf !(filename.txt)'
devssh 'shopt -s extglob; rm -rf !(filename.txt)'
I wouldn't do it that way. I wouldn't rely on bash being on the remote, and I wouldn't rely on any bashisms. I would use:
$ ssh user#host 'rm $(ls | grep -v "^filename.txt$")'
If I wanted to protect against the possibility that the directory might be empty, I'd assign the output of $(...) to a variable, and test it for emptiness. If I was concerned the command might get too long, I'd write the names to a file, and send the grep output to rm with xargs.
If it got too elaborate, I'd copy a script to the remote and execute it.

rm: cannot remove '–rf': No such file or directory

I have a Bash script that automates creating some SVN folders. In the course of doing so, it creates a temporary directory. When I try to delete that temp directory with the rm -rf command, I get the following error...
rm: cannot remove '–rf': No such file or directory
It seems to think that "-rf" is a file name. The command works fine on the command line.
Here is my script...
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Usage: $0 reponame1 reponame2 ..."
else
for var in "$#"
do
REPONAME=$var
mkdir -p ~/temp-$REPONAME/branches
mkdir ~/temp-$REPONAME/tags
mkdir ~/temp-$REPONAME/trunk
svnadmin create $REPONAME
svn import ~/temp-$REPONAME svn+ssh://username#192.168.123.234/home/username/svnrepos/$REPONAME -m "Initial structure"
rm –rf ~/temp-$REPONAME/
done
fi
And here is the output
$ ./mkrepo.sh mysvnrepo
username#192.168.123.234's password:
username#192.168.123.234's password:
Adding /home/username/temp-mysvnrepo/branches
Adding /home/username/temp-mysvnrepo/tags
Adding /home/username/temp-mysvnrepo/trunk
Committing transaction...
Committed revision 1.
rm: cannot remove '–rf': No such file or directory
rm: cannot remove '/home/username/temp-mysvnrepo/': Is a directory
You managed to type a unicode "EN DASH"(U+2013) which is not recognised by rm as a normal hyphen "-"(U+002D) so rm thinks it is the beginning of a file name and not of your parameters. They do look alike, but they are not the same for a program. To fix it, just erase it and type it again making sure you take the normal hyphen/minus key.
The '-' in your script in rm –rf is not the one it expects.
The correct one is rm -rf.
I hope you can spot the difference.
rm –rf
rm -rf

Difference between alias rm and /bin/rm

What is the difference between using /bin/rm abc.txt and the times when sometimes you have to alias rm which is then performed with rm abc.txt
/bin/rm will always refer to the binary rm command on your system. If you just write rm abc.txt one of these may happen:
Your shell implements rm directly as a builtin function or there is a shell function called rm (no external command is run).
rm has previously been aliased (with alias rm=<substituted-command>) to mean something different. Usually the aliased command is similar in function but it does not have to be.
If none of the above is applicable, the shell looks up the external command in /bin and runs it.
You can use alias to see all defined aliases. Also check out the command -V shell builtin which can tell you if a given command is an external command, shell function, builtin or special builtin.
A typical reason to create an alias for rm is to add the -i or -I option. In "interactive" mode rm will ask for confirmation before deleting anything.
$ alias rm="/bin/rm -i"
$ rm myfile
rm: remove regular file ‘myfile’? _

Why might "mkdir -p" lead to a Bash permission error?

If I make a directory with mkdir -p, it causes problems with scripts
$ mkdir -p test2/test2
$ cd test2/test2
$ echo '#!/bin/sh
> echo hello' > hello.sh
$ ./hello.sh
bash: ./hello.sh: Permission denied
This is nothing to do with mkdir. You simply haven't given hello.sh executable permissions. You need the following:
chmod +x hello.sh
Check your permissions
Check the permissions on your directories and the script itself. There may be a problem there, although it's unlikely.
ls -lad test2/test2
ls -l test2/test2/hello.sh
You can always use the --mode flag with mkdir if your permissions aren't being set correctly for some reason. See chmod(1) and mkdir(1) for more information.
Execute the file directly
You can execute the script with Bash directly, rather than relying on a shebang line or the executable bit, as long as the file is readable by the current user. For example:
bash test2/test2/hello.sh
Change file permissions
If you can execute the file when invoked explicitly with Bash, then you just need to make sure your file has the execute bit set. For example:
chmod 755 test2/test2/hello.sh

Including a chunk of code in a shell script

I have a number of shell scripts that all look like this:
#!/bin/bash
cd ~/Dropbox/cms_sites/examplesite/media
sass -C --style compressed --update css:css
cd ~/Dropbox/cms_sites/examplesite
rm -f ./cache/*.html
rm -fr ./media/.sass-cache/
rm -fr ./admin/media/.sass-cache/
rsync -auvzhL . username#host:/home/username/remote_folder
(I know the use of cd seems weird, but they have evolved!)
Now, all these scripts have a few differences, in that they have different usernames, hosts, local folder and remote folder names, and I want an inexperienced user to be able to run them without arguments (so he can drag and drop them into a terminal without issue).
What I'd like to do is something like:
#!/bin/bash
cd ~/Dropbox/cms_sites/examplesite/media
sass -C --style compressed --update css:css
cd ~/Dropbox/cms_sites/examplesite
include ~/scripts/common.sh
rsync -auvzhL . username#host:/home/username/remote_folder
then have a file in common.sh that looks like:
rm -f ./cache/*.html
rm -fr ./media/.sass-cache/
rm -fr ./admin/media/.sass-cache/
so that I can easily change sections of the code in lots of scripts at once.
Is this possible, or is there a better way to do this without using arguments and having one script?
Use the source command. It's bash's version of 'include'
No need for "include" if the script is executable:
~/scripts/common.sh
If the script is not executable or does not have an appropriate shebang line then you'll need to specify the interpreter:
bash ~/scripts/common.sh

Resources