Packaging a Rust project for Debian

25/01/20 — capitol


I have started to package rust things for Debian, and the process have been pretty smooth so far, but it was very hard finding information on how to start, so here is a small writeup on how I packaged my first rust crate for Debian.

This was a totally uncomplicated crate without anything special in it.

1) Add my name and email to .bashrc

DEBFULLNAME="First-name Last-name"

We also added a CHROOT variable, this is explained in point 9.

2) git clone

All Debian’s rust package information lives in one big repository on

3) cd debcargo-conf

4) ./ crate-name

This is the script that does most of the work, it looks at the crate named crate-name on and pre-populates what it can in the files in the src/crate-name/debian/ directory.

5) cd src/crate-name/debian/

The Debian directory is where all the meta information that Debian needs about a package lives.

6) cp copyright.debcargo.hint copyright

The script tries to obtain the correct copyright information, but it guesses a lot and often needs to be touched up afterwards. The original guess work is in copyright.debcargo.hint and should not be modified, but committed as is.

7) Fix copyright

After copying the original guess we need to go over it and fix the things it didn’t manage to generate correctly.

Common things to fix is:

  • The years that the work was produced in, as this information isn’t in the crate. I normally look at the git repository and take the years for the first and last commit.
  • Copyright for the license files, it often singles out the license files and have those as separate sections. It’s unclear if the license text is under copyright, most likely not, and it should just be deleted from the file.
  • Add license text. It the crate only refers to a license, without including the text in a license file, and it’s not one of Debian’s well known licenses, then it needs to be added manually.

8) Add dependencies to native packages like this in debcargo.toml

If your rust code depends on a native library, then you need to tell specify the name of that Debian package.

depends = ["libyaml-dev"]

9) Setup a repeatable build env.

We need to test that the crate can be built without problems, and for that we need a build environment.

sudo apt install devscripts reprepro debootstrap sbuild dh-cargo
sudo sbuild-createchroot --include=eatmydata,ccache,gnupg,dh-cargo,cargo,lintian,perl-openssl-defaults \
      --chroot-prefix debcargo-unstable unstable \

10) cd ../../../build && ./ crate-name

This performs a test build of the crate, and runs the lintian tool in order to detect common mistakes.

These two lintian failures always happens with new crates, and can be ignored:

E: rust-gpgme-sys changes: bad-distribution-in-changes-file UNRELEASED-FIXME-AUTOGENERATED-DEBCARGO
W: librust-gpgme-sys-dev: new-package-should-close-itp-bug

11) Add RFS file

When everything builds and lintian doesn’t complain, then it’s time to ask a Debian maintainer to upload the package to Debian. This is done by adding a file named RFS to the Debian directory.

touch ../src/crate-name/debian/RFS

12) Add everything to a branch

cd ..
git branch package-crate-name
git checkout package-crate-name
git add .
git commit -m "package version X.Y.Z of crate-name"

13) git push

Push your branch to your fork of the debcargo-conf project, and create a pull request from it.

14) Join #debian-rust on on irc

The people in the channel was really helpful and answered all my stupid questions about how the process works when I tried to learn it.

Release of Ripasso version 0.3.0

01/12/19 — capitol


We have just released version 0.3.0 of Ripasso, a password manager that lets you control the level of risk that you expose your passwords to.

New Features

Support for signing Git commits, if it’s configured in Git’s config

If you set the Git configuration values commit.gpgsign and user.signingkey, then Ripasso will respect them when creating Git commits and signing those.

Display who touched a password last

If the password store is backed by a Git repository, then Ripasso will read and display who changed a password last.

Support for initializing a Git repo in the quick start wizard

If you start Ripasso without a password store directory you will get a guide that helps you get set up. That guide now also gives you the opportunity to initialize a Git repository.

Added a status bar, and a menu

We reworked to information at the bottom of the screen, moved the shortcuts into a menu and added a status bar that displays what’s happening in the application.

Bugs Fixed

Made Ctrl-W behave as in the shell, so that you can delete last typed word with it.

Fixed performance problem if the Git repo was large

Ripasso initialized the Git repository once for every operation that it did, which was very slow. Ownership of the Git repository object have now been moved so that it will only be initialized once.

Fixed problem with passwords that contained a . character

