Minor Rant: Linux, Scripts, and SetUID

You know the feeling when a system that you’ve used for years, and trusted, suddenly throws a curveball at you with a fun “Yeah you know this simple and concrete rule that’s never broken? Well in this one exception, it is, and nobody ever points it out. Have fun!”

So let’s set the stage. You’re writing a blog. This blog is kept in a Git repository. “Great!” You think, knowing that you can make changes from any of your computers, and just git pull on the server to bring everything down. Because how NGINX works, you don’t need to reload anything, the new content is available immediately. There’s just one problem: this means you need to SSH into the server to pull everything, which requires SSH into a border server first, tunneling your connection through another one. While yes, a few tweaks to ~/.ssh will make that work just fine, it’s still a bit of a pain. Suddenly, an idea strikes you: cron! Set up a cron.hourly task to cd into the directory, run a git pull, and then finally ask hugo to rebuild the site. To test this, you write a little shell script, give it +x, and run. Because you were planning to modify cron files, you’re root. You try, and….

Error: add site dependencies: create deps: failed to create file caches from configuration: mkdir /tmp/hugo_cache/root: permission denied

That’s…. strange. Run from your user? Perfectly fine. Root? Error. Then it hits you: Linux permission flags have two special bits: setuid and setgid. setuid will cause you to automatically assume the user that owns the file, and setgid will do the same but for groups and group ownership. So in theory, if your fictional user was named blog-update, then chown blog-update update; chmod u+s update would set setuid on the update script, and designate blog-update as the user that owns it… in theory, anyone who runs ./update would assume the rights and permissions of blog-update, right? No. Try again, same error. Debug time: add a whoami to your script. What does it report? Why, root of course. If you weren’t already doing your best JonTron impression, you are now.

So… what gives?

Why setuid and setgid Aren’t as Useful as They Should Be

setuid and setgid do change your user and group ID, but with one major, and, in my research, unmentioned exception (until someone at StackOverflow had the answer… obviously): They do not work on scripts! Yes, both bits work for compiled binary programs, such as fusermount, umount, su, ping (yes, ping needs to be run as root), and so on. Start your file with a #!/bin/sh, make it a shell script, and as a security feature, the setuid and setgid bits are flat-out ignored. Lovely!

Now yes, in fairness, it does make sense. But either I’ve not been looking in the right places, or that information is so rare, that it really took a bit of head-scratching for me to even realize what was going on… after I googled the answer as to why.

But… The Solution?

I just made a cron script as my user instead, problem solved. Not super extravagant, but it works.