Switching from Gnome to a tiling window manager

After having thought about it since "forever", I finally decided to switch to a tiling window manager. I went with sway since it runs on wayland and since it seems to be the recommended "wayland version of i3", a tiling window manager that many of my tech friends use ;)

After a few days of using sway, I'm pretty sure that I won't switch back anytime soon. It feels super convenient to have all windows tiled on the screen and being able to rearrange and resize them easily with a few keyboard shortcuts.

There's still some things that didn't work instantly, so I'll try to document them here in hope that it's useful to others. Feedback welcome!

This blog post covers the following topics:

Install sway on Debian Buster

I run Debian Buster on my work machine. The sway components aren't available in Buster or buster-backports yet, so I went with installing the packages from Unstable or experimental manually. I'll probably help with backporting them to buster-backports once I settled on using sway.

Lucky enough, sway packages only bring one dependency that's not satisfied in Buster, which is libjson-c4. So for now, to install the sway Debian packages on Buster, you have to do the following:

mkdir ~/devel/sway && cd ~/devel/sway

wget http://ftp.de.debian.org/debian/pool/main/w/wlroots/libwlroots3_0.7.0-2_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/s/scdoc/scdoc_1.10.0-1_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/s/swaybg/swaybg_1.0-2_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/s/swaylock/swaylock_1.4-1_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/s/swayidle/swayidle_1.5-1_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/s/sway/sway-backgrounds_1.2-1_all.deb
wget http://ftp.de.debian.org/debian/pool/main/j/json-c/libjson-c4_0.13.1+dfsg-6_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/s/sway/sway_1.2-1_amd64.deb

apt install ./libwlroots3_0.7.0-2_amd64.deb ./scdoc_1.10.0-1_amd64.deb ./swaybg_1.0-2_amd64.deb ./swaylock_1.4-1_amd64.deb ./swayidle_1.5-1_amd64.deb ./sway-backgrounds_1.2-1_all.deb ./libjson-c4_0.13.1 ./sway_1.2-1_amd64.deb

apt install dunst i3status suckless-tools

Basic sway configuration

Sway brings a good basic configuration at /etc/sway/config. In order to customize it, copy the file over to ~/.config/sway/config. First things I changed were the following:

# Disable windows title bars
default_borter pixel

# Use tilix wrapper as terminal emulator (more on that later)
set $term ~/.config/sway/scripts/tilix-wrapper.sh

# My internal laptop screen
set $laptop_screen eDP-1

# Command to lock screen
set $lock 'swaylock -F -f -e -K -l -c 000000'

# Default wallpaper
output * bg ~/Pictures/favourite_background.jpg fill

# Idle configuration
exec swayidle -w \
         timeout 300 $lock \
         timeout 600 'swaymsg "output * dpms off"' \
         resume 'swaymsg "output * dpms on"' \
         before-sleep $lock

# Internal Thinkpad Keyboard
input "1:1:AT_Translated_Set_2_keyboard" {
    xkb_layout de,us
    # Change keyboard layouts on <Super>+<Space>
    xkb_options grp:win_space_toggle
}

# Cherry Keyboard
input "1130:275:Cherry_GmbH_CHERRY_Wired_Keyboard" {
    xkb_layout de,us
    # Change keyboard layouts on <Super>+<Space>
    xkb_options grp:win_space_toggle
}

# Internal Thinkpad Touchscreen
input "2:7:SynPS/2_Synaptics_TouchPad" natural_scroll "enabled"

# Status Bar
bar {
    position top
    # Use i3status as status bar
    status_command i3status
}

# Custom key bindings

# Lock screen
bindsym $mod+Escape exec $lock

# Audio and brightness key bindings
bindsym XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5%
bindsym XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5%
bindsym XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ toggle
bindsym XF86AudioMicMute exec pactl set-source-mute @DEFAULT_SOURCE@ toggle
bindsym XF86MonBrightnessDown exec brightnessctl set 5%-
bindsym XF86MonBrightnessUp exec brightnessctl set +5%
bindsym XF86AudioPlay exec playerctl play-pause
bindsym XF86AudioNext exec playerctl next
bindsym XF86AudioPrev exec playerctl previous

# Bindings for Firefox and Thunderbird
bindsym $mod+Shift+b exec "env MOZ_ENABLE_WAYLAND=1 firefox"
bindsym $mod+Shift+m exec "thunderbird"

# Autostart

# Start dunst, a notification daemon
exec dunst

