Securing circumlunar.space with bind mounts ------------------------------------------- Lately I have been spending some time preparing for "opening up" circumlunar.space a bit, by which I mean actively soliciting new users from the general Gopher-using public. I will probably announce the Zaibatsu's existence on the gopher-project mailing list in the near future. So, I have been writing scripts to automate the task of setting up accounts, setting quotas, etc. The general plan is that people will be offered sftp-only access to their gopherspace, with authentication via public key only, without shell access. Disallowing password authentication means I don't have to worry about weak passwords getting brute forced. OpenSSH has very nice options for enforcing sftp only access. Modern versions of sshd have an internally-implemented version of the sftp server, which means you can chroot users into their home directory without having to populate it with libraries and things. I trust the OpenSSH guys to have got this right, so I feel safe granting this kind of access to strangers and trusting that they won't be able to get shell access until I explicitly grant it to them (and I do plan to offer shell access to anybody who maintains their gopherspace regularly and gives the impression of being somebody I would like to share a server with). The one wrinkle on this is Gophernicus' extensive CGI support. I have never used Gopher CGI myself and am not really interest in it, but I know a lot of phloggers use it for various things, so I don't want to just turn it off. However, offering sftp-only access rather than shell access is kind of pointless if somebody can upload any shell script they like to their gopher hole, point their gopher client at it and get Gophernicus to execute it. It's kind of like a very circuitous shell access of its own. And, okay, Gophernicus will run it as a low-privilege user, but I'm still not happy about this loop hole. At first glance, this is easy to fix. General CGI executables have to live in a "cgi_bin" directory inside a user's gopherspace for Gophernicus to execute them, so when I set up a user account I can create this directory myself, chown it to root and make sure the user has no access. My "make new user" script does this, and this straightforwardly means people can't upload anything in there. When they prove trustworthy and I give them shell access, I can just chown that directory back to them and we're all good. CGI problem solved, right? Well, not quite. Because Gophernicus *also* supports executable gophermaps. Any file named "gophermap" with the executable bit set will be treated as a CGI file. I can't stop people doing this with simple file permissions, because I *have* to let them be able to create their own directories in their gopherhole, and as soon as they create a directory of their own, they can put a gophermap file in it. This stumped me for a bit. All I could think was that if each user's gopherspace was on its own partition, I could mount them with the noexec option and then CGI would be totally useless for them. But giving each user their own partition would be a nightmare, and besides, the cheap-as-chips OpenVZ VPS technology doesn't give you the freedom to partition your own dedicated virtual disk. I did some searching and it turned out I was thinking along the right lines, but I was just a little bit out of date. It turns out that these days, on Linux at least, you don't need to put stuff in its own partition to facilitate using mount options. There's this new-fangled thing called a "bind mount" that I'd never heard of before. I am really thrilled that this little project is teaching me new stuff about system administration. A bind mount is conceptually pretty simple, it just lets you re-mount an existing part of your filesystem at a new mountpoint. Do: mount -o bind /home/bob /home/alice and the contents of /home/bob will be mirrored in /home/alice. It's kind of like a symbolic link, except that it's more transient (it won't survive a reboot, unless you put it in /etc/fstab) and a little more powerful because you can apply all the standard mounting options like ro, relatime, noexec, nosuid, nodev, etc. The following: mount -o bind,ro /home/bob /home/alice would make /home/alice a read-only mirror of /home/bob (/home/bob remains writable as always). Most interestingly, for the current application, is that you can bind mount some existing location *over the top of itself*, which basically means that you can apply those mounting options anywhere you like on your filesystem. This: mount -o bind,ro /home/bob /home/bob simply makes /home/bob read-only, until umount is used to reverse this. So, even if your entire system is installed in one big monolithic "/" partition, as is the style these days, you can still make any individual directory read-only or no-exec or whatever you like. That's pretty nice! So not only do my scripts now mount new users' gopherspaces noexec (and remount as such after reach reboot), but I can also do standard server hardening tricks like mounting /tmp noexec or mounting /usr ro, despite the fact that at first glance OpenVZ doesn't give me enough control to facilitate this.