APK Foundry design primer

APK Foundry is essentially a thick wrapper around the bubblewrap container tool. The desired properties are the following:

Privilege handling

The primary needs for privilege inside the container is to use apk to install dependencies and to add users and groups needed by the build. The traditional abuild workflow uses a setuid binary (abuild-apk, abuild-adduser, and abuild-addgroup which are symlinked to abuild-sudo) to accomplish this for users in the abuild group. Since bwrap sets the PR_SET_NO_NEW_PRIVS prctl(2) option, setuid binaries do not work inside the container. Instead, the first sub-UID (distinct from the build user) is designated to map to UID zero inside the container and listens over an internal socketpair for privileged commands to execute. This includes not only the functionality of abuild-apk, abuild-adduser, and abuild-addgroup, but also abuild-fetch and apk fetch (needed when network isolation is in effect). An internal daemon is responsible for handling these requests and validating their authorization. The af-sudo client is executed by the build user to initiate these requests inside the container. By default, the container environment is setup with SUDO_APK, ADDUSER, ADDGROUP, ABUILD_FETCH, and APK_FETCH to use af-sudo.

In this model, the elevated privileges needed are:

The root user inside the container drops all capabilities except the following:

In addition, the NO_NEW_PRIVS prctl is in effect. For more information, see user_namespaces(7), capabilities(7), and prctl(2).

The job lifecycle

  1. Receive a list of packages to build, or determine what to build based on a git revision range

  2. Generate a dependency graph in order to perform a topological sort of the packages to-be-built with respect to their dependencies

  3. Bootstrap the container as "root", if it does not already exist

    1. Stage 1 (outside container): download, verify, and unpack rootfs

    2. Run the refresh script inside the container as root

    3. Copy $AF_CONFIG/abuild to $ABUILD_USERDIR inside the container, if it exists

    4. Stage 2 (inside container): upgrade it, add build user, and generate and install a packaging key if necessary

  4. Perform each build

    1. Run the refresh script inside the container as root

    2. Run the build script inside the container as build

  5. Re-sign .apk files outside of the container, if a re-signing key is given

  6. Optionally, destroy the container