# Start some programs in fixed worspaces
assign [app_id="firefox"] → 1
exec "env MOZ_ENABLE_WAYLAND=1 firefox"
assign [class="thunderbird"] → 2
exec "thunderbird"

Picking an application launcher

The default application launcher to be used is dmenu (from suckless-tools). While it works okayish, I don't particularly like it. In my eyes, it looks rather old-fashioned, and even worse, it doesn't seem to have support for freedesktop.org desktop entries.

I looked around a bit and wofi sounded pretty promising. It's not in Debian yet but was easy to compile. A big downer though is that it depends on a newer libglib2.0 version (2.60) than in Debian Buster. I still compiled it in a Bullseye schroot and got a first impression. I like it's look and feel (after a bit CSS customization) and probably I'll go with packaging it for Debian.

For the moment, I'm stuck with dmenu on my working system, though.

Configure the status bar

I decided to go with the i3status status bar and it serves my purposes pretty well. Here's my config (/.config/i3status/config):

# i3status configuration file.
# see "man i3status" for documentation.

# It is important that this file is edited as UTF-8.
# The following line should contain a sharp s:
# ß
# If the above line is not correctly displayed, fix your editor first!

general {
        #colors = true
        colors = false
        interval = 5
}

order += "load"
order += "wireless _first_"
order += "ethernet _first_"
order += "path_exists VPN"
order += "battery all"
order += "tztime local"

# Customized wireless status
wireless _first_ {
        format_up = "W: (%quality at %essid) %ip"
        format_down = "W: down"
}

# Only show ethernet status when connected
ethernet _first_ {
        # if you use %speed, i3status requires root privileges
        format_up = "E: %ip"
        format_down = ""
}

# Display VPN status
path_exists VPN {
        # path exists when a VPN tunnel launched by nmcli/nm-applet is active
        path = "/proc/sys/net/ipv4/conf/tun0"
}

# Customized battery status
battery all {
        format = "%status %percentage"
        status_chr = "⚡"
        status_bat = "🔋"
        status_full = "☻"
}

# Localized time format
tztime local {
        #format = "%Y-%m-%d %H:%M:%S"
        format = "%a %d. %b %Y %H:%M"
}

load {
        format = "L: %1min"
}

Configure a notification daemon

I'm really used to getting notifications by my chat programs (XMPP, IRC, Signal), and I don't want to dismiss this. So I installed dunst and configured sway to auto-start it (see above). That's it, it worked instantly. Well, that was easy :)

Preserve working directory in new terminal instances

One thing that really annoyed me after switching to sway was, that the working directory wasn't preserved when spawning new terminal instances. I often open five or more terminal instances in parallel when working on a complex project, and I'm very used to just open a new terminal and continue working in the same directory there immediately.

So I was really eager to find a solution here. Turned out that it's not that easy and needs a bit of dirty scripting, but I found a solution (with help from some nice folks in #sway on Freenode).

First some words about the problem: spawning a new terminal in sway doesn't use whatever sophisticated means to spawn new instances of the same terminal process. Instead, it just spawns a fresh process of your favourite terminal emulator. While I really like tilix and used it as a tiling terminal emulator, I no longer want to use it's tiling features when I now have a tiling window manager. I'll stick for tilix for now as I like its look and feel, though.

So if the new terminal emulator process doesn't know about the working directory of your former terminal, what to do about it?

The solution: Luckily, it's possible to identify the PID of your focused window in sway using swaymsg -t get_tree. In case that the focused window is a terminal emulator, it's parent ID should be your shell. And the shells PWD can easily be determined by reading the symlink /proc/$PID/cwd.

So let's put this in a wrapper script under ~/.config/sway/scripts/tilix-wrapper.sh:

#!/bin/sh

# Small script that tries to determine the PWD of the focused terminal
# (in sway tiling window manager) and pass it to the newly spawned one.

TERMINAL_CMD="tilix --new-process"

FOCUSED_PID=""
if [ ! type jq 2>/dev/null ]; then
    echo "ERROR: jq not installed" >&2
else
    FOCUSED_PID="$(swaymsg -t get_tree | jq '.. | select(.type?) |
        select(.type=="con") | select(.focused==true).pid')"
fi

FOCUSED_PWD=""
# Check if $FOCUSED_PID is an integer
if [ "$FOCUSED_PID" -eq "$FOCUSED_PID" 2>/dev/null ]; then
    FOCUSED_PPID="$(ps -o pid= --ppid "$FOCUSED_PID" | awk '{print $1}')"
    if [ "$FOCUSED_PPID" -eq "$FOCUSED_PPID" 2>/dev/null ]; then
        FOCUSED_PWD="$(readlink "/proc/$FOCUSED_PPID/cwd")"
    fi
