Posts from 2005–2010 temporarily unavailable.

A friend and I each run a D&D game, and we also play in each other’s games. We disagree on a number of different things about how the game is best played, and I learn a lot from seeing how both sets of choices play out in each of the two games.

One significant point of disagreement is how important it is to ensure that combat is balanced. In my game I disallow all homebrew and third party content. Only the core rulebooks, and official printed supplements, are permitted. By contrast, my friend has allowed several characters in his game to use homebrew races from the Internet, which are clearly more powerful than the PHB races. And he is quite happy to make modifications to spells and abilities without investigating the consequences for balance. Changes which seem innocuous can have balance consequences that you don’t realise for some time or do not observe without playtesting; I always assume the worst, and don’t change anything. (I constantly reflavour abilities and stats. In this post I’m interested in crunch alone.)

In this post I want to explain why I put such a premium on balance. Before getting on to that explanation, I first need to say something about the blogger Mike Shea’s claim that “D&D 5e is imbalanced by design and that’s ok. Imbalance leads to interesting stories.” (one, two). Shea is drawing a contrast between 4e and 5e. It was possible to more precisely quantify all character and monster abilities in 4e, which meant that if the calculations showed that an encounter would be easy, medium or hard, it was more likely to turn out to be easy, medium or hard. By contrast, 5e involves a number of abilities that can turn the tide of combat suddenly and against the odds. So while the XP thresholds might indicate that a planned encounter will be an easy one, a monster’s ability to petrify a character with just two failed saves could mean that the whole party goes down. Similarly for character abilities that can turn a powerful boss into a sitting duck for the entire combat. Shea points out that such abilities add an awful lot of fun and suspense to the game that might have been lacking from 4e.

I am not in a position to know whether 4e really lacked the kind of surprise and suspense described here. Regardless, Shea has identified something important about 5e. A great deal is added to combat by abilities on both sides that can quickly turn the tide. However, I find it misleading to say that this makes 5e unbalanced. Shea also uses the term ‘unpredictable’, and I think that’s a better way to refer to this phenomenon. For balance is more than determining an accurate challenge rating, and using this to pit the right number of monsters against the right number of players. In the absence of tide-turning abilities, that’s all balance is; however, balance is also a concept that applies to individual abilities, including tide-turning abilities.

I suggest that a very powerful ability, that has the potential to change the tide of a battle, is balanced by some combination of being (i) highly situational; (ii) very resource-depleting; and (iii) requires a saving throw, or similar, with the parameters set so that the full effect of the ability is unlikely to occur. Let me give a few examples. It’s been pointed out that the Fireball spell deals more damage than a multi-target 3rd level spell is meant to deal (DMG, p. 384). However, the spell is highly situational because it is highly likely to also hit your allies. (Evokers have a mitigation for this, but that is at the cost of a full class feature.) Power Word Kill might down a powerful enemy much sooner than expected. But there’s another enemy in the next room, and then that spell slot is gone.

We should conclude that 5e is not imbalanced by design, but unpredictable by design. In fact, I suggest that 5e spells and abilities are a mixture of the predictable and the unpredictable, and the concept of balance applies differently to these two kinds of abilities. A creature’s standard attack is predictable; balancing it is simply a matter of adjusting the to-hit bonus and the damage dice. Balancing its tide-turning ability is a matter of adjusting the factors I discussed in the previous paragraph, and possibly others. Playtesting will be necessary for both of these balancing efforts to succeed. Predictable abilities are unbalanced when they don’t do enough damage often enough, or too much damage too often, as compared with their CR. Unpredictable abilities are unbalanced when they offer systematic ways to change the tide of battle. Indeed, this makes them predictable.

