Skip to content

docker: gate --privileged behind DOCKER_PRIVILEGED toggle (default yes)#9733

Merged
igorpecovnik merged 1 commit into
armbian:mainfrom
iav:exp/no-docker-privileged
May 5, 2026
Merged

docker: gate --privileged behind DOCKER_PRIVILEGED toggle (default yes)#9733
igorpecovnik merged 1 commit into
armbian:mainfrom
iav:exp/no-docker-privileged

Conversation

@iav
Copy link
Copy Markdown
Contributor

@iav iav commented Apr 28, 2026

Summary

Introduces a DOCKER_PRIVILEGED env var (default yes) gating the
hardcoded --privileged flag on the build container. When set to no,
the container launches with a narrow capability set instead.

This is intentionally a conservative first step: default behaviour
is unchanged. A follow-up PR can flip the default to no after
broader board coverage is confirmed.

Motivation

--privileged has been hardcoded in lib/functions/host/docker.sh:384
since 2022 (commit d24d3327a8). The original comment cited two
reasons:

Running this container in privileged mode is a simple way to solve
loop device access issues, required for USB FEL or when writing
image directly to the block device, when CARD_DEVICE is defined

Both reasons are stale today:

  • USB FEL support was removed in ca6bf9046b (2023-02-09).
  • CARD_DEVICE is passed explicitly via
    DOCKER_ARGS+=("--device=${CARD_DEVICE}") at docker.sh:471.

The surrounding code already has a near-complete narrow-capability
setup:

--cap-add=SYS_ADMIN, MKNOD, SYS_PTRACE
--security-opt=apparmor:unconfined
--device-cgroup-rule='b 7:* rmw'      # loop majors
--device-cgroup-rule='b 259:* rmw'    # loop partitions
-v /dev:/tmp/dev:ro                   # CONTAINER_COMPAT trick
--device=${loop_device_host}          # specific loops

This PR adds the two missing pieces to make a non-privileged container
usable for armbian builds:

  • --device=/dev/loop-control:/dev/loop-controllosetup -f needs
    /dev/loop-control, not exposed without --privileged.
  • --security-opt=seccomp=unconfined — default seccomp profile may
    block mount, pivot_root, unshare even with CAP_SYS_ADMIN,
    which breaks debootstrap and chroot setup.

Tested

  • Host: aarch64 (Armbian 26.2.1 noble), kernel 7.0.1, Docker 29.4.1.

  • Builds (all with ARTIFACT_IGNORE_CACHE=yes DOCKER_PRIVILEGED=no RELEASE=noble, full image build: debootstrap, loop, parted, mkfs,
    chroot, zstd, rsync):

    Board Family Arch Profile Kernel Runtime
    odroidm1 rockchip64 arm64 minimal 7.0.2 49 min
    helios4 mvebu armhf server 6.18.25 134 min
    helios64 rockchip64 arm64 server 7.0.2 47 min

    All three: 🐳 successful, image written, no Operation not permitted / seccomp / loop_control errors in the log.

  • DOCKER_PRIVILEGED unset (default yes) verified separately to keep
    current behaviour.

Out of scope

  • The losetup helper at docker.sh:619 (docker run --rm --privileged --cap-add=MKNOD ...) is unchanged. It is gated by /dev/loop0
    missing on the host, which is rare on Linux. A follow-up could mirror
    the toggle there.
  • The @TODO rpardini: maybe it's dead already? on --cap-add=SYS_PTRACE
    is left as-is — orthogonal to this change.

Follow-up

After this lands and gets exercise on more boards, a one-line PR can
flip the default to no.

Summary by CodeRabbit

  • New Features
    • Docker container execution now supports configurable privileged mode settings, allowing users to control container security behavior. When privileged mode is enabled, containers run with full privileges. When disabled, the system adjusts device access and security constraints automatically. This provides flexibility for different deployment scenarios while maintaining backward compatibility.

Adds DOCKER_PRIVILEGED env var (default "yes" for backward compatibility).
Set DOCKER_PRIVILEGED=no to launch the build container without --privileged,
instead complementing the existing --cap-add=SYS_ADMIN/MKNOD/SYS_PTRACE
and loop-related --device-cgroup-rule entries with:
  --device=/dev/loop-control:/dev/loop-control
  --security-opt=seccomp=unconfined

