Stackable Wrappers

Ex Debian dev Michael Stapelberg discusses package install hooks and triggers and how they complicate the package manager and potentially slow down the install process. An exciting development he mentions is “hermetic packages” which is discussed briefly under the heading “Implemented in an interpreted language”. This seems to be a framework for defining wrappers in a compiled language instead of bash. Worth reaching out and discussing his views and work which can help with the stacked wrappers proposal.

1 Like

He had an excellent talk on the subject of package manager speed. German language. Not sure automated translation will be good enough.

GPN19 - Linux package manager sind zu langsam! - YouTube


Contacted.

stackable wrapper, .d configuration drop-in folders:
https://www.whonix.org/pipermail/whonix-devel/2019-August/001423.html

1 Like

Could you please provide feedback for this stackable wrappers proposal?

1 Like

There wasn’t much feedback on stackable wrappers from Michael Stapelberg.

Other stackable wrappers resources:

Some suggestions for the config language you can potentially use. I am not technically apt in this space so excuse any simplistic suggestions.

  • How the wrappers would be specified in the config language is yet to be invented, which will be done if this implementation path looks favorable.
  • You would invoke the master wrapper script by specifying “–mwrap” in a program’s cli command. mwrap is short for mega wrapper.

  • Somehow preserve the use of mwrap with software across their updates.

  • mwrap would assume a default “wrapper.d” dir is present in the programs “etc” subfolder. This can be extended however to add other arbitrary dirs for it to look into and to chain wrappers contained therein in lexical order as you suggested.

  • The default wrapper.d location should be overridable in case it is a portable program or one installed in the home folder like TBB or /opt.

  • The order of custom wrapper folders should be interpreted according to how they are fed to the command.

  • If a duplicate wrapper script is found in two different folders, mwrap should exit with an error telling the user to change the file name before it can be applied.

  • It should be designed to run different instances per each program that uses it to avoid situations of privilege escalation. Think a malicious program in home folder can feed the mwrap instance running under sudo with arbitrary scripts if it is already used by a binary with folders in /etc/

1 Like

Can’t we implement this ourselves without requiring upstream?

Create a wrapper that reads from /etc/wrapper.d/ and have wrapper_pre (for commands to be added before the actual program) and wrapper_post (for commands to be added after) variables based on the contents of the files there so you have:

${wrapper_pre}${program_name}${wrapper_post}

Create symlinks to run the program with our stackable wrapper by default and configure the program via wrapper.d.

So the program has one, configurable wrapper that wraps the program with other wrappers.

1 Like

An implementation of what I said above would be the following.

Create a file called /usr/bin/stackable-wrapper and add:

#!/bin/bash

sw_dir="/etc/wrapper.d"
program_name="$(basename $0)"

if [ -f "${sw_dir}/${program_name}.conf" ]; then
  . "${sw_dir}/${program_name}.conf"
else
  echo "ERROR: File ${sw_dir}/${program_name}.conf doesn't exist!"
  exit 1
fi

# So we don't execute ourselves.
PATH="$(echo ${PATH} | sed -e 's/\/usr\/local\/bin\://g')"

${wrapper_pre} ${program_name} ${wrapper_post}

Then create the config file e.g. /etc/wrapper.d/gpg.conf:

wrapper_pre="torsocks firejail"
wrapper_post="--example"

Then symlink it:

ln -s /usr/bin/stackable-wrapper /usr/local/bin/gpg

Should work fine.

We don’t have to use /usr/local/bin/. We can create our own directory for this and modify $PATH to check it first.

1 Like

For sure possible but then more likely not a solid, sustainable, long term maintainable solution. By asking upstream(s) for input we can avoid running into later one hard to fix issues. Also if we’d manage to make others use this too, then they won’t be inventing their own custom wrappers which then would be incompatible with other wrappers / our wrappers.

proposals/634-stackable-wrappers.txt at master · Kicksecure/proposals · GitHub on subject of $PATH currently mentions:

  • amend PATH environment variable …
    ** …
    ** Does not work for services started by init / systemd.

It’s not easy to change environment variables (PATH) globally in all cases. Such cases include login in virtual console, X, run by systemd services (root), run by systemd services (user), run by cron, and whatnot. Though, that may be possible.

Changing PATH may also cause AppArmor issues? Dunno yet. Needs investigation. “Ideally” stackable wrappers won’t break existing MAC confinements.

Related:

Needs more than a single ${wrapper_pre}. Needs to be stackable.

Here are some examples of commands to prepend:

  • firejail firefox
  • torsocks gpg
  • LD_PRELOAD=“$LD_PRELOAD”:libeatmydata.so rsync [ld preload hardened malloc]
  • bindp, timeprivacy and probably a lot more
  • probably quite some dpkg diversions used for that purpose

Newer examples:

  • hardend kernel apt (hkapt)
  • rapt (restricted apt)

Here are some examples of commands to append:

  • for ZeroNet it would be useful to always automatically append for example --tor always

I don’t understand.
For now, the idea was the wrappers to be transparent. Adding functionality (such as stream isolation, MAC confinement, etc.) but not requiring laymen user to be aware of it.

For sure. Wrappers absolutely must survive upgrades of applications these are wrapping. Needs to be added to proposal or maybe separate discussion summary document as a defined goal.