Now that I’ve responded to Shea, I’ll say what I think the point of combat encounters is, and why this leads me to disallow content that has not been rigorously playtested. (My thinking here is very much informed by how Rodrigo Lopez runs D&D on the Critical Hit podcast, and what he says about running D&D in Q&A. Thank you Rodrigo!) Let me first set aside combat encounters that are meant to be a walkover, and combat encounters that are meant to end in multiple deaths or retreat. The purpose of walkover encounters is to set a particular tone in the story. It allows characters to know that certain things are not challenging to them, and this can be built into roleplaying (”we are among the most powerful denizens of the realm. That gives us a certain responsibility.”). The purpose of unwinnable combat encounters is to work through turning points in a campaign’s plot. The fact that an enemy cannot be defeated by the party is likely to drive a whole story arc; actually running that combat, rather than just saying that their attempt to fight the enemy fails, helps drive that home, and gives the characters points of reference (”you saw what happened when he turned his evil gaze upon you, Mazril. We’ve got to find another way!”).

Consider, then, other combat encounters. This is what I think they are all about. The GM creates an encounter that the rules say is winnable, or unwinnable but otherwise surviveable. Then the GM and the players fight out that encounter within the rules, each side trying to fully exploit the resources available to them, though without doing anything that would not make sense from the points of view of the characters and the monsters. Rolls are not made in secret or fudged, and HP totals are not arbitrarily adjusted. The GM does not pull punches. There are no restrictions on tactical thinking; for example, it’s fine for players to deduce enemy ACs, openly discuss them and act accordingly. However, actions taken must have an in-character justification. The outcome of the battle depends on a combination of tactics and luck: unpredictable abilities can turn the tide suddenly, and that might be enough to win or lose, but most of the time good tactical decision-making on the part of the players is rewarded. (The nature of monster abilities means that less interesting tactics are possible; further, the players have multiple brains between them, so ought to be able to out-think the GM in most cases.)

The result is that combat is a kind of minigame within D&D. The GM takes on a different role. In particular, GM fiat is suspended. The rules of the game are in charge (except, of course, where the GM has to make a call about a situation not covered by the rules). But isn’t this to throw out the advantages tabletop roleplaying games have over video games? Isn’t the GM’s freedom to bend the rules what makes D&D more fun and flexible? My basic response is that the rules for combat are only interesting when they do not depend on GM fiat, or other forms of arbitrariness, and for the parts of the game where GM fiat works well, it is better to use ability checks, or skills challenges, or straight roleplaying.

The thought is that the complexity of the combat rules is justified only when those rules are not arbitrary. If the players must think tactically within a system that can change arbitrarily, there’s no longer much point in investing energy in that tactical thinking. It is not intellectually interesting, it is much less fun, and it does not significantly contribute to the story. Tabletop games have an important role for a combination of GM fiat and dice rolls—the chance of those rolls succeeding remaining under the GM’s control—but that can be leveraged with simpler rules than those for combat. Now, I think that the combat rules are fun, so it is good to include them alongside the parts of the game that are more straightforwardly a collaboration between the GM and the players. But they should be deployed differently in order to bring out their strengths.

It should be clear, based on this, why I put such a premium on balance in combat: imbalance introduces arbitrariness to the combat system. If my tactical thinking is nullified by the systematic advantage that another party member has over my character, there’s no point in my engaging in that tactical thinking. Unpredictable abilities nullify tactical thinking in ways that are fun, but only when they are balanced in the ways I described above.

All of this is a matter of degree. I don’t think that combat is fun only when the characters and monsters are restricted to the core rulebooks; I enjoy combat when I play in my friend’s game. My view is just that combat is more fun the less arbitrary it is. I have certainly experienced the sense that my attempt to intellectually engage with the combat is being undermined by certain house rules and the overpowered abilities of homebrew races. Fortunately, thus far this problem has only affected a few turns of combat at a time, rather than whole combats.

Another friend is of the view that the GM should try to convince the players that they really are treating combat as I’ve described, but still fudge dice rolls in order to prevent, e.g., uninteresting character deaths. In response, I’ll note that I don’t feel capable of making those judgements, in the heat of the moment, about whether a death would be interesting. Further, having to worry about this would make combat less fun for me as the GM, and GM fun is important too.

Posted Sat 03 Mar 2018 23:32:56 UTC

The attack

