#!/usr/bin/env zsh ## debuerreotype sequence ## docker run --cap-add SYS_ADMIN --cap-drop SETFCAP --tmpfs /tmp:dev,exec,suid,noatime ... # bootstrapping a new architecture? # ./scripts/debuerreotype-init /tmp/docker-rootfs bullseye now # ./scripts/debuerreotype-minimizing-config /tmp/docker-rootfs # ./scripts/debuerreotype-debian-sources-list /tmp/docker-rootfs bullseye # ./scripts/debuerreotype-tar /tmp/docker-rootfs - | docker import - debian:bullseye-slim # alternate: # debootstrap --variant=minbase bullseye /tmp/docker-rootfs # tar -cC /tmp/docker-rootfs . | docker import - debian:bullseye-slim # (or your own favorite set of "debootstrap" commands to create a base image for building this one FROM) ## build_docker_dist() { fn build_docker_dist req=(workdir strapdir os arch size imageformat parted_type) ckreq || return 1 notice "Building complete docker image(s)" act "obtain apt/dpkg versions." apt_version=apt-version dpkg_version=dpkg-version act "create and populate the rootfs." bootstrap_complete_base-wrapper || { zerr; return 1; } blend_preinst || { zerr; return 1; } blend_postinst || { zerr; return 1; } notice "import, tag and push them image." docker-init-policy || { zerr; return 1; } # docker-hostname || { zerr; return 1; } # docker-resolvconf || { zerr; return 1; } act "apt related fixups move to sysconf-docker." docker-apt-speedup || { zerr; return 1; } docker-apt-autoremove-suggests || { zerr; return 1; } docker-apt-clean || { zerr; return 1; } docker-apt-gzip-indexes || { zerr; return 1; } docker-apt-no-languages || { zerr; return 1; } act "import, tag and push the image." docker-import || { zerr; return 1; } docker-push || { zerr; return 1; } act "slimify the rootfs." docker-purge_slimify_package || { zerr; return 1; } docker-slimify || { zerr; return 1; } act "import, tag and push the slim image." docker-import --slim || { zerr; return 1; } docker-push --slim || { zerr; return 1; } clean_strapdir || { zerr; return 1; } } docker-push() { fn docker-push req=(R strapdir release registry_url registry_user registry_token registry_tag) ckreq || return 1 notice "beginning the push procedure." act "registry login" docker login -u ${registry_user} -p ${registry_token} ${registry_url} #${registry_tag} # date, $CI_COMMIT_SHA, $CI_COMMIT_REF_SLUG if [[ -n $1 ]]; then case $1 in -s|--slim) release=${release}-slim act "setting release to ${release}." shift ;; *) echo "Unknown parameter passed: $1" exit 1 ;; esac fi docker tag ${registry_url}/${release}:${registry_tag} ${registry_url}:${release} docker push ${registry_url}/${release} docker logout ${registry_url} } docker-import() { fn docker-import req=(R strapdir release apt_version) ckreq || return 1 notice "tar-ing the \$strapdir before importing for docker." act "adding additional excludes for apt versions older than 0.8" if dpkg --compare-versions "$apt_version" '>=' '0.8~'; then # if APT is new enough to auto-recreate "partial" directories, let it # (https://salsa.debian.org/apt-team/apt/commit/1cd1c398d18b78f4aa9d882a5de5385f4538e0be) excludes+=( './var/cache/apt/**' './var/lib/apt/lists/**' './var/state/apt/lists/**' ) # (see also the targeted exclusions in "tar-excludes" that these are overriding) fi act "calling docker-fixup before tar." docker-fixup act "setting arguments for tar." tarArgs=( --create --file "$strapdir" --auto-compress --directory "$strapdir" --exclude-from "${BLENDPATH}/tar-excludes" ) if [ -z "$includeDev" ]; then act "excluding the contents of /dev from the archive." excludes+=( './dev/**' ) fi for exclude in "${excludes[@]}"; do tarArgs+=( --exclude "$exclude" ) done tarArgs+=( --numeric-owner --transform 's,^./,,' --sort name . ) if [[ -n $1 ]]; then case $1 in -s|--slim) release=${release}-slim act "setting release to ${release}." shift ;; *) echo "Unknown parameter passed: $1" exit 1 ;; esac fi act "calling tar." tar "${tarArgs[@]}" - | docker import - devuan:${release} } docker-fixup() { fn docker-fixup req=(R strapdir ) ckreq || return 1 notice "removing redundant log and cache files." pushd "$strapdir" || { zerr; return 1; } rm -f \ "./var/log/dpkg.log" \ "./var/log/bootstrap.log" \ "./var/log/alternatives.log" \ "./var/cache/ldconfig/aux-cache" act "removing redundant /run mounts." # https://github.com/debuerreotype/debuerreotype/pull/32 rm -f "./run/mount/utab" # (also remove the directory, but only if it's empty) rmdir "./run/mount" 2>/dev/null || : # fix ownership/permissions on / (otherwise "debootstrap" leaves them as-is which causes reproducibility issues) chown 0:0 "./" chmod 0755 "./" popd } docker-slimify() { fn docker-slimify req=(R strapdir ) ckreq || return 1 notice "Removing files specified in the blends config-docker \$slimExcludes variable and adding the paths to the dpkg config." dest=$strapdir/etc/dpkg/dpkg.cfg.d dpkgCfgFile="${dest}/docker" mkdir -p $(dirname "$dpkgCfgFile") cat > ${dpkgCfgFile} <<-EOF # This is the "slim" variant of the Devuan base image. # Many files which are normally unnecessary in containers are excluded, # and this configuration file keeps them that way. EOF act "make a record of the directory structure under /usr/share/man." extraSpecialDirectories=( "$strapdir"/usr/share/man/man[0-9]/ ) act "processing the includes in \$slimIncludes" findMatchIncludes=() for slimInclude in "${slimIncludes[@]}"; do [ "${#findMatchIncludes[@]}" -eq 0 ] || findMatchIncludes+=( '-o' ) findMatchIncludes+=( -path "$slimInclude" ) done findMatchIncludes=( '(' "${findMatchIncludes[@]}" ')' ) act "processing the excludes in \$slimIncludes" for slimExclude in "${slimExcludes[@]}"; do { echo echo "# dpkg -S '$slimExclude'" if dpkgOutput="$(chroot-script /bin/bash -c " \ dpkg -S "$slimExclude" 2>&1")"; then echo "$dpkgOutput" | sed 's/: .*//g; s/, /\n/g' | sort -u | xargs else echo "$dpkgOutput" fi | fold -w 76 -s | sed 's/^/# /' echo "path-exclude $slimExclude" } >> "$dpkgCfgFile" notice "remove directories from slimExcludes in two passes." if [[ "$slimExclude" == *'/*' ]]; then if [ -d "$strapdir/$(dirname "$slimExclude")" ]; then # use two passes so that we don't fail trying to remove directories from $slimIncludes # this is our best effort at implementing https://sources.debian.net/src/dpkg/stretch/src/filters.c/#L96-L97 in shell act "pass one: delete everything that doesn't match '\$slimIncludes' and isn't a directory or a symlink" chroot-script /bin/bash -c " \ find "$(dirname "$slimExclude")" \ -depth -mindepth 1 \ -not \( -type d -o -type l \) \ -not "${findMatchIncludes[@]}" \ -exec rm -f '{}' ';' " act "pass two: repeatedly delete any dangling symlinks and empty directories until there aren't any" # (might have a dangling symlink in a directory which then makes it empty, or a symlink to an empty directory) while [ "$( chroot-script /bin/bash -c " \ find "$(dirname "$slimExclude")" \ -depth -mindepth 1 \( -empty -o -xtype l \) \ -exec rm -rf '{}' ';' -printf '.' \ | wc -c " )" -gt 0 ]; do true; done fi else chroot-script bash -c " rm -f "$slimExclude" " fi done act "appending includes to the dpkg conf." { echo for slimInclude in "${slimIncludes[@]}"; do echo "path-include $slimInclude" done } >> "$dpkgCfgFile" chmod 0644 "$dpkgCfgFile" act "recreate directory structure under /usr/share/man" if [ "${#extraSpecialDirectories[@]}" -gt 0 ]; then mkdir -p "${extraSpecialDirectories[@]}" fi } docker-purge_slimify_package() { fn docker-purge_slimify_packages req=(strapdir slimify_packages) ckreq || return 1 cat <<-EOF | sudo tee "$strapdir/purge_slimify_packages" >/dev/null #!/bin/sh apt-get --yes --force-yes purge ${purge_packages_option} ${slimify_packages} || exit 1 apt-get --yes --force-yes --purge autoremove || exit 1 apt-get clean EOF chroot-script -d purge_slimify_packages || { zerr; return 1; } } docker-init-policy() { fn docker-init-policy req=(R strapdir) ckreq || return 1 dest=$strapdir/usr/sbin cat > ${dest}/policy-rc.d.docker </dev/null #!/bin/sh update-alternatives --quiet --install /usr/sbin/policy-rc.d \ policy-rc.d /usr/sbin/policy-rc.d.docker 100 case "${release}" in jessie|ascii) dpkg-divert --quiet --local --add /sbin/initctl ;; *) dpkg-divert --quiet --local --no-rename --add /sbin/initctl ;; esac ln -fs /bin/true /sbin/initctl EOF chroot-script -d init-policy } docker-apt-speedup() { fn docker-apt-speedup req=(R strapdir) ckreq || return 1 dest=$strapdir/etc/dpkg/dpkg.cfg.d sudo mkdir -p ${dest} cat <<-EOF | sudo tee ${dest}/docker-apt-speedup" >/dev/null force-unsafe-io EOF chmod 0644 "${dest}/docker-apt-speedup" } docker-apt-autoremove-suggests() { fn docker-apt-autoremove-suggests req=(R strapdir) ckreq || return 1 dest=$strapdir/etc/apt/apt.conf.d sudo mkdir -p ${dest} cat <<-EOF | sudo tee ${dest}/docker-apt-autoremove-suggests" >/dev/null Apt::AutoRemove::SuggestsImportant "false"; EOF chmod 0644 "${dest}/docker-apt-autoremove-suggests" } docker-apt-clean() { fn docker-apt-clean req=(R strapdir) ckreq || return 1 dest=$strapdir/etc/apt/apt.conf.d sudo mkdir -p ${dest} aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";' cat <<-EOF | sudo tee ${dest}/docker-apt-clean" >/dev/null APT::Update::Post-Invoke { $aptGetClean }; Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache ""; EOF chmod 0644 "${dest}/docker-apt-clean" } docker-apt-gzip-indexes() { fn docker-apt-gzip-indexes req=(R strapdir) ckreq || return 1 dest=$strapdir/etc/apt/apt.conf.d sudo mkdir -p ${dest} cat <<-EOF | sudo tee ${dest}/docker-apt-gzip-indexes" >/dev/null Acquire::GzipIndexes "true"; EOF chmod 0644 "${dest}/docker-apt-gzip-indexes" } docker-apt-no-languages() { fn docker-apt-no-languages req=(R strapdir) ckreq || return 1 dest=$strapdir/etc/apt/apt.conf.d sudo mkdir -p ${dest} cat <<-EOF | sudo tee ${dest}/docker-apt-no-languages" >/dev/null Acquire::Languages "none"; EOF chmod 0644 "${dest}/docker-apt-no-languages" } remove-apt-comments() { fn remove-apt-comments req=(R strapdir apt_version) ckreq || return 1 dest=$strapdir/etc/apt/apt.conf.d if dpkg --compare-versions "$apt_version" '>=' '0.7.22~'; then exit fi sed -ri -e 's!^#!//!' "${dest}" } # set in zlibs/sysconf:conf_print_hostname() using $os # override in $BLENDPATH/sysconf:conf_print_hosts # a container’s hostname defaults to be the container’s ID in # Docker unless overriden at build. So this is less important here. docker-hostname() { fn docker-hostname req=(R strapdir hostname) ckreq || return 1 pushd act "setting the hostname." echo "${hostname}" > "./etc/hostname" chmod 0644 \ "$strapdir/etc/hostname" popd } # set in zlibs/sysconf:conf_print_resolvconf() # override in $BLENDPATH/sysconf docker-resolvconf() { fn docker-resolvconf req=(R strapdir) ckreq || return 1 pushd $strapdir act "replacing /etc/resolv.conf." cat <<-EOF | sudo tee ./etc/resolv.conf" >/dev/null # https://1.1.1.1 (privacy-focused, highly-available DNS service) nameserver 1.1.1.1 nameserver 1.0.0.1 EOF chmod 0644 \ "./etc/resolv.conf" popd } # If inside a virtual machine or container environment the # /etc/machine-id is usually taken from the container UUID #docker-id() { # fn docker-id # req=(R strapdir epoch) # ckreq || return 1 # # pushd $strapdir #act "Initialize the machine ID in /etc/machine-id" #echo "$epoch" \ # | md5sum \ # | cut -f1 -d' ' \ # > "./etc/machine-id" # chmod 0644 \ # "./etc/machine-id" # popd #}