fi

# Spawn terminal in background
if [ -d "$FOCUSED_PWD" ]; then
    $TERMINAL_CMD --working-directory="$FOCUSED_PWD" $@ &
else
    $TERMINAL_CMD $@ &
fi

Finally, we have to set the script as $term in sways config (see above). Yay, now I've a solution to preserve my working directory when spawning new terminals!

Use gnome-keyring as SSH agent with sway

Another super annoying thing was that my SSH agent no longer worked with sway, mostly because I used gnome-keyring before and it wasn't spawned automatically when starting sway. So let's change that. I found it a bit complicated to get this working as docs on the internet said a lot of different things, but in the end, the following worked.

Since I still use gdm3 as desktop manager, gnome-keyring-daemon is started automatically during login. So the only thing that's missing is to initalize the gnome-keyring-daemon when starting a terminal. To do so, add the following to ~/.profile (in order to only do it on a login shell):

# Connect to and initalize gnome-keyring-daemon when in sway session
if [ "$DESKTOP_SESSION" = "sway" ]; then
    export $(gnome-keyring-daemon --start)
fi

What's missing

  • I want to start profanity (XMPP client) and irssi (IRC client) automatically in workspace 3, but so far I failed to find a working filter for sways assign feature to identify tilix instances with profanity/irssi (in order to automatically assign those terminals to workspace 3).
  • I miss the redshift feature of gnome 3. redshift itself doesn't support wayland yet. There's a fork with wayland support, but I didn't find time to look into it yet.
  • I'll probably switch from i3status to py3status soon as it's list of modules looks really promising.
Posted Thu 05 Dec 2019 02:10:00 PM UTC Tags:

Debian LTS report for October 2019

This month I was allocated 0 hours and carried over 14.5 hours from August. Unfortunately, once again I didn't find time to work on LTS issues. Since I expect it to stay that way for a few more months, I set the limit of hours that I get allocated to 0 last month already. I'll give back the remaining 14.5 hours and continue with LTS work once I again have some spare cycles to do so.

Posted Tue 29 Oct 2019 02:47:03 PM UTC Tags:

Debian LTS report for September 2019

This month I was allocated 10 hours and carried over 9.5 hours from August. Unfortunately, again I didn't find much time to work on LTS issues, partially because I was travelling. I spent 5 hours on the task listed below. That means that I carry over 14.5 hours to October.

Posted Tue 08 Oct 2019 02:34:42 PM UTC Tags:

Debian LTS report for August 2019

This month I was allocated 10 hours. Unfortunately, I didn't find much time to work on LTS issues, so I only spent 0.5 hours on the task listed below. That means that I carry over 9.5 hours to September.

  • Triaged CVE-2019-13640/qbittorrent: After digging through the code, it became obvious that qbittorrent 3.1.10 in Debian Jessie is not affected by this vulnerability as the affected code is not present yet.
Posted Thu 12 Sep 2019 01:13:11 PM UTC Tags:

Debian LTS report for July 2019

This month I was allocated 17 hours. I also had 2 hours left over from Juney, which makes a total of 19 hours. I spent all of them on the following tasks/ issues.

  • DLA-1843-1: Fixed CVE-2019-10162 and CVE-2019-10163 in pdns.
  • DLA-1852-1: Fixed CVE-2019-9948 in python3.4. Also found, debugged and fixed several further regressions in the former CVE-2019-9740 patches.
  • Improved testing of LTS uploads: We had some internal discussion in the Debian LTS team on how to improve the overall quality of LTS security uploads by doing more (semi-)automated testing of the packages before uploading them to jessie-security. I tried to summarize the internal discussion, bringing it to the public debian-lts mailinglist. I also did a lot of testing and worked on Jessie support in Salsa-CI. Now that salsa-ci-team/images MR !74 and ci-team/debci MR !89 got merged, we only have to wait for a new debci release in order to enable autopkgtest Jessie support in Salsa-CI. Afterwards, we can use the Salsa-CI pipeline for (semi-)automatic testing of packages targeted at jessie-security.
Posted Sat 03 Aug 2019 03:29:23 PM UTC Tags:

Debian LTS report for June 2019

This month I was allocated 17 hours. I also had 1.75 hours left over from May, which makes a total of 18.75 hours. I spent 16.75h of them on the following issues, which means I again carry over 2h to the next month.