Laptops need full disc encryption. Indeed, my university has explicitly banned us keeping any information on our students’ grades on our laptops unless we use FDE. Not even comments on essays, apparently, as that counts as grade information.

There must, though, exist unencrypted code that tells the computer how to decrypt everything else. Otherwise you can’t turn your laptop on. If you’re only trying to protect your data from your laptop being permanently stolen, it’s fine for this to be in an unencrypted partition on the laptop’s HDD: when your laptop is stolen, the data you are trying to protect remains encrypted.

An evil maid attack involves the replacement of this unencrypted code with something malicious – perhaps it e-mails data from the encrypted partition to someone who wants it. Of course, if someone has access to your laptop without your knowledge, they can always work around any security scheme you might develop. They might add a hardware keylogger, for example. So why might we want to try to protect against the evil maid attack – haven’t we already lost if someone is able to modify the contents of the unencrypted partition of our hard drive?

Well, the thing about the evil maid attack is that it is very quick and easy to modify the contents of a laptop’s hard drive, as compared to other security-bypassing hardware modifications, which take much longer to perform without detection. Users expect to be able to easily replace their hard drives, so they are usually accessible with the removal of just a single screw. It could take less than five minutes to deploy the evil maid payload.

Laptops are often left unattended for the two or three minutes it would take to deliver an evil maid payload; they are less often left for long enough that deeper hardware modifications could be made. So it is worth taking steps to prevent evil maid attacks.

The best solution

UEFI Secure Boot. But

  • Debian does not support this yet; and
  • my laptop does not have the hardware support anyway.

My current solution

The standard solution is to put the unencrypted hard drive partition on a USB key, and keep that on one’s keychain. Then there is no unencrypted code on the laptop at all; you boot from the USB, which decrypts the root partition, and then you unmount the USB key.

Problem with this solution

The big problem with this is kernel and bootloader upgrades. You have to ensure your USB key is mounted before your package manager upgrades the kernel. This effectively rules out using unattended-upgrades to get security upgrades for the kernel. They must be applied manually. Further, you probably want a backup USB key with the kernel and bootloader on it. Now you have to upgrade both, using commands like apt-get --reinstall.

This is a real maintenance burden and is likely to delay your security upgrades. And the whole point of putting /boot on a USB key was to improve security!

Something better

Recent GRUB is able to decrypt partitions itself. So /boot can reside within your encrypted root partition. GRUB’s setup scripts are smart enough that you can switch over to this in just a few steps:

  1. Move contents of /boot from USB drive into root partition.
  2. Remove/comment /boot from /etc/fstab.
  3. Set GRUB_ENABLE_CRYPTODISK=y in /etc/default/grub.
  4. grub-install /dev/sda
  5. update-grub

It’s still true that there must be unencrypted code that knows how to decrypt the root partition. Where does that go? grub-install is the command that installs that code; where does it put it? The ArchLinux wiki has the answer. If you’re using EFI, it will go in the EFI system partition (ESP). Under BIOS, if your drive is formatted with an MBR, it goes in the “post-MBR gap” between the MBR and the first partition (on drive partitioned with very old tools, this post-MBR gap might be too small to accommodate the larger GRUB image that contains the decryption code; however, drives partitioned with recent tools that “support 1 MiB partition alignment” (including the Debian stretch installer) will be fine – to check fdisk -l and look at where your first partition starts). Under BIOS, if your drive is formatted with a GPT, you have to add a 1MiB BIOS boot partition, and the code goes there.

We’ve resolved the issue of package updates modifying /boot, which now resides in the encrypted root partition. However, this is not all of the picture. If we are using EFI, now we have unencrypted code in the EFI system partition which is subject to the evil maid attack. And if we respond by moving the EFI system partition onto a USB drive, the package update problem reoccurs: the EFI system partition will not always be mounted. If we are using BIOS, the evil maid reoccurs since it is not that much harder to modify the code in the post-MBR gap or the BIOS boot partition.

