From 4b2e6a179ad632f961b85437cbb948354e15fc0b Mon Sep 17 00:00:00 2001 From: Robert Kirkman Date: Tue, 9 Jun 2026 20:11:10 -0500 Subject: [PATCH] enhance(Dockerfile): use `/etc/resolv.conf` and restrict `/data` permission - After https://github.com/termux/termux-packages/pull/30044 - Partially reverts https://github.com/termux/termux-docker/pull/49 - Handle case of duplicate dependencies between termux-docker packages and termux bootstrap packages - Document pacman image - Restrict access to `/data` and `/data/data` so that termux-docker can reproduce this issue, and issues very much like it, for termux-docker to be useful for developing a solution to them https://github.com/termux/termux-packages/issues/28433 . Docker Engine does not seem to allow restricting access to `/`. --- Dockerfile | 55 ++++++++++++++++++++++++---------------------- README.md | 17 ++++++++++---- entrypoint.sh | 9 -------- entrypoint_root.sh | 8 ------- generate.sh | 27 ++++++++++++----------- 5 files changed, 56 insertions(+), 60 deletions(-) diff --git a/Dockerfile b/Dockerfile index abbcee7..e52e466 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,41 +8,44 @@ ARG TERMUX__CACHE_DIR # Install generated rootfs containing: # - termux bootstrap -# - aosp-libs (bionic libc, linker, boringssl, zlib, libicuuc, debuggerd) -# - aosp-utils (toybox, mksh, iputils) +# - aosp-libs (bionic libc, linker, boringssl, zlib, libicuuc, debuggerd, depends on resolv-conf) +# - aosp-utils (toybox, grep, mksh, iputils) # - libandroid-stub -# - dnsmasq # Since /system is now a symbolic link to $PREFIX/opt/aosp, # which has contents that can be updated by the system user via apt, -# the entire rootfs is now owned by the system user (1000:1000). +# the entire rootfs is now owned by the system user (1000:1000), +# except for /data and /data/data (see below) COPY --chown=1000:1000 ${TERMUX_DOCKER__ROOTFS} / # Docker uses /bin/sh by default, but we don't have it. -ENV PATH=/system/bin +ENV PATH=${TERMUX__PREFIX}/bin SHELL ["sh", "-c"] +# Prevent the unprivileged user from having read access to +# /data and /data/data just like all real Android devices +# this will enable termux-docker to reproduce bugs (for debugging and development) +# like this one: +# https://github.com/termux/termux-packages/issues/28433 +# but unfortunately it won't enable termux-docker to reproduce bugs that actually +# require / to be inaccessible to reproduce, like this one: +# https://github.com/termux-user-repository/tur/issues/1897 +# it doesn't seem possible to set chmod 771 persistently on / +# within docker, since it reverts to 755 immediately when the container is run. +RUN chown root:root /data/ /data/data/ +RUN chmod 771 /data/ /data/data/ + # Install updates and cleanup -# Start dnsmasq to resolve hostnames, and, -# for some reason the -c argument of toybox-su is not working, -# so this odd-looking script forces the update process -# to work using the -s argument of toybox-su instead, which is working. -RUN sh -T /dev/ptmx -c "$TERMUX__PREFIX/bin/dnsmasq -u root -g root --pid-file=/dnsmasq.pid" && \ - sleep 1 && \ - echo '#!/system/bin/sh' > /update.sh && \ - echo "PATH=$TERMUX__PREFIX/bin" >> /update.sh && \ - echo "source $TERMUX__PREFIX/bin/termux-setup-package-manager" >> /update.sh && \ - echo 'if [ "$TERMUX_APP_PACKAGE_MANAGER" = "apt" ]; then' >> /update.sh && \ - echo 'apt update' >> /update.sh && \ - echo 'apt upgrade -o Dpkg::Options::=--force-confnew -y' >> /update.sh && \ - echo 'elif [ "$TERMUX_APP_PACKAGE_MANAGER" = "pacman" ]; then' >> /update.sh && \ - echo 'pacman-key --init' >> /update.sh && \ - echo 'pacman-key --populate' >> /update.sh && \ - echo 'pacman -Syyu --noconfirm' >> /update.sh && \ - echo 'fi' >> /update.sh && \ - chmod +x /update.sh && \ - su system -s /update.sh && \ - rm -rf /update.sh \ - "${TERMUX__PREFIX}"/var/lib/apt/* \ +USER 1000:1000 +RUN . $TERMUX__PREFIX/bin/termux-setup-package-manager && \ + if [ "$TERMUX_APP_PACKAGE_MANAGER" = "apt" ]; then \ + apt update && \ + apt upgrade -o Dpkg::Options::=--force-confnew -y; \ + elif [ "$TERMUX_APP_PACKAGE_MANAGER" = "pacman" ]; then \ + pacman-key --init && \ + pacman-key --populate && \ + pacman -Syyu --noconfirm; \ + fi && \ + rm -rf "${TERMUX__PREFIX}"/var/lib/apt/* \ "${TERMUX__PREFIX}"/var/log/apt/* \ "${TERMUX__CACHE_DIR}"/apt/* \ "${TERMUX__PREFIX}"/var/cache/pacman/pkg/* \ diff --git a/README.md b/README.md index 64ac457..1a15857 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,16 @@ Available tags: - `x86_64` - `latest` (multiplatform) +The other package manager for Termux, pacman, is available in a separate image: + +```.sh +docker run -it termux/termux-docker-pacman +``` + If architecture is not compatible with host, the additional setup will be needed. Read this document further to learn how you can run containers of incompatible CPU architecture. -**Important note**: do not pass `--user` option to Docker command line. -The initial user of container must be root. Otherwise DNS will be broken -because of `dnsmasq` server failure. - ### Running ARM containers In order to run AArch64 container on x86(64) host, you need to setup @@ -133,6 +135,13 @@ Podman: ./generate.sh --podman ``` +Pacman: + +```.sh +export TERMUX_PACKAGE_MANAGER=pacman +./generate.sh +``` + ## Known issues There a number of known issues which may not be resolved: diff --git a/entrypoint.sh b/entrypoint.sh index 4e56612..82fc4b1 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,18 +5,9 @@ if [ $# -lt 1 ]; then fi if [ "$(id -u)" != "0" ]; then - echo "[!] Container is running as non-root, unable to start dnsmasq. DNS will be unavailable." >&2 exec "$@" fi -if [ -z "$(pidof dnsmasq)" ]; then - /system/bin/sh -T /dev/ptmx -c "dnsmasq -u root -g root --pid-file=/dnsmasq.pid" >/dev/null 2>&1 - sleep 1 - if [ -z "$(pidof dnsmasq)" ]; then - echo "[!] Failed to start dnsmasq, host name resolution may fail." >&2 - fi -fi - exec /system/bin/su -s "$PREFIX/bin/env" system -- \ -i \ ANDROID_DATA="$ANDROID_DATA" \ diff --git a/entrypoint_root.sh b/entrypoint_root.sh index 97d0ee8..47c0e97 100755 --- a/entrypoint_root.sh +++ b/entrypoint_root.sh @@ -9,12 +9,4 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -if [ -z "$(pidof dnsmasq)" ]; then - /system/bin/sh -T /dev/ptmx -c "dnsmasq -u root -g root --pid-file=/dnsmasq.pid" >/dev/null 2>&1 - sleep 1 - if [ -z "$(pidof dnsmasq)" ]; then - echo "[!] Failed to start dnsmasq, host name resolution may fail." >&2 - fi -fi - exec "$@" diff --git a/generate.sh b/generate.sh index a49b400..3b3be5b 100755 --- a/generate.sh +++ b/generate.sh @@ -52,20 +52,20 @@ fi case "${TERMUX_PACKAGE_MANAGER}" in apt) TERMUX_DOCKER__IMAGE_NAME="termux/termux-docker" - TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.04.26-r1%2Bapt.android-7" + TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.06.07-r1%2Bapt.android-7" TERMUX_DOCKER__BOOTSTRAP_SRCURL="https://github.com/termux/termux-packages/releases/download/bootstrap-${TERMUX_DOCKER__BOOTSTRAP_VERSION}/bootstrap-${TERMUX_ARCH}.zip" declare -A REPO_BASE_URLS=( ["main"]="https://packages-cf.termux.dev/apt/termux-main/dists/stable/main" - ["root"]="https://packages-cf.termux.dev/apt/termux-root/dists/root/stable" + # ["root"]="https://packages-cf.termux.dev/apt/termux-root/dists/root/stable" ) ;; pacman) TERMUX_DOCKER__IMAGE_NAME="termux/termux-docker-pacman" - TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.04.26-r1%2Bpacman.android-7" + TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.06.07-r1%2Bpacman.android-7" TERMUX_DOCKER__BOOTSTRAP_SRCURL="https://github.com/termux-pacman/termux-packages/releases/download/bootstrap-${TERMUX_DOCKER__BOOTSTRAP_VERSION}/bootstrap-${TERMUX_ARCH}.zip" declare -A REPO_BASE_URLS=( ["main"]="https://sync.termux-pacman.dev/main" - ["root"]="https://sync.termux-pacman.dev/root" + # ["root"]="https://sync.termux-pacman.dev/root" ) ;; *) @@ -73,17 +73,13 @@ case "${TERMUX_PACKAGE_MANAGER}" in exit 1 ;; esac +echo "[*] Package manager that will be installed in termux-docker image: ${TERMUX_PACKAGE_MANAGER}" # packages that are extracted, along with their dependencies, # on top of the bootstrap to form the termux-docker rootfs. # libandroid-stub is described in multiple places as existing explicitly # for use with termux-docker, so pulling it in here. -# dnsmasq will not get automatically updated during 'pkg upgrade' by the user -# after termux-docker has been installed, since root-repo is not installed for now -# to imply that other root-packages are not directly supported, -# but aosp-utils, aosp-libs and libandroid-stub will get automatically updated -# by user-invoked instances of 'pkg upgrade' since they are in the main repository. -TERMUX_DOCKER__DEPENDS="aosp-utils, libandroid-stub, dnsmasq" +TERMUX_DOCKER__DEPENDS="aosp-utils, libandroid-stub" TERMUX_DOCKER__BUILD_DEPENDS="ar, awk, curl, docker, grep, gzip, find, sed, tar, xargs, xz, unzip, jq" TERMUX_APP__PACKAGE_NAME="com.termux" TERMUX_APP__DATA_DIR="/data/data/$TERMUX_APP__PACKAGE_NAME" @@ -198,7 +194,12 @@ pull_package_apt() { if [ -n "$package_dependencies" ]; then local dep for dep in $package_dependencies; do - if [ ! -e "${TERMUX_DOCKER__PKGDIR}/${dep}" ]; then + # Since termux-docker builds on top of an existing bootstrap, + # the preinstalled packages from the bootstrap also need + # to be checked for duplicate dependencies + if [ ! -e "${TERMUX_DOCKER__PKGDIR}/${dep}" ] && \ + ! grep -q "^Package: $dep$" \ + "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/dpkg/status"; then pull_package_apt "$dep" fi done @@ -321,7 +322,7 @@ pull_package_pacman() { { echo "%FILES%" $TAR xvf package.pkg.tar.xz -C "$TERMUX_DOCKER__ROOTFS" .INSTALL .MTREE data 2> /dev/null | grep '^data/' || true - } >> "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/files" + } > "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/files" mv "${TERMUX_DOCKER__ROOTFS}/.MTREE" "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/mtree" if [ -f "${TERMUX_DOCKER__ROOTFS}/.INSTALL" ]; then mv "${TERMUX_DOCKER__ROOTFS}/.INSTALL" "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/install" @@ -335,7 +336,7 @@ pull_package_pacman() { done jq -r -j '."'${package_name}'" | to_entries | .[] | select(.key | contains('$(sed 's/^/"/; s/ /","/g; s/$/"/' <<< ${keys_desc})')) | "%",(if .key == "ISIZE" then "SIZE" else .key end),"%\n",.value,"\n\n" | if type == "array" then (.| join("\n")) else . end' \ "${PATH_DB_PACKAGES}" - } >> "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/desc" + } > "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/desc" ) fi }