Supporting rootless builds ========================== Status: recommendation, stable Version: 1.0 Background ---------- Traditionally, Debian packages have required (fake)root privileges for some of the "debian/rules" targets. This has required also a split between build and binary targets; making the builds slower, due to the increased amount of invocations of "debian/rules" and the overhead of using fakeroot(1) or equivalent fake environments, or less secure due to the increased dangers of running under real root via sudo or equivalent. On this document when talking about "(fake)root" privileges, it will refer to any mechanism, supported by the dpkg-buildpackage "-r/--root-command" option, that can provide either a real or faked root user environment. Specification ------------- We add a new field to the "Source" stanza of debian/control: Rules-Requires-Root: no | binary-targets | The case sensitive values are defined as: * If "no", then "debian/rules binary" will not require root at all (not even fakeroot). This is the default in dpkg-build-api level >= 1. - If the "no" keyword is used, it MUST be the only keyword in that field and MUST appear exactly once. * If "binary-targets", then "debian/rules binary" (etc.) must always be run under (fake)root. This is the status quo, default in dpkg-build-api level 0. * will be a space separated list of keywords which define when root is required. - Keywords consists of /. The "namespace" part cannot contain "/" or whitespace. The "cases" part cannot contain whitespace. Furthermore, both parts MUST consist entirely of printable ASCII characters. - Each tool/package will define a namespace named after itself and provide a number of cases where (fake)root is required. (See also "Implementation provided keywords".) - When "Rules-Requires-Root" is set to , the builder (i.e. whatever is executing debian/rules) will expose an interface that is used to run a command under (fake)root via the "Gain Root API". If the builder cannot provide such a command, it MUST behave like "Rules-Requires-Root" was set to "binary-targets", i.e. run "debian/rules binary" under (fake)root. When the builder supports this specification, it MUST notify this fact to the rules file via the "DEB_RULES_REQUIRES_ROOT" environment variable, with the value it has obtained from the Rules-Requires-Root field or some builder specific override mechanism, which will denote the level of support the builder has chosen to commit to take effect during the build. When set, it MUST be a valid value for the Rules-Requires-Root field. If unset, the build system SHOULD assume that the builder does not recognize the Rules-Requires-Root field at all. It is always permissible for a builder to ignore this field and fall back to running the binary targets under (fake)root. This is to ensure backwards compatibility when builds are performed by legacy builders or older versions of the tooling. Tools called from the rules file MUST cope gracefully with being called under (fake)root even when Rules-Requires-Root is set to a value that implies they should not be (e.g. "no"). However, they MUST NOT attempt to run processes under (fake)root when run as a regular user when Rules-Requires-Root does not list any keywords they respond to. Tools MUST gracefully ignore valid unknown keywords outside their namespace. They MAY warn about unknown keywords inside their namespace. The value of this field MUST NOT change the content of the package in any way. Notably, packages that are bit-for-bit reproducible MUST still provide bit-for-bit identical results even when the field is ignored. Implementation provided keywords -------------------------------- Keywords provided by various implementations: * dpkg/target-subcommand: When the package needs to run a given command under (fake)root within the "debian/rules" files directly, this MUST be declared via this keyword. * dpkg/target/: When a specific "debian/rules" unofficial target (none of the root-requiring "binary-indep", "binary-arch", "binary", "clean", nor the non-root-requiring "build-indep", "build-arch", "build") needs to be run under (fake)root, this MUST be declared via this dynamic keyword, where is the name of the "debian/rules" target. * debhelper/upstream-make-install: The dh_auto_install command will run the "install" target from the upstream's Makefile under (fake)root (for the "makefile" build system or one derived from it). Gain Root API ------------- The builder will provide a command to promote a given command to (fake)root by exposing it in the environment variable "DEB_GAIN_ROOT_CMD". Tools that need this promotion will then use it like the following: $DEB_GAIN_ROOT_CMD cmd-that-needs-root ... This command is subject to the same requirements as the "gain-root-command" that dpkg-buildpackage accepts via its "-r/--root-command" option, which means that it can contain space-separated parameters. If dpkg-buildpackage is called with "-r/--root-command", then dpkg-buildpackage shall use that value as the value for "DEB_GAIN_ROOT_CMD". The command SHOULD preserve all the environment variables, unmodified. The variable SHOULD only be provided when there is a need for it. Notably when "Rules-Requires-Root" is either "no" or "binary-targets" the variable SHOULD NOT be defined. (The "DEB_GAIN_ROOT_CMD" variable used to be named "DPKG_GAIN_ROOT_CMD" starting with dpkg 1.19.0 and before dpkg 1.19.1 when this specification got released as stable. The old name MUST not be used.) Common cases ------------ * Upstream installation insists on "sudo make install"-like behavior. => Use dpkg/target-subcommand or debhelper/upstream-make-install. * Files shipped in the package must be owned by another user than root. => Not covered; use "binary-targets" for now until dpkg+debhelper provides the required interface. Prototyping/preparation ======================= dpkg side --------- dpkg-deb --build provides the --root-owner-group option so that dh_builddeb or direct calls can control the owner/group file values w/o requiring (fake)root. dpkg-buildpackage must export DEB_GAIN_ROOT_CMD when necessary (for prototyping, doing this unconditionally would be fine). debhelper side -------------- When the field is present: * dh_testroot will behave as usual when Rules-Requires-Root is not present or set to "binary-targets". * dh_testroot will be a no-op when Rules-Requires-Root is set to "no". * Otherwise, dh_testroot will either verify that it is run under (fake)root (as usual) OR assert that DEB_GAIN_ROOT_CMD is defined. * debhelper build systems will be patched to check for the "debhelper/upstream-make-install" keyword and use the "Gain Root API" accordingly. * All other (src:)debhelper commands will skip their calls to chown (currently they just reset them to "0:0" anyway). With the above, a default "dh $@" will no longer require (fake)root when built (and Rules-Requires-Root is "no"). Prototyping: * During prototyping, dh_builddeb can wrap the dpkg-deb --build call with fakeroot (when not already root).