Newly created passwords that contained a . character weren’t accessible without a restart.

Install Instructions

Arch Linux

Arch now has two packages, ripasso-git that tracks the development branch and ripasso-cursive that contains the stable version.

yay install ripasso-git


yay install ripasso-cursive


nix-env -iA nixpkgs.ripasso-cursive


git clone
cd ripasso
cargo build -p ripasso-cursive


  • Joakim Lundborg - developer
  • Alexander Kjäll - developer
  • Stig Palmquist - NixOS packager
  • Tae Sandoval - NixOS macos packager
  • David Plassman - Arch packager

Also a big thanks to everyone who contributed with bug reports and patches.

Signing git commits in Rust

26/11/19 — capitol


This weekend I managed to get my rust code to produce signed gpg commits, and since that wasn’t trivial I thought I should do a small writeup about it.

Why sign your git commits?

Since git is a distributed version control system, there is no central point of authority that will control what you write in the committer field. One way to reduce the attack surface is to sign your commits with your gpg key. That way a bad actor needs to steal both your gpg key and gain write access to the repository in order to impersonate you.

What is actually being signed?

We don’t sign the actual bytes that make up the commit inside the git data structure, but rather a text representation of the commit, which looks like this:

tree 996fdae121be5db0fad96f3f9a82bec92109acd4
parent 509d883c16dbb7f48b949457cdee0d74ab01a5d3
author Alexander Kjäll <> 1574528313 +0100
committer Alexander Kjäll <> 1574528313 +0100

Signing that with gpg produces another text blob which you can add to the git commit.



How to tell git to sign commits?

Communicating to git that you want to sign your commits with gpg is done with two configuration parameters. A boolean commit.gpgsign which determines if you want to sign it at all and a string user.signingkey that contains the keyid of the gpg key to sign with.

Those are easily read in rust like this:

let config = git2::Config::open_default()?;

let signing_key = config.get_string("user.signingkey")?;

What does the code look like?

I use the excellent git2 library, rust bindings for the libgit2 library, which is one of the libraries that powers GitHub.

The two most important functions are commit_create_buffer and commit_signed. The first one produces the text representation of the commit, and the second does the actual commit to disk.

The rust code looks like this:

let commit_buf = (*repo_opt).as_ref().unwrap().commit_create_buffer(
    &signature, // author
    &signature, // committer
    message, // commit message
    &tree, // tree
    &parents)?; // parents

let commit_as_str = str::from_utf8(&commit_buf).unwrap().to_string();

let sig = gpg_sign_string(&commit_as_str)?;

let commit = (*repo_opt).as_ref().unwrap().commit_signed(&commit_as_str, &sig, Some("gpgsig"));

Where gpg_sign_string is my own function that handles the gpg signing business.

And that leads us to the gpg situation.

Signing a string with gpgme

I used the gpgme library, and that isn’t a very nice library to work with, it gets the job done, but it has some nasty pitfalls.

The code ended up like this:

pub fn gpg_sign_string(commit: &String) -> Result<String> {
    let config = git2::Config::open_default()?;

    let signing_key = config.get_string("user.signingkey")?;

    let mut ctx = gpgme::Context::from_protocol(gpgme::Protocol::OpenPgp)?;
    let key = ctx.get_secret_key(signing_key)?;

    let mut output = Vec::new();
    let signature = ctx.sign_detached(commit.clone(), &mut output);

    if signature.is_err() {
        return Err(Error::GPG(signature.unwrap_err()));

    return Ok(String::from_utf8(output)?);

And the API has some design problems that makes it harder than necessary to use:

  • The sign_detached method returns its output in the Vec that you supply as the second argument instead of simply returning it.
  • If you forget the mut modifier on the argument to the sign_detached method, the code compiles without problem, and you get this runtime error: Bad file descriptor (gpg error 32779). That really doesn’t help explaining what’s going on at all.

But aside from those gripes, it gets the job done.

I hope I will have time to investigate the new openpgp library sequoia in the future. It looks like it might be a good replacement.

Packaging an executable Python script nicely in Nix for NixOS

05/11/19 — sshow

This is a cross-post from

I was making a Python script as a backend to an Arduino project I did. As usual I got the idea of upping the ante in my hobby project, this time by packaging and running that backend on NixOS.

This was a great way for me to learn more about NixOS, but also a way to discover the difficulties of getting into it from scratch. I want to share the .nix files I ended up with, and how I incorporated it into my NixOS config, in the hope that I can speed things up for someone else getting to know Nix.

I will not go deep into how the language Nix works, but rather comment a tiny bit on what I’ve done that I feel is noteworthy for the new citizens of NixOS.

Building a custom Python package in NixOS

Python project

My Python project consists of just a few files, of which the below are the only ones that matter for the sake of this post.

├── tests
│   ├── test_data.json
│   └──
├── requirements.txt

The file is a small file that leans on the Python 3 built-in library distuils, which is used to define an installable Python package. Most importantly, it defines what scripts that should be added to the PATH, i.e. executable scripts.

from distutils.core import setup


This file will let you “install” the package/script by running python install, which is also how the Nix function python37Packages.buildPythonPackage builds Python packages by default.

Since the script will be installed as an executable, I have to define a suitable hashbang at the top of

#!/usr/bin/env python3

I will not be locking down my python library dependencies to specific versions right now, but rather just pull in the (latest) versions from the Nix package repository. The contents of my requirements.txt are:


You will see these being defined explicitly in the Nix function buildPythonPackage later on. They will not be installed using pip.

Before continuing, you should verify that running python install works as intended. Maybe you have to run pip install -r requirements.txt on your development system first.


You can start out in a new directory wherever you’d like.

Let’s start out with the default.nix file. This file will describe the package itself, where and how to get its sources, what dependencies to inject and how to actually build it.

# Below, we can supply defaults for the function arguments to make the script
# runnable with `nix-build` without having to supply arguments manually.
# Also, this lets me build with Python 3.7 by default, but makes it easy
# to change the python version for customised builds (e.g. testing).
{ nixpkgs ? import <nixpkgs> {}, pythonPkgs ? nixpkgs.pkgs.python37Packages }:

  # This takes all Nix packages into this scope
  inherit (nixpkgs) pkgs;
  # This takes all Python packages from the selected version into this scope.
  inherit pythonPkgs;

  # Inject dependencies into the build function
  f = { buildPythonPackage, bottle, requests }:
    buildPythonPackage rec {
      pname = "ruterstop";
      version = "0.0.1";

      # If you have your sources locally, you can specify a path
      #src = /home/stigok/src/ruterstop

      # Pull source from a Git server. Optionally select a specific `ref` (e.g. branch),
      # or `rev` revision hash.
      src = builtins.fetchGit {
        url = "git://";
        ref = "master";
        #rev = "a9a4cd60e609ed3471b4b8fac8958d009053260d";

      # Specify runtime dependencies for the package
      propagatedBuildInputs = [ bottle requests ];

      # If no `checkPhase` is specified, `python test` is executed
      # by default as long as `doCheck` is true (the default).
      # I want to run my tests in a different way:
      checkPhase = ''
        python -m unittest tests/*.py

      # Meta information for the package
      meta = {
        description = ''
          Realtime stop info for public transport in Oslo, using the EnTur JourneyPlanner API

  drv = pythonPkgs.callPackage f {};
  if pkgs.lib.inNixShell then drv.env else drv
  • You can see if your package file compiles with nix-instantiate --eval default.nix
  • You can build it and look at the resulting package with nix-build default.nix. A symlink results will be created in your working directory.

I want to run my service on boot with systemd. Next section takes on defining that service file.


Create this file right next to default.nix

{ config, lib, pkgs, ... }:

        # The package itself. It resolves to the package installation directory.
        ruterstop = pkgs.callPackage ./default.nix {};

        # An object containing user configuration (in /etc/nixos/configuration.nix)
        cfg =;

        # Build a command line argument if user chose direction option
        directionArg = if cfg.direction == ""
                          then ""
                          else "--direction=${cfg.direction} ";
in {
    # Create the main option to toggle the service state = lib.mkEnableOption "ruterstop";

    # The following are the options we enable the user to configure for this
    # package.
    # These options can be defined or overriden from the system configuration
    # file at /etc/nixos/configuration.nix
    # The active configuration parameters are available to us through the `cfg`
    # expression. = lib.mkOption {
        type = lib.types.str;
        default = "";
        example = "";
    }; = lib.mkOption {
        type =;
        default = 4000;
    }; = lib.mkOption {
        type = lib.types.str;
        example = "6013";
    }; = lib.mkOption {
        type = lib.types.str;
        default = "";
        example = "inbound";
    }; = lib.mkOption {
        type = lib.types.str;
        default = "";
        example = "inbound";
    }; = lib.mkOption {
        type = lib.types.listOf lib.types.str;
        default = [""];
        example = ["--debug"];

    # Everything that should be done when/if the service is enabled
    config = lib.mkIf cfg.enable {
        # Open selected port in the firewall.
        # We can reference the port that the user configured.
        networking.firewall.allowedTCPPorts = [ cfg.port ];

        # Describe the systemd service file = {
            description = "Et program som viser sanntidsinformasjon for stoppesteder i Oslo og Akershus.";
            environment = {
                PYTHONUNBUFFERED = "1";

            # Wait not only for network configuration, but for it to be online.
            # The functionality of this target is dependent on the system's
            # network manager.
            # Replace the below targets with if you're unsure.
            after = [ "" ];
            wantedBy = [ "" ];

            # Many of the security options defined here are described
            # in the systemd.exec(5) manual page
            # The main point is to give it as few privileges as possible.
            # This service should only need to talk HTTP on a high numbered port
            # -- not much more.
            serviceConfig = {
                DynamicUser = "true";
                PrivateDevices = "true";
                ProtectKernelTunables = "true";
                ProtectKernelModules = "true";
                ProtectControlGroups = "true";
                RestrictAddressFamilies = "AF_INET AF_INET6";
                LockPersonality = "true";
                RestrictRealtime = "true";
                SystemCallFilter = "@system-service @network-io @signal";
                SystemCallErrorNumber = "EPERM";
                # See how we can reference the installation path of the package,
                # along with all configured options.
                # The package expression `ruterstop` expands to the root
                # installation path.
                ExecStart = "${ruterstop}/bin/ --server --host ${} --port ${toString cfg.port} --stop-id ${cfg.stop-id} ${directionArg}${lib.concatStringsSep " " cfg.extraArgs}";
                Restart = "always";
                RestartSec = "5";

Adding the package to the system

Now the service can be registered in the system configuration. Since the package itself is defined by the service file, it’s not necessary to import default.nix.

Open /etc/nixos/configuration.nix and import the service.nix file. Somewhere up at the top is the imports section. Add the path to the service file there.

imports =
    /path/to/your/ruterstop/service.nix # <--

Then, further down the file, where appropriate for your taste, enable the service and service options.

services.ruterstop.enable = true;
services.ruterstop.stop-id = "6013";
services.ruterstop.direction = "outbound";
services.ruterstop.extraArgs = ["--debug"];

Now, try to rebuild the system and switch to the new configuration immediately by using the switch argument.

# nixos-rebuild switch

If everything went right, that should have started the service and enabled it to start on boot. Check the service log to see if it runs alright.

# journalctl -u ruterstop.service

That should be it! Comments are very welcome!


Solution to SquareCTF 2019 - Talk To Me

19/10/19 — capitol

Talk To Me


general - ruby





We got a telnet interface to a small chat bot, but it didn’t understand anything we said to it at first.

Sending it characters that wasn’t in [A-Za-z] gave a more interesting result:

(eval):1: syntax error, unexpected end-of-input, expecting ')'
/talk_to_me.rb:16:in `eval'
/talk_to_me.rb:16:in `receive_data'
/var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run_machine'
/var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run'
/talk_to_me.rb:31:in `<main>'

So obviously they ran the input through eval. This led us down a rabbit hole of trying to get a reverse shell working, but that was blocked.

And then one of us noticed that the chat bot asked us to greet it as it had greeted us, so the problem was reduced to how to send it something that could pass the first check for [A-Za-z] and then be evaluated to the string ‘Hello!’.

Since none of us where ruby programmers this took quite a while to figure out. The solution we came up with in the end was:

$ (sleep 1; echo "('' << 72)+('' << 101)+( '' << 108)+( '' << 108)+('' << 111)+('' << 33)") | nc -v 5678

The flag was flag-2b8f1139b0726726.