Posted Mon 01 Jul 2019 12:59:09 PM UTC Tags:

Debian LTS report for May 2019

This month I was allocated 17 hours. I spent 15.25 hours on the following issues:

  • DLA 1766-1: OpenPGP signature spoofing in evolution. On this issue I actually spent way more time than expected during April. I took over some of the remaining hours to May.
  • DLA 1778-1: Several vulnerabilities in symfony, a PHP web application framework.
  • DLA 1791-1: Several vulnerabilities in drupal7, a PHP web site platform.
Posted Tue 04 Jun 2019 05:24:31 PM UTC Tags:

Debian LTS report for April 2019

After a longer break (~two years) I again took part in the funded Debian LTS project in April 2019.

I was allocated 14 hours and spent all of them (and even a bit more) on the following two issues:

  • DLA 1748-1: Several security fixes for apache2
  • DLA 1766-1: OpenPGP signature spoofing in evolution
Posted Fri 26 Apr 2019 09:20:47 PM UTC Tags:

Cryptsetup sprint report

The Cryptsetup team – consisting of Guilhem and Jonas – met on June 15 to 17 in order to work on the Debian cryptsetup packages. We ended up working three days (and nights) on the packages, refactored the whole initramfs integration, the SysVinit init scripts and the package build process and discussed numerous potential improvements as well as new features. The whole sprint was great fun and we enjoyed a lot sitting next to each other, being able to discuss design questions and implementation details in person instead of using clunky internet communication means. Besides, we had very nice and interesting chats, contacted other Debian folks from the Frankfurt area and met with jfs on Friday evening.

Splitting cryptsetup into cryptsetup-run and cryptsetup-initramfs

First we split the cryptsetup initramfs integration into a separate package cryptsetup-initramfs. The package that contains other Debian specific features like SysVinit scripts, keyscripts, etc. now is called cryptsetup-run and cryptsetup itself is a mere metapackage depending on both split off packages. So from now on, people can install cryptsetup-run if they don't need the cryptsetup initramfs integration. Once Buster is released we intend to rename cryptsetup-run to cryptsetup, which then will no longer have a strict dependency on cryptsetup-initramfs. This transition over two releases is necessary to avoid unexpected breakage on (dist-)upgrades. Meanwhile cryptsetup-initramfs ships a hook that upon generation of a new initramfs image detects which devices need to be unlocked early in the boot process and, in case it didn't find any, suggests the user to remove the package.

The package split allows us to define more fine-grained dependencies: since there are valid use case for wanting the cryptsetup binaries scripts but not the initramfs integration (in particular, on systems without encrypted root device), cryptsetup ≤2:2.0.2-1 was merely recommending initramfs-tools and busybox, while cryptsetup-initramfs now has hard dependencies on these packages.

We also updated the packages to latest upstream release and uploaded 2:2.0.3-1 on Friday shortly before 15:00 UTC. Due to the cryptsetup → cryptsetup-{run,initramfs} package split we hit the NEW queue, and it was manually approved by an ftpmaster… a mere 2h later. Kudos to them! That allowed us to continue with subsequent uploads during the following days, which was beyond our expectations for this sprint :-)

Extensive refactoring work

Afterwards we started working on and merging some heavy refactoring commits that touched almost all parts of the packages. First was a refactoring of the whole cryptsetup initramfs implementation that downsized both the cryptroot hook and script dramatically (less than half the size they were before). The logic to detect crypto disks was changed from parsing /etc/fstab to /proc/mounts and now the sysfs(5) block hierarchy is used to detect dm-crypt device dependencies. A lot of code duplication between the initramfs script and the SysVinit init script was removed by outsourcing common functions into a shared shell functions include file that is sourced by initramfs and SysVinit scripts. To complete the package refactoring, we also overhauled the build process by migrating it to the latest Debhelper 11 style. debian/rules as well was downsized to less than half the size and as an extra benefit we now run the upstream build-time testsuite during the package build process.

Some git statistics speak more than a thousand words:

$ git --no-pager diff --ignore-space-change --shortstat debian/2%2.0.2-1..debian/2%2.0.3-2 -- ./debian/
 92 files changed, 2247 insertions(+), 3180 deletions(-)
$ find ./debian -type f \! -path ./debian/changelog -print0 | xargs -r0 cat | wc -l
7342
$ find ./debian -type f \! -path ./debian/changelog -printf x | wc -c
106

On CVE-2016-4484