My proposed solution, pending UEFI Secure Boot, is to use BIOS boot with a MBR partition table, keep /boot in the encrypted root partition and grub-install to the USB drive. dpkg-reconfigure grub-pc and tell it to never grub-install to anything. Then set the laptop’s boot order to never try to boot from the HDD, only from USB. (There’s no real advantage of GPT with my simple partitioning setup but I think that would also work fine.)

How does this solve the various issues I’ve raised? Well, the amount of code on the USB drive is very small (less than 1MiB) so it is much less likely to require manual updates. Kernel updates will modify /boot; only bootloader could require me to manually run grub-install to modify the contents of the post-MBR gap, but these are very infrequent.

Of course, the BIOS could be cracked such that the laptop will boot from the HDD no matter what USB I have plugged in, or even only when some USB is plugged in, but that’s a hardware modification beyond the evil maid, against which we are not trying to protect.

As a nice bonus, the USB drive’s single FAT32 partition is now usable for sneakernet.

Posted Wed 14 Feb 2018 19:22:07 UTC Tags:

Bird went out into the driveway alone. It wasn’t raining and the wind had died: the clouds sailing the sky were bright, dry. A brilliant morning had broken from the dawn’s cocoon of semidarkness, and the air had a good, first-days-of-summer smell that slackened every muscle in Bird’s body. A night softness had lingered in the hospital, and now the morning light, reflecting off the wet pavement and off the leafy trees, stabbed like icicles at Bird’s pampered eyes. Labouring into this light on his bike was like being poised on the edge of a diving board; Bird felt severed from the certainty of the ground, isolated. And he was as numb as stone, a weak insect on a scorpion’s sting. (Ōe, A Personal Matter (trans. John Nathan), ch. 2)

Forenoon: the most exhilarating hour of an early summer day. And a breeze that recalled elementary school excursions quickened the worms of tingling pleasure on Bird’s cheeks and earlobes, flushed from lack of sleep. The nerve cells in his skin, the farther they were from conscious restraint, the more thirstily they drank the sweetness of the season and the hour. Soon a sense of liberation rose to the surface of his consciousness. (ch. 3)

Posted Sun 04 Feb 2018 17:23:55 UTC

Looks like there won’t be a release of Policy this month, but please consider contributing so we can get one out next month. Here’s a selection of bugs:

Consensus has been reached and help is needed to write a patch

#685746 debian-policy Consider clarifying the use of recommends

#749826 [multiarch] please document the use of Multi-Arch field in debian/c…

#757760 please document build profiles

#759316 Document the use of /etc/default for cron jobs

#761219 document versioned Provides

#767839 Linking documentation of arch:any package to arch:all

#770440 policy should mention systemd timers

#793499 The Installed-Size algorithm is out-of-date

#823256 Update maintscript arguments with dpkg >= 1.18.5

#835451 Building as root should be discouraged

#853779 Clarify requirements about update-rc.d and invoke-rc.d usage in mai…

#874206 allow a trailing comma in package relationship fields

Wording proposed, awaiting review from anyone and/or seconds by DDs

#582109 document triggers where appropriate

#610083 Remove requirement to document upstream source location in debian/c…

#649530 [copyright-format] clearer definitions and more consistent License:…

#662998 stripping static libraries

#682347 mark ‘editor’ virtual package name as obsolete

#737796 copyright-format: support Files: paragraph with both abbreviated na…

#742364 Document debian/missing-sources

#756835 Extension of the syntax of the Packages-List field.

#786470 [copyright-format] Add an optional “License-Grant” field

#835451 Building as root should be discouraged

#845255 Include best practices for packaging database applications

#846970 Proposal for a Build-Indep-Architecture: control file field

#864615 please update version of posix standard for scripts (section 10.4)

Merged for the next release

#299007 Transitioning perms of /usr/local

#515856 remove get-orig-source

#886890 Fix for found typos

#888437 Several example scripts are not valid.

Posted Mon 29 Jan 2018 04:17:14 UTC Tags:

A few comments on Star Wars: The Last Jedi.

