Deck is very simple, but its a bit hard to explain. Basically its a high-level interface to overlays with a couple of tricks up its sleeve.
The impetus for inventing deck was to come up with a better way to create and configure chroot overlays. Doing it by hand created a few problems:
too many long arcane command line options
you had to create your overlay and mount points by hand
having the overlay in-your-face besides the mount point (and the original readonly branch) is messy:
$ ls /chroot/ rootfs rootfs-tmp rootfs-tmp.overlay
FYI, rootfs is the original debootstrap chroot I created and rootfs-tmp is the overlay in which I setup my development environment.
after I created the overlay I would have redo the various mounts inside rootfs-tmp (e.g., /proc /sys /dev/pts /turnkey). These sub=mounts don't carry over from a branch of the union to the union mount point, so I had to redo them by hand every time I rebooted. I eventually wrote a small script to do this.
I couldn't branch from a given chroot (not easily anyhow). For example, say I wanted to branch off rootfs-tmp-dirty from rootfs-tmp and try a dirty operation there, which I could then throw away. Theres no easy way to do that.
Ideally, I wanted chroots to be as flexible as vmware snapshots.
- deck
- a virtual filesystem branch derived from a directory or another deck
- stack
- a stack of filesystem levels a deck is comprised of
- levels
- behind the scenes, decks are a union of multiple filesystem levels
Deck solves all of the above problems in a very elegant way. The most clever part is how it supports arbitrary branching, by structuring the 'deck' as a stack of unioned overlays.
In principle, to branch 'deck-b' from 'deck-a', you copy the stack structure for 'deck-a' into the new 'deck-b' and then add another overlay layer to each of the decks, so that post-branching writes go to the separate branches. This way the decks remain independent following the branching (changes to deck-a don't effect deck-b and vice versa). Since the stack structure is really an ordered list of pointers to the overlay levels, the branching operation does not need to copy/replicate any actual files during the branching so the operation is very fast and efficient.
Note that deck isn't specific to chroots. Thats just the original application that got the ball rolling. Since its built on top of useraufs, regular users can use it to branch their own directories. However, deck only takes care of sub-mounts for you if you are root, as this requires special privileges.
Note, that the cli is designed to resemble a simplified mount. (not a toolkit)
Syntax: deck /path/to/dir/or/deck /path/to/new/deck Syntax: deck [ -options ] /path/to/existing/deck Deck a filesystem
Options:
-m mounts deck (the default) -u unmount deck (also refresh's the deck's fstab) -r refresh the deck's fstab (without unmounting) -d delete the deck
Usage examples:
cd /chroot # create a new deck, branching rootfs.1 from rootfs will replicate any # mounts in rootfs to rootfs.1 deck rootfs rootfs.1 # branch rootfs.1.1 from rootfs.1 deck rootfs.1 rootfs.1.1 # unmount rootfs.1 deck -u rootfs.1 # remount rootfs.1 (reconstructs the various mounts we had) deck rootfs.1 # delete rootfs.1 deck -d rootfs.1 # refresh mounts cd rootfs.1.1 mount --bind /mnt/archive/repository rootfs.1.1/turnkey/repository # refresh mounts (without unmounting) deck -r rootfs.1.1 # when you unmount it also updates the fstab deck rootfs.1.1 mount # --bind /home/z rootfs.1.1/home/z deck -u rootfs.1.1 deck rootfs.1.1 # sure enough, deck has saved the state of your mounts for you ls -la # rootfs.1.1/home/z
Deck internals are stored in '.deck' inside the same directory as the deck we created is located. One caveat is that you can't branch from decks in another directory, only in the same directory.
Here's a snapshot of the contents of the .deck directory:
.deck/ levels.refs/ # references to overlay levels <level-id>/ <name> -> ../stacks/<name> levels # overlay levels <level-id>/ # <level-id> is a random hash stacks/ # stack structure of decks (which levels in what order) <name>/ 0 -> /base 1 -> ../../levels/3902bf6c37bdd2d16d92731ed76400ab 2 -> ../../levels/0944b20f5eceec96a2da758182e8516e <name-derivative>/ 0 -> /base 1 -> ../../levels/3902bf6c37bdd2d16d92731ed76400ab 2 -> ../../levels/5b04f8c439460a60b200393c1604d5cc mounts/ # pointers to a record of mounts for this deck (if we're root) <name> <name-derivative>
In high risk applications, Deck is potentially a security risk. It is dangerous to allow arbitrary mounts as this can be used to subvert the security policy. For example, an attacker could use mount --bind /etc to a location of his choosing, and the security policy will not apply to the new location, only the location of the old files.
Overlays are dangerous because they are sort of a more powerful equivalent of mount --bind, and they can be used to compromise the MAC policy just as well, but an even greater risk is deck's automatic support for sub-mounts.
I've built a few countermeasures into deck to reduce the risk:
We rely on useraufs's configuration to allow the administrator to configure: which users are allowed to use the system which directories is the system allowed to operate on
Since useraufs makes these decisions, its important to make sure we are running a good version of it, and not some spoofed version that never fails. To prevent these tricks, I fixed that project's execproxy so that it cleans the environment regardless of whether we are running as suid or not.
This however, is not enough, since a malicious root user can potentially trick deck into making arbitrary mounts by tampering with the fstab file. So...
To prevent tampering, fstabs are not stored inside the local .deck sub-directory, but rather in a global location (/etc/deck/mounts). This directory can be protected by the MAC policy, such that only the deck process itself can access it.