Since 2:1.7.3-2, our initramfs boot script went to sleep for a full minute when the number of failed unlocking attempts exceeds the configured value (tries crypttab(5) option, which defaults to 3). This was added in order to defeat local brute force attacks, and mitigate one aspect of CVE-2016-4484; back then Jonas wrote a blog post to cover that story. Starting with 2:2.0.3-2 we changed this behavior and the script will now sleep for one second after each unsuccessful unlocking attempt. The new value should provide better user experience while still offering protection against local brute force attacks for very fast password hashing functions. The other aspect mentioned in the security advisory — namely the fact that the initramfs boot process drops to a root (rescue/debug) shell after the user fails to unlock the root device too many times — was not addressed at the time, and still isn't. initramfs-tools has a boot parameter panic=<sec> to disable the debug shell, and while setting this is beyond the scope of cryptsetup, we're planing to ask the initramfs-tools maintainers to change the default. (Of course setting panic=<sec> alone doesn't gain much, and one would need to lock down the full boot chain, including BIOS and boot loader.)

New features (work started)

Apart from the refactoring work we started/continued work on several new features:

  • We started to integrate luksSuspend support into system suspend. The idea is to luksSuspend all dm-crypt devices before suspending the machine in order to protect the storage in suspend mode. In theory, this seemed as simple as creating a minimal chroot in ramfs with the tools required to unlock (luksResume) the disks after machine resume, running luksSuspend from that chroot, putting the machine into suspend mode and running luksResume after it got resumed. Unfortunately it turned out to be way more complicated due to unpredictable race conditions between luksSuspend and machine suspend. So we ended up spending quite some time on debugging (and understanding) the issue. In the end it seems like the final sync() before machine suspend ( https://lwn.net/Articles/582648/ ) causes races in some cases as the dm-crypt device to be synced to is already luksSuspended. We ended up sending a request for help to the dm-crypt mailinglist but unfortunately so far didn't get a helpful response yet.
  • In order to get internationalization support for the messages and password prompts in the initramfs scripts, we patched gettext and locale support into initramfs-tools.
  • We started some preliminary work on adding beep support to the cryptsetup initramfs and sysVinit scripts for better accessibility support.

The above features are not available in the current Debian package yet, but we hope they will be included in a future release.

Bugs and Documentation

We also squashed quite some longstanding bugs and improved the crypttab(5) documentation. In total, we squashed 18 bugs during the sprint, the oldest one being from June 2013.

On the need for better QA

In addition to the many crypttab(5) options we also support a huge variety of block device stacks, such as LUKS-LVM2-MD combined in all ways one can possibly imagine. And that's a Debian addition hence something we, the cryptsetup package maintainers, have to develop and maintain ourselves. The many possibilities imply corner cases (it's not a surprise that complex or unusual setups can break in subtle ways) which motivated us to completely refactor the Debian-specific code, so it becomes easier to maintain.

While our final upload squashed 18 bugs, it also introduced new ones. In particular 2 rather serious regressions which fell through our tests. We have thorough tests for the most usual setups, as well as for some complex stacks we hand-crafted in order to detect corner cases, but this approach doesn't scale to covering the full spectrum of user setups: even with minimal sid installations the disk images would just take far too much space! Ideally we would have a automated test-suite, each test deploying a new transient sid VM with a particular setup. As the current and past regressions show, that's a beyond-the-scenes area we should work on. (In fact that's an effort we started already, but didn't touch during the sprint due to lack of time.)

More to come

There's some more things on our list that we didn't find time to work on. Apart from the unfinished new features we mentioned above, that's mainly the LUKS nuke feature that Kali Linux ships and the lack of keyscripts support to crypttab(5) in systemd.

Conclusion

In our eyes, the sprint was both a great success and great fun. We definitely want to repeat it anytime soon in order to further work on the open tasks and further improve the Debian cryptsetup package. There's still plenty of work to be done. We thank the Debian project and its generous donors for funding Guilhem's travel expenses.

Guilhem and Jonas, June 25th 2018

Posted Tue 26 Jun 2018 02:26:42 PM UTC Tags:

Debian LTS report for March 2017

March 2017 was my seventh month as a Debian LTS team member. I was allocated 14,75 hours and spent 11,25 of them on the following tasks:

  • DLA 836-2: Regression update for munin
  • DLA 869-1: Several security fixes for cgiemail
  • tested packaged prepared by other LTS team members against regressions
  • libical: bug triaging, testing reproducers
  • putty: tested reproducers, backported patches (not finished yet)
Posted Mon 03 Apr 2017 01:37:33 PM UTC Tags:

This blog is powered by ikiwiki.