Vice Admiral Holdo’s subplot was a huge success. She had to make a very difficult call over which she knew she might face a mutiny from the likes of Poe Dameron. The core of her challenge was that there was no speech or argument she could have given that would have placated Dameron and restored unity to the crew. Instead, Holdo had to press on in the face of that disunity. This reflects the fact that, sometimes, living as one should demands pressing on in the face deep disagreement with others.

Not making it clear that Dameron was in the wrong until very late in the film was a key component of the successful portrayal of the unpleasantness of what Holdo had to do. If instead it had become clear to the audience early on that Holdo’s plan was obviously the better one, we would not have been able to observe the strength of Holdo’s character in continuing to pursue her plan despite the mutiny.

One thing that I found weak about Holdo was her dress. You cannot be effective on the frontlines of a hot war in an outfit like that! Presumably the point was to show that women don’t have to give up their femininity in order to take tough tactical decisions under pressure, and that’s indeed something worth showing. But this could have been achieved by much more subtle means. What was needed was to have her be the character with the most feminine outfit, and it would have been possible to fulfill that condition by having her wear something much more practical. Thus, having her wear that dress was crude and implausible overkill in the service of something otherwise worth doing.

I was very disappointed by most of the subplot with Rey and Luke: both the content of that subplot, and its disconnection from the rest of film.

Firstly, the content. There was so much that could have been explored that was not explored. Luke mentions that the Jedi failed to stop Darth Sidious “at the height of their powers”. Well, what did the Jedi get wrong? Was it the Jedi code; the celibacy; the bureaucracy? Is their light side philosophy to absolutist? How are Luke’s beliefs about this connected to his recent rejection of the Force? When he lets down his barrier and reconnects with the force, Yoda should have had much more to say. The Force is, perhaps, one big metaphor for certain human capacities not emphasised by our contemporary culture. It is at the heart of Star Wars, and it was at the heart of Empire and Rogue One. It ought to have been at the heart of The Last Jedi.

Secondly, the lack of integration with the rest of the film. One of the aspects of Empire that enables its importance as a film, I suggest, is the tight integration and interplay between the two main subplots: the training of Luke under Yoda, and attempting to shake the Empire off the trail of the Millennium Falcon. Luke wants to leave the training unfinished, and Yoda begs him to stay, truly believing that the fate of the galaxy depends on him completing the training. What is illustrated by this is the strengths and weaknesses of both Yoda’s traditional Jedi view and Luke’s desire to get on with fighting the good fight, the latter of which is summed up by the binary sunset scene from A New Hope. Tied up with this desire is Luke’s love for his friends; this is an important strength of his, but Yoda has a point when he says that the Jedi training must be completed if Luke is to be ultimately succesful. While the Yoda subplot and what happens at Cloud City could be independently interesting, it is only this integration that enables the film to be great. The heart of the integration is perhaps the Dark Side Cave, where two things are brought together: the challenge of developing the relationship with oneself possessed by a Jedi, and the threat posed by Darth Vader.

In the Last Jedi, Rey just keeps saying that the galaxy needs Luke, and eventually Luke relents when Kylo Ren shows up. There was so much more that could have been done with this! What is it about Rey that enables her to persuade Luke? What character strengths of hers are able to respond adequately to Luke’s fear of the power of the Force, and doubt regarding his abilities as a teacher? Exploring these things would have connected together the rebel evacuation, Rey’s character arc and Luke’s character arc, but these three were basically independent.

(Possibly I need to watch the cave scene from The Last Jedi again, and think harder about it.)

Posted Sun 14 Jan 2018 23:54:05 UTC

If you are a Debian Maintainer (DM) or Debian Developer (DD) doing source-only uploads to Debian for packages maintained in git, you are probably using some variation of the following:

% # sbuild/pbuilder, install and test the final package
% # everything looks good
% dch -r
% git commit debian/changelog "Finalise 1.2.3-1 upload"
% gbp buildpackage -S --git-tag
% debsign -S
% dput ftp-master ../foo_1.2.3-1_source.changes
% git push --follow-tags origin master

where the origin remote is probably Please consider replacing the above with the following:

% # sbuild/pbuilder, install and test the final package
% # everything looks good
% dch -r
% git commit debian/changelog "Finalise 1.2.3-1 upload"
% dgit push-source --gbp
% git push --follow-tags origin master

