I'm rootless


07:48 - 22 Mar 2015

One of the advantages of being based on NPM is the so modular that NodeOS is. That’s the reason why you would have not seen too much movement lately, but the fact is that in the project dependencies there has been a huge improvement: now thanks to a huge and agressive usage of the new Linux kernel multi-layer OverlayFS (we are needing to use a release candidate version of upcoming Linux 4.0 so it could be available! :-D), NodeOS offer a per-user independent & isolated root filesystem, and in fact now there’s no real root partition at all (that’s great to simplify the process to create a live ISO or a Raspberry PI disk image!!! :-D), making this not only the first OS to have a similar approach (loosely inspired in Plan9 bind union directories) but probably making it one of the most secure OSes, at least from a file access point of view :-)

[    1.072752] EXT4-fs (sda): warning: mounting unchecked fs, running e2fsck is recommended
[    1.076131] EXT4-fs (sda): mounted filesystem without journal. Opts: errors=remount-ro
Hello! I'm a user init script :-)
Welcome to NodeOS!: username:  nodeos
Welcome to NodeOS!: password:  
/ # ls
[ 'bin', 'dev', 'etc', 'init', 'lib', 'proc', 'share', 'tmp', 'usr' ]
/ # ls /bin
[ 'blessed',
  'slap' ]
/ # node
> process.env
{ HOME: '/',
  TERM: 'linux',
  LANG: 'es_ES.UTF-8',
  vga: '0x318',
  PATH: '/bin' }
/ # cat init
#!/usr/bin/env node

console.log("Hello! I'm a user init script :-)")
/ # ./init
Hello! I'm a user init script :-)
/ # ls usr/bin/
[ 'env' ]
/ # cd usr/bin/
/usr/bin # ls
[ 'env' ]
/usr/bin # cd
/ # ls share/
[ 'man' ]
/ # ls share/man/
[ 'man1', 'man3', 'man5', 'man7' ]
/ # 

Pretty, isn’t it? :-D

The way it works is fairly simple and it’s inspired by this IBM article about mount namespaces (that definitely are the next step where to move to improve all this OverlayFS big hack… if needed :-P): initramfs not only mount the users filesystem, but also means as a basis for the “general root filesystem” used as basis later for all the users (since by design since Linux 2.6.0 it will be always loaded in memory, so we re-use it). This is done by removing the unneeded modules and executables used to start and mount the filesystem so earn some RAM, but also to remove some possible security holes on that modules, so we keep only with an initram only with the Node.js executable and its needed dynamic libraries, and since they will be used everywhere this makes also the system faster. The users partition need to be mounted somewhere, so I’m doing it on the /tmp folder on the initramfs, so this way I can later mount over it an in-ram tmpfs filesystem for each user, hidding the raw users partition from being accesible by anybody and also preventing to write temporal data on the real hard disks.

After that, we use this initram as a read-only “carbon copy” where to mount over each of the users home (also root) and later using chroot to give to each users an unique root filesystem isolated from the others. This is only needed to exec the per-user /init executable and when login in the system, so it’s easy to have the access controlled, and all their subsequent process will be running inside their corresponding user chroot jail. There’s a minor exception regarding to the root user: if it’s available in the users filesystem (that’s not mandatory at all), since logon needs to access to the users home directory to read their login configuration, it is mounted first and later the users partition mount point is moved to a home folder inside its own filesystem, so it can access to them without modifying the carbon-copy initram root filesystem. After that, it is configured exactly the same way as any other regular user.

This also has the advantage to give to the users a simple filesystem hierarchy were to work, since their “home” is in fact the root of their own filesystem hierarchy, so they can easily install packages globally without requiring special permissions and will not need to configure anything since they will naturally be installed in their home directory by deafult.

There are some improvements that need to be done, obviously. First of all, the “demo” users filesystem that’s automatically generated on the build process needs to give diferent uids and gids to each one of the users folders since this is used as basis for the security scheme (maybe a patch for genext2fs?), but since it’s only for demo purposses this is not so a problem (but definitely we need to add some some info about how to create and config a custom, real one…).

Other notable problem is regarding to the /dev and /proc filesystems, since they are shown on each user filesystem as empty folder. In the case of /dev, the best solution is to implement a filesystem that only show the device entries that are accesible by the user and owned by him or not currently used, so they could be exclusively accessed. This could be easily done by using fusejs, since the NodeOS Linux kernel is already being compiled with support both for FUSE and CUSE. Regarding to /proc, something similar could be done, but seems its configurable at mount how accesible it is, so by using shared mountpoints I could have it replicated on all the users root filesystem. On the other hand, I would like that Node.js remove its dependency on it so it could be removed at all from NodeOS, since it’s obviously a posible entry point for security issues since you hace access to all the kernel internal structures… :-/

Next step, update the project documentation, some love for Docker (I have been focusing on QEmu since by default Docker needs sudo, so it makes more dificult to build the images) and generate (nightly) live ISOs :-) It will be funny to see NodeOS running on real hardware… :-D

P.D.: oh, and we have now 2200 stargazers and counting!!! :-D


NodeOS is made possible by its many contributors around the world. Source code licensed under the MIT license.

Made with ❤️
and 🍺