Using noexec option in fstab

This link explains the benefits of using noexec option when using mount. However it lists one limitation - if I have a Perl/Python/shell script or a file that starts with #! and will try to execute it - I will be able to do so whether I supplied the option or not.

Is there a way to prevent such execution? I.e. is there an additional option to noexec which I can give and the execution of the scripts will not be possible?


There's a major misunderstanding here. Let's make these things clear.

First of all, the limitation you refer to, as it is stated, is not true:

However, when a script (a text file that begins with she-bang line; i.e., a line that begins with #!) is given to some shells (bash), it will run the executable named on that line (e.g., /usr/bin/perl) and connect the content of the script file to the stdin of that executable, which may not be on that drive.

Surprisingly it seems to explain the ability to execute, despite noexec. I think the asker there got it all wrong in the first place and it wasn't his or her fault! One wrong assumption in the question caused another wrong assumption in the answer.

What's wrong then?

1. Bind mount is specific

To get some context let's see what happens when you try to bind mount as read-only. There's this question: Why doesn't mount respect the read only option for bind mounts? The conclusion is:

To achieve the desired result one needs to run two commands:

mount SRC DST -o bind
mount DST -o remount,ro,bind

Newer versions of mount (util-linux >=2.27) do this automatically when one runs

mount SRC DST -o bind,ro

But when you try to use noexec instead of ro, you still need two commands! In my Kubuntu I have util-linux 2.27.1-6ubuntu3.3 and this command:

mount SRC DST -o bind,noexec

ignores noexec, I need to remount. It's the same if mounting is via /etc/fstab. You can experiment. At any time check with plain mount command what the actual options are.

I bet the asker thought the mount was with noexec option, but actually it was not. He or she was able to execute a script from within allegedly noexec mountpoint. It was strange, hence the question.

Then the answer author interpreted this, as if it was the shell what reads shebang, calls another executable and doesn't worry about noexec for the script. If the mountpoint was really noexec then this would be a reasonable speculation.

But…

2. It's a common myth that shells read shebangs; kernel does

Read How does the #! shebang work? and notice one of the answers there had originally followed the myth, then it was corrected.

So if you have:

  • a mountpoint /mnt/foo/ with noexec option,
  • a script /mnt/foo/script.py which is otherwise executable (e.g. chmod -x … was invoked),
  • a shebang like #!/usr/bin/python as the first line in the script

and you run it like this

/mnt/foo/script.py

then your Linux kernel won't let you because of noexec. It would have happened in this other question if the mounting was actually noexec there; but I believe it was not.

3. Still, there are two ways to "execute" a script

From comments:

"and will try to execute it" How? By running it directly or by passing it to the interpreter?

Running it directly means:

 /mnt/foo/script.py

This will honor noexec as elaborated above. The executable is script.py.

Passing it to the interpreter means:

python /mnt/foo/script.py

In this case the executable is python. It doesn't matter if foo/ is mounted with noexec; it doesn't matter if script.py is executable at all; it doesn't matter what the shebang is. The point is script.py is not executed, it's read.

As long as user can read a file and run proper interpreter, there's no way to prevent passing the file to the interpreter; but it's not the file that is executed.