From: Cedric Hombourger <cedric_hombourger@mentor.com>
To: Helmut Grohne <helmut@subdivi.de>, Jan Kiszka <jan.kiszka@siemens.com>
Cc: isar-users <isar-users@googlegroups.com>,
Baurzhan Ismagulov <ibr@ilbers.de>,
"MacDonald, Joe" <Joe_MacDonald@mentor.com>
Subject: Re: [RFC] using lightweight containers instead of chroot
Date: Fri, 9 Jul 2021 18:12:44 +0200 [thread overview]
Message-ID: <dbdc6ba8-2754-37dd-607a-5f17ad61fe9a@mentor.com> (raw)
In-Reply-To: <f08277ba-7517-38c7-ac88-42553152bda7@mentor.com>
On 7/9/2021 5:46 PM, Cedric Hombourger wrote:
>
> On 7/8/2021 3:52 PM, Helmut Grohne wrote:
>> On Thu, Jul 08, 2021 at 01:38:01PM +0200, Jan Kiszka wrote:
>>> On 08.07.21 11:07, Cedric Hombourger wrote:
>>> ...
>>> Longterm, there is also the desire to include support for DPKG_ROOT as
>>> chroot-less way of building packages, faster when doing it cross and
>>> also without special permissions (e.g. for qemu-user). But that
>>> requires
>>> per-package support from Debian upstream. Discussions only started, in
>>> particular with Helmuth (added to CC).
>> You appear to be confusing some aspects here. DPKG_ROOT is not relevant
>> to building packages. It is only relevant to installing them (which may
>> be relevant here for creating filesystem images).
>>
>> Building packages without chroot in a reproducible way seems next to
>> impossible to me. Even when you use user namespaces, chroot does not go
>> away. It merely becomes unprivileged. Is that what you mean here?
>>
>>>> Proposal
>>>>
>>>> We may want to use unshare(1) to create a mount namespace where we
>>>> will create our bind mounts,
>>>> chroot into the buildchroot and run the specified command/script
>> Are you aware that sbuild directly supports this use case? It has a
>> --mode argument and one of its values is "unshare". In that case, you
>> supply a tarball containing the chroot and it'll perform an unprivileged
>> build inside an unshared chroot.
>
> I was not and that's very promising. I am now modifying the PoC code
> to use it.
>
> Did I read correctly that we can tell sbuild to use an existing
> directory for its chroot when using the "unshare" mode? I am asking
> because that's failing for me (my host is on Debian/testing). Here's
> the error
looks like --chroot=<dir> isn't supported yet for "unshare" as found in
the sbuild code:
# FIXME: support directory chroots
#if (-d $path) {
# if ($file eq $chroot) {
# $tarball = $path;
# last;
# }
#} else {
>
> copy() failed: Is a directory
> tar: This does not look like a tar archive
> tar: Exiting with failure status due to previous errors
>
> and here's the full command I ran (with the key args being -c
> <existing-chroot-dir> --chroot-mode=unshare):
>
> + sbuild -c
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/
> --chroot-mode=unshare -d industrial-os --no-apt-update -v
> --pre-build-commands= mkdir -p
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/base-apt
> ; mount --bind
> /home/chombourger/unshare_sbuild/experimental/mel-apt
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/base-apt
> ; mount --bind
> '/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/deploy/isar-apt/industrial-os-amd64/apt/industrial-os'
> '/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/isar-apt'
> ; mount --bind
> '/home/chombourger/unshare_sbuild/experimental/build-ipc/downloads'
> '/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/downloads'
> ; mount -t proc none
> '/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/proc'
> ; mount --rbind /sys
> '/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/sys'
> ; mount -t tmpfs -o rw,nosuid,nodev,seclabel none
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/dev/shm
> ; mount -o bind /dev/pts
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/dev/pts
> ; mkdir -p
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs//home/builder/base-files
> ; mount --bind
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs//home/builder/base-files
> --build-path=/home/builder/base-files/base-files-10.3+deb10u10 -D
>
> and it produced the following output:
>
> dh clean
> dh: warning: Compatibility levels before 10 are deprecated (level 9
> in use)
> dh_clean
> dh_clean: warning: Compatibility levels before 10 are deprecated
> (level 9 in use)
> dpkg-source: info: using source format '3.0 (native)'
> dpkg-source: info: building base-files in base-files_2.4+ind3.tar.xz
> dpkg-source: info: building base-files in base-files_2.4+ind3.dsc
> Selected distribution industrial-os
> Selected chroot
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/
> D: Setting Config=Sbuild::ConfBase=HASH(0x55a7fa84d508)
> D: Setting ABORT=undef
> D: Setting
> Job=/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0/base-files_2.4+ind3.dsc
> D: Setting Build Dir=
> D: Setting Max Lock Trys=120
> D: Setting Lock Interval=5
> D: Setting Pkg Status=pending
> D: Setting Pkg Status Trigger=undef
> D: Setting Pkg Start Time=0
> D: Setting Pkg End Time=0
> D: Setting Pkg Fail Stage=init
> D: Setting Build Start Time=0
> D: Setting Build End Time=0
> D: Setting Install Start Time=0
> D: Setting Install End Time=0
> D: Setting This Time=0
> D: Setting This Space=0
> D: Setting Sub Task=initialisation
> D: Setting Config=Sbuild::ConfBase=HASH(0x55a7fa84d508)
> D: Setting Session ID=
> D: Setting Chroot ID=/
> D: Setting Defaults=HASH(0x55a7fc2a7e48)
> D: Setting Split=1
> D: Setting Split=0
> D: Setting Host=Sbuild::ChrootRoot=HASH(0x55a7fc2a8010)
> D: Setting Priority=0
> D: Setting Location=/
> D: Setting Session Purged=0
> D: Setting Session=undef
> D: Setting Dependency Resolver=undef
> D: Setting Log File=undef
> D: Setting Log Stream=undef
> D: Setting Summary Stats=HASH(0x55a7fc283c70)
> D: Setting dpkg-buildpackage pid=undef
> D: Setting Dpkg Version=undef
> D: Setting DSC:
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0/base-files_2.4+ind3.dsc
> D: Setting
> DSC=/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0/base-files_2.4+ind3.dsc
> D: Setting Source
> Dir=/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0
> D: Setting DSC Base=base-files_2.4+ind3.dsc
> D: DSC =
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0/base-files_2.4+ind3.dsc
> D: Source Dir =
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0
> D: DSC Base = base-files_2.4+ind3.dsc
> D: Setting package version:
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0/base-files_2.4+ind3.dsc
> D: Parsing
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0/base-files_2.4+ind3.dsc
> D: Setting Package=base-files
> D: Setting Version=1:2.4+ind3
> D: Setting Package_Version=base-files_1:2.4+ind3
> D: Setting Package_OVersion=base-files_1:2.4+ind3
> D: Setting Package_OSVersion=base-files_2.4+ind3
> D: Setting Package_SVersion=base-files_2.4+ind3
> D: Setting OVersion=1:2.4+ind3
> D: Setting OSVersion=2.4+ind3
> D: Setting SVersion=2.4+ind3
> D: Setting VersionEpoch=1
> D: Setting VersionUpstream=2.4+ind3
> D: Setting VersionDebian=
> D: Setting DSC File=base-files_2.4+ind3.dsc
> D: Setting DSC Dir=base-files-2.4+ind3
> D: Package = base-files
> D: Version = 1:2.4+ind3
> D: Package_Version = base-files_1:2.4+ind3
> D: Package_OVersion = base-files_1:2.4+ind3
> D: Package_OSVersion = base-files_2.4+ind3
> D: Package_SVersion = base-files_2.4+ind3
> D: OVersion = 1:2.4+ind3
> D: OSVersion = 2.4+ind3
> D: SVersion = 2.4+ind3
> D: VersionEpoch = 1
> D: VersionUpstream = 2.4+ind3
> D: VersionDebian =
> D: DSC File = base-files_2.4+ind3.dsc
> D: DSC Dir = base-files-2.4+ind3
> D: Setting Pkg Status Trigger=CODE(0x55a7fc2300c8)
> D: Setting Pkg Status=building
> D: Setting Pkg Start Time=1625844658
> D: Setting Pkg End Time=1625844658
> D: Setting Host Arch=amd64
> D: Setting Build Arch=amd64
> D: Setting Build Profiles=
> D: Setting Build Type=binary
> D: Setting FILTER_PREFIX=__SBUILD_FILTER_1412690:
> D: Setting COLOUR_PREFIX=__SBUILD_COLOUR_1412690:
> D: Setting Log
> File=/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/2.4+ind3-r0/base-files_2.4+ind3_amd64-2021-07-09T15:30:58Z.build
> D: Setting Log Stream=GLOB(0x55a7fc28c788)
> sbuild (Debian sbuild) 0.81.2 (31 January 2021) on build.local
>
> +==============================================================================+
> | base-files 1:2.4+ind3 (amd64) Fri, 09 Jul 2021
> 15:30:58 +0000 |
> +==============================================================================+
>
> Package: base-files
> Version: 1:2.4+ind3
> Source Version: 1:2.4+ind3
> Distribution: industrial-os
> Machine Architecture: amd64
> Host Architecture: amd64
> Build Architecture: amd64
> Build Type: binary
>
> D: Setting Config=Sbuild::ConfBase=HASH(0x55a7fa84d508)
> D: Setting Chroots=HASH(0x55a7fc28ff88)
> I: No tarballs found in /home/builder/.cache/sbuild
> D: Setting Chroots=HASH(0x55a7fc298bd0)
> D: Setting Config=Sbuild::ConfBase=HASH(0x55a7fa84d508)
> D: Setting Session ID=
> D: Setting Chroot
> ID=/home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/
> D: Setting Defaults=HASH(0x55a7fc362e08)
> D: Setting Chroots=Sbuild::ChrootInfoUnshare=HASH(0x55a7fc2880a0)
> D: Setting Uid Gid Map=ARRAY(0x55a7fbdf7608)
> running perl -e require 'syscall.ph';pipe my $rfh, my $wfh;my $ppid
> = $$;my $cpid = fork() // die "fork() failed: $!";if ($cpid == 0)
> {close $wfh;0 == sysread $rfh, my $c, 1 or die "read() did not
> receive EOF";0 == system "newuidmap $ppid 0 1000 1 1 100000 1" or
> die "newuidmap failed: $!";0 == system "newgidmap $ppid 0 1000 1 1
> 100000 1" or die "newgidmap failed: $!";exit 0;}0 == syscall
> &SYS_unshare, 268435456 or die "unshare() failed: $!";close
> $wfh;$cpid == waitpid $cpid, 0 or die "waitpid() failed: $!";if ($?
> != 0) {die "child had a non-zero exit status: $?";}0 == syscall
> &SYS_setgid, 0 or die "setgid failed: $!";0 == syscall &SYS_setuid,
> 0 or die "setuid failed: $!";0 == syscall &SYS_setgroups, 0, 0 or
> die "setgroups failed: $!";exec { $ARGV[0] } @ARGV or die "exec()
> failed: $!"; chown 1:1 /tmp/tmp.sbuild.l7HJK58Vy2
> Unpacking
> /home/chombourger/unshare_sbuild/experimental/build-ipc/tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs
> to /tmp/tmp.sbuild.l7HJK58Vy2...
> running perl -e require 'syscall.ph';pipe my $rfh, my $wfh;my $ppid
> = $$;my $cpid = fork() // die "fork() failed: $!";if ($cpid == 0)
> {close $wfh;0 == sysread $rfh, my $c, 1 or die "read() did not
> receive EOF";0 == system "newuidmap $ppid 0 100000 65536" or die
> "newuidmap failed: $!";0 == system "newgidmap $ppid 0 100000 65536"
> or die "newgidmap failed: $!";exit 0;}0 == syscall &SYS_unshare,
> 268435456 or die "unshare() failed: $!";close $wfh;$cpid == waitpid
> $cpid, 0 or die "waitpid() failed: $!";if ($? != 0) {die "child had
> a non-zero exit status: $?";}0 == syscall &SYS_setgid, 0 or die
> "setgid failed: $!";0 == syscall &SYS_setuid, 0 or die "setuid
> failed: $!";0 == syscall &SYS_setgroups, 0, 0 or die "setgroups
> failed: $!";exec { $ARGV[0] } @ARGV or die "exec() failed: $!"; tar
> --exclude=./dev/urandom --exclude=./dev/random --exclude=./dev/full
> --exclude=./dev/null --exclude=./dev/console --exclude=./dev/zero
> --exclude=./dev/tty --exclude=./dev/ptmx --directory
> /tmp/tmp.sbuild.l7HJK58Vy2 --extract
> copy() failed: Is a directory
> tar: This does not look like a tar archive
> tar: Exiting with failure status due to previous errors
> D: Error run_chroot_session(): Error creating chroot session:
> skipping base-filesD: Setting Session=undef
> D: Error run_chroot(): Error creating chroot session: skipping
> base-filesE: Error creating chroot session: skipping base-files
> D: Setting Pkg Status=failed
> D: Setting Pkg Fail Stage=create-session
>
>
> The extra bind-mounts would be needed for the following apt source to
> be work:
>
> $ cat
> tmp/work/industrial-os-amd64/base-files/buildchroot/rootfs/etc/apt/sources.list.d/isar-apt.list
> deb [trusted=yes] file:///isar-apt mel main
>
>>
>>>> The immediate benefit of this approach is that all mounts
>>>> automatically disappear as the supplied
>>>> command exits (whether it aborts prematurely because of an
>>>> error or
>>>> normally on completion).
>>>>
>>>> Another nice benefit is that bind mounts we created within this
>>>> namespace are not (directly) visible
>>>> from the parent namespace
>>>>
>>>> However, we found that running scripts within an unshare
>>>> environment
>>>> may not be as easy as
>>>> chroot. We would welcome feedback on the code snippets provided
>>>> below if you happen to have
>>>> some better ideas.
>> All of this applies to sbuild --mode=unshare as well except that it
>> makes running scripts from hooks simple.
> it certainly would!
>>
>>> I suspect Helmuth can tell us if that would take us on a fragile path
>>> from Debian perspective. Isar-internal implementation details we could
>>> likely sort out, but if that approach has architectural limits /wrt
>>> what
>>> Debian packages expect/require, it might be the wrong direction.
>> Reimplementing this functionality seems like a waste of time to me. If
>> we ignore that for a moment, we notice that there are already ~10
>> implementations and updating them all is painful. Therefore, we can
>> conclude that changes to the build environment are rare and your
>> reimplementation likely is maintainable with limited effort.
>>
>>>> def isar_user_spec():
>>>> import os
>>>> return '%d:%d' % (os.getuid(), os.getgid())
>>>>
>>>> ISAR_USER_SPEC = "${@ isar_user_spec() }"
>>>> ISAR_UNSHARE_CMD = "sudo unshare --pid --fork --ipc --mount sh
>>>> -ex"
>>>> ISAR_CHROOT_SHELL = "sh -ex"
>>>> ISAR_CHROOT_ROOT = "chroot ${BUILDCHROOT_DIR}
>>>> ${ISAR_CHROOT_SHELL}"
>>>> ISAR_CHROOT_USER = "chroot --userspec='${ISAR_USER_SPEC}'
>>>> ${BUILDCHROOT_DIR} ${ISAR_CHROOT_SHELL}"
>>>>
>>>> # Would be similar to buildchroot_do_mounts but will happen in a
>>>> separate mount namespace
>>>> BUILDCHROOT_DO_MOUNTS =
>>>> " \
>>>> mount --bind '${REPO_ISAR_DIR}/${DISTRO}'
>>>> '${BUILDCHROOT_DIR}/isar-apt' ; \
>>>> mount --bind '${DL_DIR}'
>>>> '${BUILDCHROOT_DIR}/downloads' ; \
>>>> mount -t proc none
>>>> '${BUILDCHROOT_DIR}/proc' ; \
>>>> mount --rbind /sys
>>>> '${BUILDCHROOT_DIR}/sys' ; \
>>>> mount -t tmpfs -o rw,nosuid,nodev,seclabel none
>>>> ${BUILDCHROOT_DIR}/dev/shm ; \
>>>> mount -o bind /dev/pts
>>>> ${BUILDCHROOT_DIR}/dev/pts \
>>>> "
>>>>
>>>> # Would be similar to dpkg_do_mounts but will happen in a separate
>>>> mount namespace
>>>> DPKG_DO_MOUNTS = " \
>>>> ${BUILDCHROOT_DO_MOUNTS} ; \
>>>> mkdir -p ${BUILDROOT} ; \
>>>> mount --bind ${WORKDIR} ${BUILDROOT} \
>>>> "
>>>>
>>>> # Build package from sources using build script
>>>> _runbuild() {
>>>> export arch=${1}
>>>>
>>>> E="${@ isar_export_proxies(d)}"
>>>> ( cat <<" UNSHARE"
>>>> ${DPKG_DO_MOUNTS}
>>>> ( cat <<" SCRIPT"
>>>> export
>>>> DEB_BUILD_OPTIONS="${DEB_BUILD_OPTIONS}"
>>>> export
>>>> DEB_BUILD_PROFILES="${DEB_BUILD_PROFILES}"
>>>> export PARALLEL_MAKE="${PARALLEL_MAKE}"
>>>> /isar/build.sh ${PP}/${PPS} ${arch}
>>>> SCRIPT
>>>> ) | ${ISAR_CHROOT_USER}
>>>> UNSHARE
>>>> ) | envsubst '$arch' | ${ISAR_UNSHARE_CMD}
>>>> }
>>>>
>>>> dpkg_runbuild() {
>>>> ( _runbuild ${PACKAGE_ARCH} )
>>>> }
>>>>
>>>> PS: I am not very happy with the need to feed the script to
>>>> execute
>>>> under unshare
>>>> via stdin, if there are better ways, we would be happy to
>>>> consider them!
>> When I started talking to Jan, I proposed adding an abstraction layer
>> for package building. Work on that layer has now progressed under the
>> name "mdbp" and source is available at
>> https://git.subdivi.de/?p=~helmut/mdbp.git. I'm also working with
>> Raphael Hertzog on unifying the API with debusine. Let us for a moment
>> consider the implications of using mdbp here.
> Thanks for the pointer. Yet another thing I probably want to look at
>>
>> * Much of the complexity would go away. What you are left with is
>> writing a json file describing how you want your package built. What
>> gets a little more difficult is getting the isar-apt repository past
>> mdbp. Likely that would require a temporary http server.
>> * mdbp is not another builder, but an adapter to existing ones. It can
>> perform your builds using an existing sbuild or pbuilder
>> installation. If you want more isolation, maybe using debspawn
>> (backed by systemd-nspawn) is for you?
>> * mdbp also provides a stateless backend that uses mmdebstrap. This
>> backend performs the build in a user namespace.
>> * If you decide that you prefer building in docker, we can add a
>> backend for e.g. debocker or something else.
>>
>> I'm not sure what you'd be missing by using mdbp here, but one thing
>> you'd certainly miss is quite a bit of complex code.
>>
>> If you want to consider this route, read the schema first:
>> https://git.subdivi.de/?p=~helmut/mdbp.git;a=blob;f=mdbp/build_schema.json
>>
>>
>> Helmut
>>
next prev parent reply other threads:[~2021-07-09 16:13 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-07-08 9:07 Cedric Hombourger
2021-07-08 11:38 ` Jan Kiszka
2021-07-08 13:52 ` Helmut Grohne
2021-07-08 17:16 ` Jan Kiszka
2021-07-08 19:34 ` Helmut Grohne
2021-07-12 8:25 ` Jan Kiszka
2021-07-12 10:54 ` Helmut Grohne
2021-07-12 11:47 ` Jan Kiszka
2021-07-12 12:29 ` Cedric Hombourger
2021-07-12 14:35 ` Jan Kiszka
2021-07-09 15:46 ` Cedric Hombourger
2021-07-09 16:12 ` Cedric Hombourger [this message]
2021-07-26 13:55 ` Anton Mikanovich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=dbdc6ba8-2754-37dd-607a-5f17ad61fe9a@mentor.com \
--to=cedric_hombourger@mentor.com \
--cc=Joe_MacDonald@mentor.com \
--cc=helmut@subdivi.de \
--cc=ibr@ilbers.de \
--cc=isar-users@googlegroups.com \
--cc=jan.kiszka@siemens.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox