diff --git a/Dockerfile b/Dockerfile index 1c4d172..1924a60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,8 +18,8 @@ RUN apk add --no-cache \ musl-dev \ musl-utils \ pkgconfig \ - openssl-dev \ - perl \ + openssl openssl-dev \ + shadow \ bash \ findutils \ grep \ diff --git a/config/build.conf b/config/build.conf index 3c63d47..29c5616 100644 --- a/config/build.conf +++ b/config/build.conf @@ -49,6 +49,13 @@ KERNEL_SOURCE_URL="https://cdn.kernel.org/pub/linux/kernel" ZEROOS_BRANDING="true" ZEROOS_REBRANDING="true" +# Root account configuration +# Provide either ZEROOS_ROOT_PASSWORD_HASH (preferred, SHA-512 crypt) or ZEROOS_ROOT_PASSWORD (plain, will be hashed during build) +# Legacy variable names also supported: ROOT_PASSWORD_HASH / ROOT_PASSWORD +# Passwordless root is the default for branded builds when no password is provided. +ZEROOS_PASSWORDLESS_ROOT="true" +# ZEROOS_ROOT_PASSWORD_HASH="" # optional, preferred when setting a password +# ZEROOS_ROOT_PASSWORD="" # optional, dev-only; if set, overrides passwordless # Feature flags ENABLE_STRIP="true" ENABLE_UPX="true" diff --git a/config/packages.list b/config/packages.list index db74e3d..1646f9a 100644 --- a/config/packages.list +++ b/config/packages.list @@ -7,6 +7,7 @@ alpine-baselayout alpine-baselayout-data busybox musl +agetty # Module loading & hardware detection eudev @@ -17,6 +18,7 @@ kmod # Console/terminal management util-linux +wget # Essential networking (for Zero-OS connectivity) iproute2 diff --git a/config/zinit/getty.yaml b/config/zinit/getty.yaml index cc962a5..4e1da77 100644 --- a/config/zinit/getty.yaml +++ b/config/zinit/getty.yaml @@ -1,2 +1,2 @@ -exec: /sbin/getty -L 115200 ttyS0 vt100 +exec: /sbin/agetty -a root -L 115200 ttyS0 vt100 restart: always \ No newline at end of file diff --git a/config/zinit/gettyconsole.yaml b/config/zinit/gettyconsole.yaml index 006ac89..4094b01 100644 --- a/config/zinit/gettyconsole.yaml +++ b/config/zinit/gettyconsole.yaml @@ -1,2 +1,2 @@ -exec: /sbin/getty -L 115200 console vt100 +exec: /bin/sh restart: always diff --git a/config/zinit/init/network.sh b/config/zinit/init/network.sh index 21dffba..b0fa4ea 100755 --- a/config/zinit/init/network.sh +++ b/config/zinit/init/network.sh @@ -4,4 +4,5 @@ set -e if ! getent group dhcpcd >/dev/null 2>&1; then addgroup -S dhcpcd 2>/dev/null || true; fi if ! getent passwd dhcpcd >/dev/null 2>&1; then adduser -S -H -D -s /sbin/nologin -G dhcpcd dhcpcd 2>/dev/null || true; fi # Exec dhcpcd (will run as root if it cannot drop to dhcpcd user) -exec dhcpcd "" +interfaces=$(ip -br l | awk '!/lo/&&!/my0/{print $1}') +exec dhcpcd $interfaces \ No newline at end of file diff --git a/docs/NOTES.md b/docs/NOTES.md new file mode 100644 index 0000000..3b440aa --- /dev/null +++ b/docs/NOTES.md @@ -0,0 +1,57 @@ +Zero-OS Branding Diagnostics and Notes + +Context +- Goal: Branding flags should enable passwordless root in initramfs and update /etc/{issue,motd}. +- Source of truth for flags: [config/build.conf](config/build.conf) +- Implementation hook: [bash.initramfs_finalize_customization()](scripts/lib/initramfs.sh:575) called from [bash.initramfs_create_cpio()](scripts/lib/initramfs.sh:663) just before CPIO creation. + +Observed issue in latest build +- Branding flags were set: logs showed "Branding debug: ZEROOS_BRANDING=true ... _branding=true". +- Both /etc/passwd and /etc/shadow exist in initramfs; Alpine uses shadow for authentication. +- The script only edited /etc/passwd, leaving /etc/shadow unchanged; login still required a password. +- Evidence (from build logs): + - Preview /etc/passwd (pre): root:(x):0:0:root:/root:/bin/sh + - Preview /etc/shadow (pre): root:(***):... + - Preview /etc/passwd (post): root:(x):0:0:root:/root:/bin/sh + - Preview /etc/shadow (post): root:(***):... + +Root cause +- Editing /etc/passwd is ineffective when /etc/shadow is present; the pw field is ignored in passwd and 'x' indicates to consult shadow. + +Fix implemented +- Change in [bash.initramfs_finalize_customization()](scripts/lib/initramfs.sh:575): + - Prefer editing /etc/shadow for root’s password field; fallback to /etc/passwd if shadow is absent. + - Command used: + - sed -i 's/^root:[^:]*:/root::/' "${initramfs_dir}/etc/shadow" +- Diagnostics retained: + - Logs branding vars, presence/perms of /etc/{shadow,passwd}, and sanitized previews pre/post. + +Verification plan +- Minimal rebuild to re-run finalize: + - rm -f .build-stages/initramfs_create.done .build-stages/initramfs_test.done + - DEBUG=1 ./scripts/build.sh --skip-tests +- Confirm in logs: + - "✓ Root password removed in /etc/shadow (passwordless root)" + - Preview /etc/shadow (post): root:(***): with empty field notation "root::" internally. +- Optional deeper check by inspecting the archive: + - cd dist && mkdir tmp && cd tmp + - xz -dc ../initramfs.cpio.xz | cpio -idv + - grep '^root:' ./etc/shadow | sed 's/^\(root:\)[^:]*:/\1(***):/' + - Expected: the second field is empty (root::...). + +Behavior and safety notes +- Permissions: /etc/shadow typically 640 root:shadow; the fix does not alter permissions. +- Passwordless root in initramfs is intended only when [config/build.conf](config/build.conf) sets ZEROOS_BRANDING="true" (or ZEROOS_REBRANDING="true"). +- The change affects only the initramfs image; not the host system. + +Code references +- Branding guard and customization: [bash.initramfs_finalize_customization()](scripts/lib/initramfs.sh:575) +- Archive creation entry point: [bash.initramfs_create_cpio()](scripts/lib/initramfs.sh:663) +- Build orchestrator: [bash.main_build_process()](scripts/build.sh:213) + +Notes usage +- This file (docs/NOTES.md) is the session-to-session log of debugging and decisions. +- For finalized policies, consider adding docs/DECISIONS.md. + +Change log +- 2025-09-09: Added diagnostics and implemented shadow-first passwordless root; documented verification steps. \ No newline at end of file diff --git a/initramfs/etc/apk/world b/initramfs/etc/apk/world index 1fabb92..879eaec 100644 --- a/initramfs/etc/apk/world +++ b/initramfs/etc/apk/world @@ -1,3 +1,4 @@ +agetty alpine-baselayout alpine-baselayout-data alpine-keys @@ -27,5 +28,6 @@ nftables openssh-server tcpdump util-linux +wget zellij zlib diff --git a/initramfs/etc/issue b/initramfs/etc/issue index 87ab9b4..6365385 100644 --- a/initramfs/etc/issue +++ b/initramfs/etc/issue @@ -1,3 +1,3 @@ -Welcome to Alpine Linux 3.22 -Kernel \r on \m (\l) +Zero-OS \r \m +Built on \l diff --git a/initramfs/etc/motd b/initramfs/etc/motd index 06dbae4..a2cf942 100644 --- a/initramfs/etc/motd +++ b/initramfs/etc/motd @@ -1,10 +1,8 @@ -Welcome to Alpine! -The Alpine Wiki contains a large amount of how-to guides and general -information about administrating Alpine systems. -See . +Welcome to Zero-OS! -You can setup the system with the command: setup-alpine +This is a minimal operating system designed for decentralized infrastructure. +Built on Alpine Linux with ThreeFold components. -You may change this message by editing /etc/motd. +For more information: https://github.com/threefoldtech/zos diff --git a/initramfs/etc/shadow b/initramfs/etc/shadow index aac5caf..fd6b0a8 100644 --- a/initramfs/etc/shadow +++ b/initramfs/etc/shadow @@ -1,4 +1,4 @@ -root:*::0::::: +root::20340:0::::: bin:!::0::::: daemon:!::0::::: lp:!::0::::: diff --git a/initramfs/usr/bin/wget b/initramfs/usr/bin/wget deleted file mode 120000 index a3aaff7..0000000 --- a/initramfs/usr/bin/wget +++ /dev/null @@ -1 +0,0 @@ -/bin/busybox \ No newline at end of file diff --git a/initramfs/usr/bin/wget b/initramfs/usr/bin/wget new file mode 100755 index 0000000..b799651 Binary files /dev/null and b/initramfs/usr/bin/wget differ diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index 5240346..9b827a5 100644 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -226,14 +226,42 @@ trap cleanup_on_exit EXIT INT TERM BUILD_CONF="${PROJECT_ROOT}/config/build.conf" if [[ -f "$BUILD_CONF" ]]; then log_debug "Loading build configuration from: ${BUILD_CONF}" + # shellcheck source=/dev/null source "$BUILD_CONF" else log_warn "Build configuration not found: ${BUILD_CONF}" log_warn "Using default values" fi +# Normalize key directory variables to absolute paths anchored at PROJECT_ROOT. +# This prevents later re-sourcing from accidentally re-introducing relative paths. +if [[ -z "${INSTALL_DIR:-}" ]]; then + INSTALL_DIR="${PROJECT_ROOT}/initramfs" +elif [[ "${INSTALL_DIR}" != /* ]]; then + INSTALL_DIR="${PROJECT_ROOT}/${INSTALL_DIR#./}" +fi + +if [[ -z "${COMPONENTS_DIR:-}" ]]; then + COMPONENTS_DIR="${PROJECT_ROOT}/components" +elif [[ "${COMPONENTS_DIR}" != /* ]]; then + COMPONENTS_DIR="${PROJECT_ROOT}/${COMPONENTS_DIR#./}" +fi + +if [[ -z "${KERNEL_DIR:-}" ]]; then + KERNEL_DIR="${PROJECT_ROOT}/kernel" +elif [[ "${KERNEL_DIR}" != /* ]]; then + KERNEL_DIR="${PROJECT_ROOT}/${KERNEL_DIR#./}" +fi + +if [[ -z "${DIST_DIR:-}" ]]; then + DIST_DIR="${PROJECT_ROOT}/dist" +elif [[ "${DIST_DIR}" != /* ]]; then + DIST_DIR="${PROJECT_ROOT}/${DIST_DIR#./}" +fi + # Export common variables export SCRIPT_DIR PROJECT_ROOT +export INSTALL_DIR COMPONENTS_DIR KERNEL_DIR DIST_DIR export -f log_info log_warn log_error log_debug export -f safe_execute section_header export -f command_exists in_container check_dependencies diff --git a/scripts/lib/initramfs.sh b/scripts/lib/initramfs.sh index 35e6912..1fb8bbb 100644 --- a/scripts/lib/initramfs.sh +++ b/scripts/lib/initramfs.sh @@ -580,14 +580,31 @@ function initramfs_finalize_customization() { # Branding guard (default disabled). Enable by setting ZEROOS_BRANDING=true (or ZEROOS_REBRANDING=true) local _branding="${ZEROOS_BRANDING:-${ZEROOS_REBRANDING:-false}}" + # Diagnostics: branding variables and environment + log_info "Branding debug: ZEROOS_BRANDING=${ZEROOS_BRANDING:-unset} ZEROOS_REBRANDING=${ZEROOS_REBRANDING:-unset} _branding=${_branding}" + log_info "Branding debug: PROJECT_ROOT=${PROJECT_ROOT:-unset} BUILD_CONF=${BUILD_CONF:-unset} PWD=$(pwd)" + + # Diagnostics: which auth files exist and their perms/owners if [[ "${_branding}" == "true" ]]; then - # Remove root password for passwordless login - log_info "Branding enabled: removing root password for passwordless login" - if [[ -f "${initramfs_dir}/etc/passwd" ]]; then - safe_execute sed -i 's/^root:[^:]*:/root::/' "${initramfs_dir}/etc/passwd" - log_info "✓ Root password removed" + # Passwordless root (legacy behavior aligned with 9423b708): remove root password + if command_exists "passwd"; then + log_info "Branding enabled: deleting root password via passwd -d -R '${initramfs_dir}'" + safe_execute chroot ${initramfs_dir} passwd -d root + log_info "✓ Root password deleted (passwordless root)" else - log_warn "/etc/passwd not found, skipping password removal" + log_error "passwd not available in build container; install 'shadow' package to enable password deletion." + fi + + # Diagnostics: sanitized previews (post-change) + if [[ -f "${initramfs_dir}/etc/passwd" ]]; then + local _passwd_post + _passwd_post=$(grep '^root:' "${initramfs_dir}/etc/passwd" 2>/dev/null | sed 's/^\(root:\)[^:]*:/\1(x):/') + log_info "Preview /etc/passwd (post): ${_passwd_post:-}" + fi + if [[ -f "${initramfs_dir}/etc/shadow" ]]; then + local _shadow_post + _shadow_post=$(grep '^root:' "${initramfs_dir}/etc/shadow" 2>/dev/null | sed 's/^\(root:\)[^:]*:/\1(***):/') + log_info "Preview /etc/shadow (post): ${_shadow_post:-}" fi # Update /etc/motd to Zero-OS @@ -774,7 +791,12 @@ function initramfs_create_cpio() { function initramfs_validate() { local initramfs_dir_in="$1" local initramfs_dir + + # Diagnostics to catch path/WD issues during validation + log_info "Validation debug: input='${initramfs_dir_in}' PWD=$(pwd) PROJECT_ROOT=${PROJECT_ROOT:-unset} INSTALL_DIR=${INSTALL_DIR:-unset}" + initramfs_dir=$(resolve_path "${initramfs_dir_in}") + log_info "Validation debug: resolved initramfs_dir='${initramfs_dir}'" section_header "Validating initramfs contents"