where the dgit push-source call does the following:

  1. Various sanity checks, some of which are not performed by any other tools, such as
    • not accidently overwriting an NMU.
    • not missing the .orig.tar from your upload
    • ensuring that the Distribution field in your changes is the same as your changelog
  2. Builds a source package from your git HEAD.
  3. Signs the .changes and .dsc.
  4. dputs these to ftp-master.
  5. Pushes your git history to dgit-repos.

Why might you want to do this? Well,

  1. You don’t need to learn how to use dgit for any other parts of your workflow. It’s entirely drop-in.
    • Assuming that your repository has upstream source committed with patches unapplied, and there is at least one quilt patch, dgit will not make any merge commits on your master branch, or anything surprising like that.
  2. No-one else in your team is required to use dgit. Nothing about their workflow need change.
  3. Benefit from dgit’s sanity checks.
  4. Provide your git history on dgit-repos in a uniform format that is easier for users, NMUers and downstreams to use (see dgit-user(7) and dgit-simple-nmu(7)).
    • Note that this is independent of the history you push to alioth/salsa. You still need to push to salsa as before, and assuming that your repository has upstream source committed with patches unapplied and there is at least one quilt patch, the format of that history is not changed.
  5. Only a single command is required to perform the source-only upload, instead of three.


  1. If you’re using git dpm you’ll want --dpm instead of --gbp.
  2. If the last upload of the package was not performed with dgit (e.g. this is your first upload using dgit), you’ll need to pass --overwrite. dgit will tell you if you need this. This is to avoid accidently excluding the changes in NMUs.
Posted Wed 10 Jan 2018 17:54:45 UTC Tags:

dgit 4.2, which is now in Debian unstable, has a new subcommand: dgit push-source. This is just like dgit push, except that

  • it forces a source-only upload; and
  • it also takes care of preparing the _source.changes, transparently, without the user needing to run dpkg-buildpackage -S or dgit build-source or whatever.

push-source is useful to ensure you don’t accidently upload binaries, and that was its original motivation. But there is a deeper significance to this new command: to say

% dgit push-source unstable

is, in one command, basically to say

% git push ftp-master HEAD:unstable

That is: dgit push-source is like doing a single-step git push of your git HEAD straight into the archive! The future is now!

The contrast here is with ordinary dgit push, which is not analogous to a single git push command, because

  • it involves uploading .debs, which make a material change to the archive other than updating the source code of the package; and
  • it must be preceded by a call to dpkg-buildpackage, dgit sbuild or similar to prepare the .changes file.

While dgit push-source also involves uploading files to ftp-master in addition to the git push, because that happens transparently and does not require the user to run a build command, it can be thought of as an implementation detail.

Two remaining points of disanalogy:

  1. git push will push your HEAD no matter the state of the working tree, but dgit push-source has to clean your working tree. I’m thinking about ways to improve this.

  2. For non-native packages, you still need an orig.tar in ... Urgh. At least that’s easy to obtain thanks to git-deborig.

Posted Mon 08 Jan 2018 07:48:27 UTC Tags:

Yesterday we released Debian Policy, containing patches from numerous different contributors, some of them first-time contributors. Thank you to everyone who was involved!

Please consider getting involved in preparing the next release of Debian Policy, which is likely to be uploaded sometime around the end of January.

Consensus has been reached and help is needed to write a patch

#780725 PATH used for building is not specified

#793499 The Installed-Size algorithm is out-of-date

#823256 Update maintscript arguments with dpkg >= 1.18.5

#833401 virtual packages: dbus-session-bus, dbus-default-session-bus

#835451 Building as root should be discouraged

#838777 Policy 11.8.4 for x-window-manager needs update for freedesktop menus

#845715 Please document that packages are not allowed to write outside thei…

#853779 Clarify requirements about update-rc.d and invoke-rc.d usage in mai…

#874019 Note that the ’-e’ argument to x-terminal-emulator works like ’–’

#874206 allow a trailing comma in package relationship fields

