Two tensions
Sometimes the contents of the Debian archive isn’t yet sufficient for working in a software ecosystem in which I’d like to work, and I want to use that ecosystem’s package manager which downloads the world into $HOME – e.g.
stack
,pip
,lein
and friends.But I can’t use such a package manager when $HOME contains my PGP subkeys and other valuable files, and my X session includes Firefox with lots of saved passwords, etc.
I want to run Debian stable on my laptop for purposes of my day job – if I can’t open Emacs on a Monday morning, it’s going to be a tough week.
But I also want to do Debian development on my laptop, and most of that’s a pain without either Debian testing or Debian unstable.
The solution
Have Propellor provision and boot a systemd-nspawn(1) container running Debian unstable, and start a window manager in that container with $DISPLAY pointing at an X server in vt8. Wooo!
In more detail:
- Laptop runs Debian stable. Main account is spwhitton.
- Achieve isolation from
/home/spwhitton
by creating a new user account, spw, that can’t read/home/spwhitton
. Also, in X startup scripts for spwhitton, runxhost -local:
. - debootstrap a Debian unstable chroot into
/var/lib/container/develacc
. - Install useful desktop things like
task-british-desktop
into/var/lib/container/develacc
. - Boot
/var/lib/container/develacc
as a systemd-nspawn container calleddevelacc
. dm-tool switch-to-greeter
to start a new X server on vt8. Login as spw.Propellor installs a script
enter-develacc
which uses nsenter(1) to run commands in thedevelacc
container. Create a further scriptenter-develacc-i3
which does/usr/local/bin/enter-develacc sh -c "cd ~spw; DISPLAY=$1 su spw -c i3"
Finally,
/home/spw/.xsession
starts i3 in the chroot pointed at vt8’s X server:sudo /usr/local/bin/enter-develacc-i3 $DISPLAY
Phew. May now
pip install foo
. AndCtrl-Alt-F7
to go back to my secure session. That session can read and write/home/spw
, so I candgit push
etc.
The Propellor configuration
develaccProvisioned :: Property (HasInfo + DebianLike)
develaccProvisioned = propertyList "develacc provisioned" $ props
& User.accountFor (User "spw")
& Dotfiles.installedFor (User "spw")
& User.hasDesktopGroups (User "spw")
& withMyAcc "Sean has 'spw' group"
(\u -> tightenTargets $ User.hasGroup u (Group "spw"))
& withMyHome "Sean's homedir chmodded"
(\h -> tightenTargets $ File.mode h 0O0750)
& "/home/spw" `File.mode` 0O0770
& "/etc/sudoers.d/spw" `File.hasContent`
["spw ALL=(root) NOPASSWD: /usr/local/bin/enter-develacc-i3"]
& "/usr/local/bin/enter-develacc-i3" `File.hasContent`
[ "#!/bin/sh"
, ""
, "echo \"$1\" | grep -q -E \"^:[0-9.]+$\" || exit 1"
, ""
, "/usr/local/bin/enter-develacc sh -c \\"
, "\t\"cd ~spw; DISPLAY=$1 su spw -c i3\""
]
& "/usr/local/bin/enter-develacc-i3" `File.mode` 0O0755
-- we have to start xss-lock outside of the container in order that it
-- can interface with host logind
& "/home/spw/.xsession" `File.hasContent`
[ "if [ -e \"$HOME/local/wallpaper.png\" ]; then"
, " xss-lock -- i3lock -i $HOME/local/wallpaper.png &"
, "else"
, " xss-lock -- i3lock -c 3f3f3f -n &"
, "fi"
, "sudo /usr/local/bin/enter-develacc-i3 $DISPLAY"
]
& Systemd.nspawned develAccChroot
& "/etc/network/if-up.d/develacc-resolvconf" `File.hasContent`
[ "#!/bin/sh"
, ""
, "cp -fL /etc/resolv.conf \\"
,"\t/var/lib/container/develacc/etc/resolv.conf"
]
& "/etc/network/if-up.d/develacc-resolvconf" `File.mode` 0O0755
where
develAccChroot = Systemd.debContainer "develacc" $ props
-- Prevent propellor passing --bind=/etc/resolv.conf which
-- - won't work when system first boots as WLAN won't be up yet,
-- so /etc/resolv.conf is a dangling symlink
-- - doesn't keep /etc/resolv.conf up-to-date as I move between
-- wireless networks
! Systemd.resolvConfed
& osDebian Unstable X86_64
& Apt.stdSourcesList
& Apt.suiteAvailablePinned Experimental 1
-- use host apt cacher (we assume I have that on any system with
-- develaccProvisioned)
& Apt.proxy "http://localhost:3142"
& Apt.installed [ "i3"
, "task-xfce-desktop"
, "task-british-desktop"
, "xss-lock"
, "emacs"
, "caffeine"
, "redshift-gtk"
, "gnome-settings-daemon"
]
& Systemd.bind "/home/spw"
-- note that this won't create /home/spw because that is
-- bind-mounted, which is what we want
& User.accountFor (User "spw")
-- ensure that spw inside the container can read/write ~spw
& scriptProperty
[ "usermod -u $(stat --printf=\"%u\" /home/spw) spw"
, "groupmod -g $(stat --printf=\"%g\" /home/spw) spw"
] `assume` NoChange
Comments
I first tried using a traditional chroot. I bound lots of /dev
into
the chroot and then tried to start lightdm on vt8. This way, the
whole X server would be within the chroot; this is in a sense more
straightforward and there is not the overhead of booting the
container. But lightdm refuses to start.
It might have been possible to work around this, but after reading
a number of reasons why chroots are less good under systemd as
compared with sysvinit, I thought I’d try systemd-nspawn, which I’ve
used before and rather like in general. I couldn’t get lightdm to
start inside that, either, because systemd-nspawn makes it difficult
to mount enough of /dev
for X servers to be started. At that point
I realised that I could start only the window manager inside the
container, with the X server started from the host’s lightdm, and went
from there.
The security isn’t that good. You shouldn’t be running anything actually untrusted, just stuff that’s semi-trusted.
chmod 750 /home/spwhitton
,xhost -local:
and the argument validation inenter-develacc-i3
are pretty much the extent of the security here. The containerisation is to get Debian sid on a Debian stable machine, not for isolationlightdm still runs X servers as root even though it’s been possible to run them as non-root in Debian for a few years now (there’s a wishlist bug against lightdm)
I now have a total of six installations of Debian on my laptop’s hard drive … four traditional chroots, one systemd-nspawn container and of course the host OS. But this is easy to manage with propellor!
Bugs
Screen locking is weird because logind sessions aren’t shared into the
container. I have to run xss-lock
in /home/spw/.xsession
before
entering the container, and the window manager running in the
container cannot have a keybinding to lock the screen (as it does in
my secure session). To lock the spw X server, I have to shut my
laptop lid, or run loginctl lock-sessions
from my secure session,
which requires entering the root password.
Usage notes
Be sure you are actually logging into the container and not just i3 session on host! Check that lightdm’s selected session is “Default X session”
enter-develacc is the convenient way to get root. =machinectl login= won’t work well because pts/0 is not listed in /etc/securetty, so won’t be able to login as root.
Don’t reboot with machinectl. Bricks container and have to reboot host to get it going again. Instead, try this (barely/un-tested):
- systemctl restart systemd-nspawn@develacc.service.d
/tmp is volatile; use /home/spw/tmp to put stuff in container