Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 41 additions & 6 deletions .github/workflows/Build-Bootstraper.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,35 @@ jobs:
submodules: false

- name: Prepare
id: prep_output
run: |
platform=linux/${{ matrix.architecture }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
if [ "${{ matrix.architecture }}" == "amd64" ]; then
echo "TARGET_TRIPLE=x86_64-generic-none-musl" >> $GITHUB_ENV
echo "target-triple=x86_64-generic-none-musl" >> "$GITHUB_OUTPUT"
echo "HOST_TRIPLE=x86_64-alpine-linux-musl" >> $GITHUB_ENV
echo "host-triple=x86_64-alpine-linux-musl" >> "$GITHUB_OUTPUT"
echo "MUSL_LDLIB=ld-musl-x86_64.so.1" >> $GITHUB_ENV
echo "musl-ldlib=ld-musl-x86_64.so.1" >> "$GITHUB_OUTPUT"
elif [ "${{ matrix.architecture }}" == "arm64" ]; then
echo "TARGET_TRIPLE=aarch64-generic-none-musl" >> $GITHUB_ENV
echo "target-triple=aarch64-generic-none-musl" >> "$GITHUB_OUTPUT"
echo "HOST_TRIPLE=aarch64-alpine-linux-musl" >> $GITHUB_ENV
echo "host-triple=aarch64-alpine-linux-musl" >> "$GITHUB_OUTPUT"
echo "MUSL_LDLIB=ld-musl-aarch64.so.1" >> $GITHUB_ENV
echo "musl-ldlib=ld-musl-aarch64.so.1" >> "$GITHUB_OUTPUT"
elif [ "${{ matrix.architecture }}" == "arm" ]; then
echo "TARGET_TRIPLE=arm-generic-none-musleabi" >> $GITHUB_ENV
echo "target-triple=arm-generic-none-musleabi" >> "$GITHUB_OUTPUT"
echo "HOST_TRIPLE=arm-alpine-linux-musleabi" >> $GITHUB_ENV
echo "host-triple=arm-alpine-linux-musleabi" >> "$GITHUB_OUTPUT"
echo "MUSL_LDLIB=ld-musl-armhf.so.1" >> $GITHUB_ENV
echo "musl-ldlib=ld-musl-armhf.so.1" >> "$GITHUB_OUTPUT"
else
echo "Unsupported architecture: ${{ matrix.architecture }}"
exit 1
fi

# QEMU setup only for armv7 emulation
- name: Set up QEMU (for armv7)
Expand Down Expand Up @@ -126,7 +152,10 @@ jobs:
build-args: |
TOYBOX_VERSION=0.8.12
TARGETARCH=${{ matrix.architecture }}
TARGET_TRIPLE=${{ steps.prep_output.outputs.target-triple }}
HOST_TRIPLE=${{ steps.prep_output.outputs.host-triple }}
MUSL_VER=1.2.5
MUSL_LDLIB=${{ steps.prep_output.outputs.musl-ldlib }}
MITL_DATE_EPOCH=${{ needs.seed.outputs.mitl-timestamp }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
Expand All @@ -144,6 +173,9 @@ jobs:
index.org.opencontainers.image.licenses="MIT"
outputs: type=image,push-by-digest=true,name-canonical=true,push=true,annotation-index.org.opencontainers.image.description=Multi-arch MITL Bootstrap image ${{ matrix.architecture }}
env:
TARGET_TRIPLE: ${{ steps.prep_output.outputs.target-triple }}
HOST_TRIPLE: ${{ steps.prep_output.outputs.host-triple }}
MUSL_LDLIB: ${{ steps.prep_output.outputs.musl-ldlib }}
SOURCE_DATE_EPOCH: ${{ needs.seed.outputs.timestamp }}
MITL_DATE_EPOCH: ${{ needs.seed.outputs.mitl-timestamp }}
TOYBOX_VERSION: "0.8.12"
Expand All @@ -159,7 +191,7 @@ jobs:
run: docker logout

- name: Upload digest
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
Expand Down Expand Up @@ -187,7 +219,7 @@ jobs:
submodules: false

- name: Download digests
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
Expand Down Expand Up @@ -263,7 +295,7 @@ jobs:
# (grype exits non-zero on findings; keep the job green by allowing non-zero)

- name: Upload per-arch SBOM and Grype report
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: sbom-and-scan
path: |
Expand Down Expand Up @@ -308,7 +340,7 @@ jobs:
run: |
docker run -d --name test-container ghcr.io/reactive-firewall/mitl-bootstrap:${{ needs.merge.outputs.merge_version }} /bin/bash -c "while true; do sleep 30; done"
printf "::group::%s\n" "Testing"
docker exec test-container /bin/bash -c "toybox bash -c 'echo Toybox shell is working'" || exit 1
docker exec test-container /bin/sh -c "toybox sh -c 'echo Toybox shell is working'" || exit 1
docker exec test-container /bin/bash --version
docker exec test-container /bin/bash -c "toybox bash --version"
docker exec test-container /bin/bash -c "printf '%s\n' 'Toybox printf is working'" || exit 1
Expand All @@ -330,7 +362,7 @@ jobs:
else
printf "%s\n" "gunzip command is not present in the image."
fi
for REQ_CMD in "bash" "basename" "cat" "chgrp" "chmod" "chown" "cp" "date" "dirname" "find" "grep" "head" "mkdir" "mv" "rm" "sed" "sha256sum" "sha512sum" ; do
for REQ_CMD in "bash" "basename" "cat" "chgrp" "chmod" "chown" "cp" "date" "dirname" "find" "grep" "head" "mkdir" "mv" "rm" "sed" "sh" "sha256sum" "sha512sum" "true" "false" ; do
if docker exec test-container /bin/bash -c "which ${REQ_CMD}"; then
printf "%s\n" "${REQ_CMD:-unknown} command is present in the image." ;
printf "%s\n" "${REQ_CMD:-unknown} command is configured in the path." ;
Expand All @@ -346,7 +378,7 @@ jobs:
fi ;
done ;
printf "::endgroup::\n\n"
for REQ_PATH in "/bin" "/usr/" "/sbin" "/usr/bin" "/usr/local/bin" "/usr/local/sbin" "/usr/lib" "/usr/libexec" "/etc" "/var" "/" ; do
for REQ_PATH in "/bin" "/usr/" "/sbin" "/lib" "/usr/bin" "/usr/local/bin" "/usr/local/bin" "/usr/local/sbin" "/usr/lib" "/usr/libexec" "/etc" "/var" "/" ; do
printf "::group::%s\n" "${REQ_PATH}"
if docker exec test-container /bin/bash -c "test -d ${REQ_PATH}"; then
printf "%s\n" "${REQ_PATH:-unknown} path is present in the image." ;
Expand All @@ -360,6 +392,9 @@ jobs:
printf "%s\n" "${REQ_PATH:-unknown} path is present in the image." ;
printf "%s\n" "${REQ_PATH:-unknown} path is not a directory in the image." ;
printf "::endgroup::\n"
printf "::group::%s\n" "Stats"
docker exec test-container /bin/bash -c "ls -la ${REQ_PATH}" ;
printf "::endgroup::\n"
else
printf "%s\n" "${REQ_PATH:-unknown} path is not present in the image."
printf "::endgroup::\n"
Expand Down
64 changes: 40 additions & 24 deletions dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@ FROM --platform="linux/${TARGETARCH}" alpine:latest AS musl-builder
# shellcheck disable=SC2154
ARG MUSL_VER=${MUSL_VER:-"1.2.5"}
ENV MUSL_VER=${MUSL_VER}
ENV MUSL_PREFIX="/usr/local/musl-llvm-staging"
ENV MUSL_PREFIX="/usr"
ENV MUSL_SYSROOT="/usr/local/musl-llvm-staging"

RUN set -eux \
&& apk add --no-cache \
cmd:bsdtar \
clang \
llvm \
cmd:llvm-ar \
libc++ \
libc++-dev \
compiler-rt \
llvm-runtimes \
lld \
make \
binutils \
Expand All @@ -26,7 +31,8 @@ RUN set -eux \
gzip \
perl \
paxctl \
&& mkdir -p /build
&& mkdir -vp /build \
&& mkdir -vp "${MUSL_SYSROOT}"

WORKDIR /build
ENV CC=clang
Expand All @@ -38,6 +44,8 @@ ENV LDFLAGS="-fuse-ld=lld"
# shellcheck disable=SC2154
ARG MITL_DATE_EPOCH
ENV MITL_DATE_EPOCH=${MITL_DATE_EPOCH}
ARG TARGET_TRIPLE
ENV TARGET_TRIPLE=${TARGET_TRIPLE}

# Download musl
RUN curl -fsSL \
Expand All @@ -50,25 +58,33 @@ WORKDIR /build/musl

# Configure, build, and install musl with shared enabled (default) using LLVM tools
RUN mkdir -p ${MUSL_PREFIX} && \
./configure --prefix=${MUSL_PREFIX} && \
make CC=clang CFLAGS="${CFLAGS} -fno-math-errno -fPIC -fno-common" AR=llvm-ar LDFLAGS="${LDFLAGS}" -j"$(nproc)" && \
make install
./configure --prefix=${MUSL_PREFIX} --target=${TARGET_TRIPLE} \
CC=clang CFLAGS="${CFLAGS} -stdlib=libc++ -rtlib=compiler-rt -fno-math-errno -fPIC -fno-common" AR=llvm-ar LDFLAGS="${LDFLAGS}" && \
make -j"$(nproc)" && \
DESTDIR=${MUSL_SYSROOT} make install

# Ensure we have the dynamic loader and libs present (example paths)
RUN ls -l ${MUSL_PREFIX}/lib || true \
&& file ${MUSL_PREFIX}/lib/* || true
# Ensure we have the dynamic loader and libs present
RUN ls -l ${MUSL_SYSROOT}${MUSL_PREFIX}/lib || true \
&& file ${MUSL_SYSROOT}${MUSL_PREFIX}/lib/* || true

# Strip unneeded symbols from shared objects to save space (optional)
RUN set -eux \
&& if command -v llvm-strip >/dev/null 2>&1; then \
find ${MUSL_PREFIX}/lib -type f -name "*.so*" -exec llvm-strip --strip-unneeded {} + || true; \
find ${MUSL_SYSROOT}${MUSL_PREFIX}/lib -type f -name "*.so*" -exec llvm-strip --strip-unneeded {} + || true; \
find ${MUSL_SYSROOT}${MUSL_PREFIX}/lib -type f -name "*.o*" -exec llvm-strip --strip-unneeded {} + || true; \
else \
find ${MUSL_PREFIX}/lib -type f -name "*.so*" -exec strip --strip-unneeded {} + || true; \
find ${MUSL_SYSROOT}${MUSL_PREFIX}/lib -type f -name "*.so*" -exec strip --strip-unneeded {} + || true; \
find ${MUSL_SYSROOT}${MUSL_PREFIX}/lib -type f -name "*.o*" -exec strip --strip-unneeded {} + || true; \
fi

RUN touch -d ${MITL_DATE_EPOCH} ${MUSL_PREFIX}/lib/* || true \
&& touch -d ${MITL_DATE_EPOCH} ${MUSL_PREFIX}/include/* || true
RUN find ${MUSL_SYSROOT}${MUSL_PREFIX}/lib -type f -name "*.so" -exec touch -d "${MITL_DATE_EPOCH}" {} + || true; \
find ${MUSL_SYSROOT}${MUSL_PREFIX}/lib -type f -name "*.o" -exec touch -d "${MITL_DATE_EPOCH}" {} + || true; \
find ${MUSL_SYSROOT}${MUSL_PREFIX}/lib -type f -name "*.a" -exec touch -d "${MITL_DATE_EPOCH}" {} + || true; \
find ${MUSL_SYSROOT}${MUSL_PREFIX}/include -type f -exec touch -d "${MITL_DATE_EPOCH}" {} + || true;

# Ensure we have the dynamic loader and libs striped and set
RUN ls -l ${MUSL_SYSROOT}${MUSL_PREFIX}/lib || true \
&& file ${MUSL_SYSROOT}${MUSL_PREFIX}/lib/* || true

# Stage 2: Build toybox based filesystem
# Use MIT licensed Alpine as the base image for the build environment
Expand Down Expand Up @@ -97,7 +113,9 @@ ENV LINUX=/usr/include/linux
# shellcheck disable=SC2154
ARG MUSL_VER=${MUSL_VER:-"1.2.5"}
ENV MUSL_VER=${MUSL_VER}
ENV MUSL_PREFIX="/usr/local/musl-llvm-staging"
ENV MUSL_PREFIX="/usr/local/musl-llvm-staging/usr"
ARG MUSL_LDLIB
ENV MUSL_LDLIB="${MUSL_LDLIB}"

# Install necessary packages
# llvm - LLVM-apache-2
Expand Down Expand Up @@ -181,7 +199,7 @@ RUN if [ -f .config ]; then \
RUN rm -rf generated flags.* || true && make oldconfig || true

# build with clang and lld
RUN make V=1 CC=clang CFLAGS="-fno-math-errno -fstrict-aliasing -fPIC -fno-common" AR=llvm-ar LINUX="${LINUX}" LDFLAGS="${LDFLAGS}" toybox root && \
RUN make V=1 CC=clang CFLAGS="-fno-math-errno -fstrict-aliasing -fPIC -fno-common" AR=llvm-ar LINUX="${LINUX}" LDFLAGS="-fmerge-constants ${LDFLAGS}" toybox root && \
mkdir -p /output/usr/bin /output/etc /output/lib && \
make install PREFIX=/usr DESTDIR=/output

Expand All @@ -195,21 +213,20 @@ RUN /usr/bin/mkroot.bash && \
printf "root:x:0:0:root:/root:/bin/sh\n" > "${DESTDIR}"/etc/passwd && \
printf "/dev/sda / ext4 defaults 0 1\n" > "${DESTDIR}"/etc/fstab && \
printf "# List of acceptable shells for chpass(1).\n\n/bin/sh\n/bin/bash\n" > "${DESTDIR}"/etc/shells && \
touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/etc/* || true;
touch -d "${MITL_DATE_EPOCH}" "${DESTDIR}"/etc/* || true;

# Copy musl runtime artifacts from builder:
# - dynamic loader (ld-musl-*.so.1)
# - libmusl shared object(s) (libc.so.*)
# - crt*.o (for static linking if needed)
# - headers
COPY --from=musl-builder ${MUSL_PREFIX}/lib/ld-musl-*.so.* "${DESTDIR}"/lib/
COPY --from=musl-builder ${MUSL_PREFIX}/lib/crt*.o "${DESTDIR}"/lib/
COPY --from=musl-builder ${MUSL_PREFIX}/lib/libc.so* "${DESTDIR}"/usr/lib/
COPY --from=musl-builder ${MUSL_PREFIX}/lib "${DESTDIR}"/lib
COPY --from=musl-builder ${MUSL_PREFIX}/include "${DESTDIR}"/usr/include

RUN touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/* || true \
&& touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/usr/include/* || true \
&& touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/usr/lib/* || true
RUN find "${DESTDIR}"/lib -type f -name "*.so" -exec touch -d "${MITL_DATE_EPOCH}" {} + || true; \
find "${DESTDIR}"/lib -type f -name "*.o" -exec touch -d "${MITL_DATE_EPOCH}" {} + || true; \
find "${DESTDIR}"/lib -type f -name "*.a" -exec touch -d "${MITL_DATE_EPOCH}" {} + || true; \
find "${DESTDIR}"/usr/include -type f -exec touch -d "${MITL_DATE_EPOCH}" {} + || true;

# Some systems expect /lib64 -> /lib for x86_64. Create symlink if appropriate (unsupported by musl).
RUN set -eux; \
Expand All @@ -219,9 +236,8 @@ RUN set -eux; \

# Ensure loader has canonical name (example: /lib/ld-musl-x86_64.so.1)
RUN set -eux \
&& for f in "${DESTDIR}"/lib/ld-musl-*; do \
ln -fns "$f" "${DESTDIR}"/lib/ld-musl.so.1 || true; \
done || true
&& ln -fns /lib/libc.so "${DESTDIR}/lib/${MUSL_LDLIB}" \
&& ln -fns "/lib/${MUSL_LDLIB}" "${DESTDIR}/lib/ld-musl.so.1"

# Stage 3: Copy over featherHash (0BSD Licensed)
FROM --platform="linux/${TARGETARCH}" ghcr.io/reactive-firewall/featherhash-shasum:master AS mitl-featherhash
Expand Down
18 changes: 9 additions & 9 deletions mkroot.bash
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ fn_host_do_cmd() {
for FILE in bin lib sbin tmp usr usr/bin usr/libexec usr/local usr/share usr/include var Users ; do
fn_host_do_cmd mkdir -p "${DESTDIR}/${FILE}" ;
fn_host_do_cmd chmod 755 "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" 2>/dev/null || true ;
fn_host_do_cmd touch -d "${MITL_DATE_EPOCH}" "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -mad "${MITL_DATE_EPOCH}" "${DESTDIR}/${FILE}" 2>/dev/null || true ;
done ;

# basic sym-links
for FILE in sbin lib ; do
fn_host_do_cmd ln -s ../../"${FILE}" "${DESTDIR}/usr/${FILE}" ;
fn_host_do_cmd ln -s "../../${FILE}" "${DESTDIR}/usr/${FILE}" ;
done ;

# fn_host_do_cmd ln -s ../"Users" "${DESTDIR}/home" ;
Expand All @@ -140,13 +140,13 @@ fn_host_do_cmd rm "${HOST_TOOLCHAIN_PATH}/etc/os-release" 2>/dev/null || true ;

for FILE in dev etc init usr/lib mnt ; do
fn_host_do_cmd mv "${HOST_TOOLCHAIN_PATH}/${FILE}" "${DESTDIR}/${FILE}" ;
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" 2>/dev/null || true ;
fn_host_do_cmd touch -d "${MITL_DATE_EPOCH}" "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -mad "${MITL_DATE_EPOCH}" "${DESTDIR}/${FILE}" 2>/dev/null || true ;
done ;

fn_host_do_cmd cp -f "${HOST_TOOLCHAIN_PATH}/usr/bin/toybox" "${DESTDIR}/usr/bin/toybox" 2>/dev/null || true ;
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/usr/bin/toybox" || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/usr/bin/toybox" 2>/dev/null || true ;
fn_host_do_cmd touch -d "${MITL_DATE_EPOCH}" "${DESTDIR}/usr/bin/toybox" || true ;
fn_host_do_cmd touch -mad "${MITL_DATE_EPOCH}" "${DESTDIR}/usr/bin/toybox" 2>/dev/null || true ;

for FILE in "bash" "basename" "cat" "chgrp" "chmod" "chown" "cp" "date" "dirname" "find" "grep" "head" "halt" "ls" "ln" "mkdir" "mv" "printf" "rm" "sed" ; do
fn_host_do_cmd ln -s "toybox" "${DESTDIR}/usr/bin/${FILE}" 2>/dev/null || true ;
Expand All @@ -156,8 +156,8 @@ fn_host_do_cmd ln -s "../usr/bin/toybox" "${DESTDIR}/bin/sh" 2>/dev/null || true
fn_host_do_cmd ln -s "../usr/bin/toybox" "${DESTDIR}/bin/bash" 2>/dev/null || true ;

for FILE in bin lib sbin tmp usr usr/bin usr/libexec usr/local usr/share usr/include var Users ; do
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}"/* || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}"/* 2>/dev/null || true ;
fn_host_do_cmd touch -d "${MITL_DATE_EPOCH}" "${DESTDIR}/${FILE}/*" || true ;
fn_host_do_cmd touch -mad "${MITL_DATE_EPOCH}" "${DESTDIR}/${FILE}/*" 2>/dev/null || true ;
done ;

# fn_host_do_cmd sha256sum "${DESTDIR}/${FILE}" || true ;
Expand Down