Wording proposed, awaiting review from anyone and/or seconds by DDs

#515856 remove get-orig-source

#582109 document triggers where appropriate

#610083 Remove requirement to document upstream source location in debian/c…

#645696 [copyright-format] clearer definitions and more consistent License:…

#649530 [copyright-format] clearer definitions and more consistent License:…

#662998 stripping static libraries

#682347 mark ‘editor’ virtual package name as obsolete

#737796 copyright-format: support Files: paragraph with both abbreviated na…

#742364 Document debian/missing-sources

#756835 Extension of the syntax of the Packages-List field.

#786470 [copyright-format] Add an optional “License-Grant” field

#835451 Building as root should be discouraged

#845255 Include best practices for packaging database applications

#846970 Proposal for a Build-Indep-Architecture: control file field

#864615 please update version of posix standard for scripts (section 10.4)

Posted Thu 28 Dec 2017 22:47:06 UTC Tags:

Two tensions

  1. 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.

  2. 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:

  1. Laptop runs Debian stable. Main account is spwhitton.
  2. 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, run xhost -local:.
  3. debootstrap a Debian unstable chroot into /var/lib/container/develacc.
  4. Install useful desktop things like task-british-desktop into /var/lib/container/develacc.
  5. Boot /var/lib/container/develacc as a systemd-nspawn container called develacc.
  6. dm-tool switch-to-greeter to start a new X server on vt8. Login as spw.
  7. Propellor installs a script enter-develacc which uses nsenter(1) to run commands in the develacc container. Create a further script enter-develacc-i3 which does

     /usr/local/bin/enter-develacc sh -c "cd ~spw; DISPLAY=$1 su spw -c i3"
  8. Finally, /home/spw/.xsession starts i3 in the chroot pointed at vt8’s X server:

     sudo /usr/local/bin/enter-develacc-i3 $DISPLAY
  9. Phew. May now pip install foo. And Ctrl-Alt-F7 to go back to my secure session. That session can read and write /home/spw, so I can dgit 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 \\"
    & "/etc/network/if-up.d/develacc-resolvconf" `File.mode` 0O0755
    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


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 in enter-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 isolation

  • lightdm 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!


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

Posted Fri 15 Dec 2017 00:18:30 UTC Tags:

Here’s are some more of the bugs against the Debian Policy Manual. In particular, there really are quite a few patches needing seconds from DDs. Please consider getting involved.

Consensus has been reached and help is needed to write a patch

#568313 Suggestion: forbid the use of dpkg-statoverride when uid and gid ar…

#578597 Recommend usage of dpkg-buildflags to initialize CFLAGS and al.

#582109 document triggers where appropriate

#587991 perl-policy: /etc/perl missing from Module Path

#592610 Clarify when Conflicts + Replaces et al are appropriate

#613046 please update example in 4.9.1 (debian/rules and DEB_BUILD_OPTIONS)

#614807 Please document autobuilder-imposed build-dependency alternative re…

#628515 recommending verbose build logs

#664257 document Architecture name definitions

#682347 mark ‘editor’ virtual package name as obsolete

Wording proposed, awaiting review from anyone and/or seconds by DDs

#582109 document triggers where appropriate

#610083 Remove requirement to document upstream source location in debian/c…

#636383 10.2 and others: private libraries may also be multi-arch-ified

#645696 [copyright-format] clearer definitions and more consistent License:…

#649530 [copyright-format] clearer definitions and more consistent License:…

#662998 stripping static libraries

#682347 mark ‘editor’ virtual package name as obsolete

#683495 perl scripts: ”#!/usr/bin/perl” MUST or SHOULD?

#688251 Built-Using description too aggressive

#737796 copyright-format: support Files: paragraph with both abbreviated na…

Merged for the next release

#683495 perl scripts: ”#!/usr/bin/perl” MUST or SHOULD?

#877674 [debian-policy] update links to the pdf and other formats of the do…

#878523 [PATCH] Spelling fixes

Posted Mon 27 Nov 2017 16:40:56 UTC Tags: