pancake

A millionth take on a development environment container

After I learned how easy and convenient containers are I started to think about the amount of software I install from volatile sources (think of unversioned pip installs).

This year the industry had several major security incidents made possible by open-source (non-)supply-chain attacks. Malware targeting everything from cryptocurrency through Discord credentials to access tokens is very common. Being careful is not good enough, all dependency chains of all your projects have to be careful as well. See CVE-2024-YIKES by the Andrew Nesbitt for an amazing illustration of the reality.

My work setup for containerized environment is very specialized, and is not transferrable beyond my machine and the layout of my git repositories. On my personal machine, I only do lightweight development, but I started to miss the good feeling of having total isolation between indirect libraries I never saw a single line of source code from, and my vacation photos.

Devcontainer managers

Distrobox exists. It works great if you aim to have an environment with software you don't need on the host, you need to target a different distribution, or your host is immutable. However, isolation is a non-goal for Distrobox. Most people want to have access to their SSH keys when they shell in and do development; I like to keep distance and separation.

My newest project, deco, is a solution from myself for myself. Instead of having opinions about how the containers are interacted with, it acts as an execution manager: interactions with Podman are explicit and hackable.

Using deco

To create a new project, run deco create: it will generate a bunch of files for you.

$ deco create --name demo
$ ls ~/.local/share/deco/demo/
autorun.sh  build.sh  Containerfile
install.sh  shell.sh  start.sh

Containerfile contains the recipe for the build, and install.sh the actual installation steps. Installation could be done in the Containerfile, but the syntax sucks, I prefer to write a regular shell script I copy into the container and execute it in there.

autorun.sh also gets copied in, and is executed every time you start the project. At work, this step copies configuration files and systemd units from bind-mounted git repositories into the container and sets them up, compiles some of the software, and configures it to follow internal company policies. This way I pay the setup tax just once: programs download their dependencies into a cache and get entrypoints created in /usr/bin/, just as if they were installed.

Other generated shell scripts, build.sh, start.sh and shell.sh, are simple wrappers around Podman.

By default, whole $HOME is mounted into the container for convenience. If you are interested in isolating your host user from 3rd party code installed in the container, edit start.sh's volume mount:

- --volume="$HOME":"$HOME":rw \
+ --volume="$HOME/github.com":"$HOME/github.com":rw \

Anything you can do with a shell you can do here as well. deco is providing execution environment; it will never edit those files unless you run create --force to overwrite them. Everything is stored under ~/.local/share/deco/; you can make this directory a git repository and synchronize it across multiple devices -- the same environment will be git pull && deco build && deco run away.

Closing words

I will be using deco in my following projects, and I expect to make minor adjustments and improvements as I do.

For now I am happy with how simple and understandable it is.

#containers #isolation #podman #security