Could use the same folders as systemd config usually uses.

A config option to expand which folders will be parsed? That’s extra complexity. Not sure what would that be useful for?

/opt (and other uncommon PATHs) is a good point. If we go for PATH variable modification to implement this then all folders should be covered. It however will probably not be possible to wrap direct calls to /opt/pkg-name/binary-name. But that should be OK. Wrappers will probably only be shipped for select applications where we know where these are installed.

Duplicate detection is hard. Two different config files that both want to prepend torsocks or something? Very few applications guard against messed up configs. The ones who ship wrapper configs need to know what they are doing.

Not sure I fully understood that threat model.
However, yes, binaries in /home are another interesting question.
I guess stackable wrappers could parse $HOME/.stackable-wrappers.d or something if that exists.

  • if run with sudo: then all bets of off anyhow. A compromised home folder would mean having failed already anyhow.
  • if not without sudo: then there’s no privilege escalation. $HOME/.stackable-wrappers.d would not execute anything that the user could not execute manually anyhow.
  • if run by root: it would parse /root/.stackable-wrappers.d (if exists) which would be safe too
  • if run by systemd / cron: there’s probably in most cases no $HOME and if there was it should not be writeable by those system users having no business there

Well, “malicious program in home folder” + sudo = fail anyhow?

2 Likes

I don’t see why it would cause apparmor issues.

No, you can stack commands with a single ${wrapper_pre}. See the example above in which torsocks and firejail are both used for gpg.

If you want multiple drop-in config files, we can modify the script to source ${sw_dir}/${program_name}_*.conf. So for example, to stack torsocks and firejail using multiple config files you can create /etc/wrapper.d/gpg_torsocks.conf and add:

wrapper_pre+="torsocks"

then create /etc/wrapper.d/gpg_firejail.conf and add

wrapper_pre+="firejail"

The modified PATH would also need to apply for invocations through sudo. Fortunately sudo has a setting secure_path.

madaidan via Whonix Forum:

I don’t see why it would cause apparmor issues.

Suppose git internally used gpg. Using standard PATH search to find the gpg binary. git simply calling “gpg” not “/usr/bin/gpg”. Then later an apparmor profile is added for git. By modifying the PATH variable and having gpg in any other path then the default “/usr/bin/gpg” could result in an apparmor violation.

Or perhaps Tor Browser or Firefox would call other binaries. Dunno yet any specific examples.

Perhaps an AppArmor /etc/apparmor.d/tunables/... or so drop-in configuration snippet could solve that.

No, you can stack commands with a single ${wrapper_pre}. See the example above in which torsocks and firejail are both used for gpg.

If you want multiple drop-in config files,

Yes. For sure.

Each application that provides a wrapper would provide a configuration snippet. Otherwise it would be very inflexible - not using any component (for testing, Kicksecure, alternatives, whatever) no longer possible - at least not without separate user changes to wrapper configs which is bad usability.

we can modify the script to source ${sw_dir}/${program_name}_{0..9}.conf. So for example, to stack torsocks and firejail using multiple config files you can create /etc/wrapper.d/gpg_0.conf and add:

wrapper_pre+="torsocks"

Maybe needs to add a space before or behind.

This might be good enough for an initial implementation but would break on more complex inputs perhaps including single quotes, double quotes or command line parameters that contain spaces.

Instead of setting variables directly with wrapper_pre+= perhaps better to use a shell function because then constructing the actual variable could be done in a shell function (later fixes for white spaces etc) and wouldn’t require later config file changes.

/usr/lib/pbuilder/pdebuild-checkparams uses

DEBBUILDOPTS=“${2:+$DEBBUILDOPTS $2}”;
EXTRA_CONFIGFILE[${#EXTRA_CONFIGFILE[@]}]=“$2”;

/usr/lib/pbuilder/pbuilder-modules

function get_source_options() {
    local source_options
    local arg
    # Split and de-escape DEBBUILDOPTS. Can't iterate over it before
    # de-escaping, as word splitting does not take quotes in the variable into
    # account. Need eval as $(echo $DEBBUILDOPTS) on its own doesn't perform
    # quote expansion, since quote expansion operates on the *original* word.
    eval local args=($DEBBUILDOPTS)
    for arg in "${args[@]}"; do

Not sure yet but proper handling for quotes, any special characters and white spaces will probably require a bash array. I don’t know any bash scripts as role models from top of my head.

then create /etc/wrapper.d/gpg_1.conf and add

I am not sure yet if the name of the program to be wrapped should be part of the configuration file name. Might work. What is the advantage of this? What would be the disadvantage of having the name or path of the to be wrapped application defined in the config file?

What do you think about uwt (/etc/uwt.d/30_uwt_default.conf) syntax

uwtwrapper["/usr/bin/git"]="1"

?

/etc/wrapper.d/gpg_1.conf would mean the first thing that would be run if someone used Debian standard PATH and would execute gpg? Dunno if /path/to/uncommon/location should be supported.

1 Like

There’s no specific reason. Having the file path in the config file does sound like a better approach.

Looks better.

1 Like

How would both the pre and post wrappers be parsed correctly? Seems more complicated.