The original justifications for --privileged (USB FEL, raw block-device
write via CARD_DEVICE) no longer apply: FEL was removed in ca6bf90,
and CARD_DEVICE is now passed via --device=${CARD_DEVICE}.

The losetup helper later in the same function still uses --privileged
unconditionally; it is gated by /dev/loop0 missing on the host (rare on
Linux) and is left out of scope.

Tested: full image build with DOCKER_PRIVILEGED=no on edge/noble:
  - odroidm1  rockchip64 arm64  minimal  Linux 7.0.2  49 min
  - helios4   mvebu      armhf  server   Linux 6.18.25 134 min
  - helios64  rockchip64 arm64  server   Linux 7.0.2  47 min
debootstrap, loop, parted, mkfs, chroot, zstd compression all
succeeded; no Operation-not-permitted / seccomp / loop_control errors;
zero sudo calls on the host across all three runs.

Assisted-by: Claude:claude-opus-4-7
@iav iav requested a review from a team as a code owner April 28, 2026 01:16
@iav iav added the Bugfix Pull request is fixing a bug label Apr 28, 2026
@iav iav requested review from TRSx80 and mhoffrog and removed request for a team April 28, 2026 01:16
@iav iav added the Bugfix Pull request is fixing a bug label Apr 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: affaadc8-490c-408d-a581-ac5d43299ed4

📥 Commits

Reviewing files that changed from the base of the PR and between 73a3e49 and 0f91d02.

📒 Files selected for processing (1)
  • lib/functions/host/docker.sh

📝 Walkthrough

Walkthrough

The Docker container launch logic in the host functions is modified to conditionally control privileged mode via a DOCKER_PRIVILEGED variable, defaulting to yes. When disabled, the container applies alternative security settings including explicit loop-control device access and unconfined seccomp.

Changes

Cohort / File(s) Summary
Docker Privileged Mode Control
lib/functions/host/docker.sh
Replaced unconditional privileged container launch with conditional logic: appends --privileged when DOCKER_PRIVILEGED=yes (default), otherwise applies --security-opt=seccomp=unconfined and adds loop-control device access.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A docker dance, so fine and neat,
With privilege control, now quite discreet,
When yes it says, full power flows,
When not, seccomp relaxes its throes!
Loop devices granted, restrictions eased—
Configuration blessed, complexity ceased! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing a DOCKER_PRIVILEGED toggle that gates the --privileged flag with a default of yes, matching the file-level changes and PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added size/small PR with less then 50 lines 05 Milestone: Second quarter release Needs review Seeking for review Framework Framework components labels Apr 28, 2026
@iav iav requested a review from rpardini April 28, 2026 01:22
@igorpecovnik
Copy link
Copy Markdown
Member

I am OK to merge, run tests in unprivileged mode on CI, then proceed with follow up.

RKflashing came to my mind, but its not supported under Docker anyway:
https://github.com/armbian/build/blob/main/extensions/rkdevflash.sh#L26

@iav iav removed the Bugfix Pull request is fixing a bug label May 2, 2026
@iav
Copy link
Copy Markdown
Contributor Author

iav commented May 3, 2026

@igorpecovnik thanks. Which combinations would you like me to run locally with PREFER_DOCKER=yes DOCKER_PRIVILEGED=no?

@igorpecovnik
Copy link
Copy Markdown
Member

yes

@iav
Copy link
Copy Markdown
Contributor Author

iav commented May 4, 2026

As I already wrote in the PR message, I have already put together several images like this.
Let me know if you want me to check other devices or any additional settings.
Sad I can't run on Runner on Github.

@igorpecovnik
Copy link
Copy Markdown
Member

igorpecovnik commented May 4, 2026

Sad I can't run on Runner on Github.

Oh, true. Now you can.

https://github.com/armbian/os/actions/workflows/complete-artifact-matrix-standard-support.yml

When executing, you can choose "Framework build branch" ... when adding new (branch must exists here, not on your fork), you need to run https://github.com/armbian/os/actions/workflows/recreate-matrix.yml so it gets there.

@iav
Copy link
Copy Markdown
Contributor Author

iav commented May 4, 2026

There it is!
https://github.com/armbian/os/actions/runs/25348467781
However, since this is the first time I have seen this tool in front of me, I am not sure that I have used it correctly, and I do not fully understand what exactly I am seeing.

@igorpecovnik
Copy link
Copy Markdown
Member

Yes, correct and it seems working. Artifacts - when needed - https://fi.mirror.armbian.de/incoming/iav/

@github-actions github-actions Bot added the Ready to merge Reviewed, tested and ready for merge label May 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

✅ This PR has been reviewed and approved — all set for merge!

@github-actions github-actions Bot removed the Needs review Seeking for review label May 5, 2026
@igorpecovnik igorpecovnik merged commit cb943c5 into armbian:main May 5, 2026
3 checks passed
@iav iav deleted the exp/no-docker-privileged branch May 5, 2026 15:03
iav added a commit that referenced this pull request May 5, 2026
…u-arm

Continues #9284 (arm64-compat-vdso extension + custom_kernel_make_params hook).
That PR enabled COMPAT_VDSO in arm64 kernels and wired CROSS_COMPILE_COMPAT
through the build; this PR uses the resulting 32-bit-capable kernels to skip
qemu-arm emulation entirely during the image-build chroot work.

Disable qemu-arm in /proc/sys/fs/binfmt_misc for the duration of the
image-build so 32-bit ARM ELF falls through to kernel binfmt_elf and
runs natively via CONFIG_COMPAT — typically ~12x faster than qemu-arm
emulation on common ARM cores (Cortex-A53/A55/A72/A73/A76). A cleanup
handler re-enables qemu-arm on build exit (success or failure).

Empirical: helios4 BUILD_MINIMAL armhf rootfs cache-miss build on
RK3568 (Cortex-A55) drops from 60:35 to 19:27 — 3.12x speedup; same
build with rootfs cache-hit drops to 6:38 (~5x).

Activation point is delayed to AFTER mmdebstrap (in
create_new_rootfs_cache_via_debootstrap, right past the bash-presence
check) and also at build_rootfs_and_image entry for the rootfs-cache
hit path. Earlier activation breaks mmdebstrap because its cross-arch
machinery requires a working qemu-arm in binfmt_misc and the chroot is
not yet populated for native exec.

Out-of-scope cases (target arch != armhf, host arch != aarch64, not
running in container) return silently with no log noise. In-scope
cases log the capability-check outcome at info level: native enabled,
already-effective no-op, or 32-bit ARM execution unsupported (no
COMPAT_VDSO in kernel, Apple Silicon, ARMv9-only cores) — falling
back to qemu-arm-static emulation.

Killswitch: NATIVE_ARMHF_ON_ARM64=no disables this path entirely,
before any detection runs — useful on shared build farms or whenever
opting out is desired (synonyms 'never' and 'disabled' are accepted
by the parser).

Sets ARMBIAN_NATIVE_ARMHF_VIA_BINFMT_ELF=yes when active so that
deploy_qemu_binary_to_chroot / undeploy_qemu_binary_from_chroot skip
copying qemu-arm-static into the chroot during image-stage (the kernel
handles the ELF directly).

Behaviour depends on DOCKER_PRIVILEGED (#9733). With the default
DOCKER_PRIVILEGED=yes, /proc/sys/fs/binfmt_misc is shared with the
host's init user-ns; disabling qemu-arm affects the host's other
concurrent processes for the duration of the build (acceptable for
single-user builders, problematic for shared build farms — opt out
with NATIVE_ARMHF_ON_ARM64=no). With DOCKER_PRIVILEGED=no, sysfs is
read-only inside the container; the disable write fails, the function
logs a warning, and the build continues via qemu-arm-static — no
regression.

Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Igor Velkov <325961+iav@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

05 Milestone: Second quarter release Framework Framework components Ready to merge Reviewed, tested and ready for merge size/small PR with less then 50 lines

Development

Successfully merging this pull request may close these issues.

2 participants