#!/usr/bin/env bash # Native Debian baseline; Docker packages are prepared and pinned by our controller. set -euo pipefail export LC_ALL=C fail() { printf 'ERROR: %s\n' "$*" >&2; return 1; } check_existing_binding() { local state_dir=$1 expected=$2 bound='' local binding="$state_dir/binding" [[ ! -L "$state_dir" && ! -L "$binding" ]] || { fail 'Symlink in bootstrap binding'; return 1; } [[ ! -e "$binding" ]] && return 0 [[ -f "$binding" && $(stat -c %u "$binding") == 0 && $(stat -c %a "$binding") == 600 && $(stat -c %h "$binding") == 1 ]] || { fail 'Existing bootstrap binding is not a trusted 0600 regular file'; return 1; } IFS= read -r bound < "$binding" || { fail 'Cannot read bootstrap binding'; return 1; } [[ "$bound" == "$expected" ]] || { fail 'Existing bootstrap binding belongs to another hostname'; return 1; } } main() { local action='' name='' packages='' public_key='' argument while (($#)); do case "$1" in --help|-h) echo 'Usage: bash host-init.sh --check|--apply --hostname NAME [--docker-dir VERIFIED_DEB_DIRECTORY] [--ssh-key-file SHARED_PUBLIC_KEY]'; return 0;; --check|--apply) [[ -z "$action" ]] || return 1; action=$1; shift;; --hostname|--docker-dir|--ssh-key-file) (($# >= 2)) || return 1 case "$1" in --hostname) name=$2;; --docker-dir) packages=$2;; --ssh-key-file) public_key=$2;; esac; shift 2;; *) fail "Unknown argument: $1"; return 1;; esac done [[ "$action" == --check || "$action" == --apply ]] || { fail 'Choose check or apply'; return 1; } local word='[a-z][a-z0-9]{0,15}' [[ "$name" =~ ^(dev|test|stage|prod)-$word-$word(-$word)?-(0[1-9]|[1-9][0-9])$ && ${#name} -le 63 ]] || { fail 'Explicit short hostname required'; return 1; } [[ $EUID == 0 && -d /run/systemd/system && ! -e /.dockerenv ]] || { fail 'Native root systemd host required'; return 1; } [[ $(uname -m) == x86_64 ]] || { fail 'The published Periphery and Docker package set currently supports x86_64 only'; return 1; } grep -Eq '^ID="?debian"?$' /etc/os-release && grep -Eq '^VERSION_ID="?13"?$' /etc/os-release || { fail 'Debian 13 required; upgrade is a separate human-run task'; return 1; } check_existing_binding /var/lib/platform-bootstrap "$name" local debs=() file package version architecture installed seen=' ' sha extra if [[ -n "$packages" ]]; then [[ "$packages" == /* && -d "$packages" && ! -L "$packages" && -f "$packages/SHA256SUMS" && ! -L "$packages/SHA256SUMS" ]] || { fail 'Prepared package directory and manifest required'; return 1; } while read -r sha file extra; do [[ "$sha" =~ ^[a-f0-9]{64}$ && "$file" =~ ^[A-Za-z0-9][A-Za-z0-9._+%:~-]*\.deb$ && -z "$extra" && -f "$packages/$file" && ! -L "$packages/$file" ]] || { fail 'Invalid package manifest'; return 1; } printf '%s %s\n' "$sha" "$packages/$file" | sha256sum -c - >/dev/null package=$(dpkg-deb -f "$packages/$file" Package); version=$(dpkg-deb -f "$packages/$file" Version); architecture=$(dpkg-deb -f "$packages/$file" Architecture) [[ "$architecture" == amd64 ]] || { fail 'Prepared Docker packages must be amd64'; return 1; } case "$package" in docker-ce|docker-ce-cli|containerd.io|docker-buildx-plugin|docker-compose-plugin) :;; *) fail 'Unexpected Docker package'; return 1;; esac [[ "$seen" != *" $package "* ]] || { fail 'Duplicate package'; return 1; }; seen+="$package " installed=$(dpkg-query -W -f='${Version}' "$package" 2>/dev/null) || installed='' [[ -z "$installed" || "$installed" == "$version" ]] || { fail 'Existing Docker version differs; plan explicit maintenance'; return 1; } debs+=("$packages/$file") done < "$packages/SHA256SUMS" [[ ${#debs[@]} == 5 ]] || { fail 'All five pinned official Docker packages required'; return 1; } else command -v docker >/dev/null && docker compose version >/dev/null || { fail 'Prepare the five pinned Docker DEBs through our resource publication first'; return 1; } fi if [[ -n "$public_key" ]]; then [[ -f "$public_key" && ! -L "$public_key" && $(wc -l < "$public_key") == 1 ]] || { fail 'One shared public key line required'; return 1; } grep -Eq '^(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp(256|384|521)) [A-Za-z0-9+/=]+( .*)?$' "$public_key" || { fail 'Public key format invalid'; return 1; } ssh-keygen -l -f "$public_key" >/dev/null [[ ! -L /root/.ssh && ! -L /root/.ssh/authorized_keys ]] || { fail 'Symlink in SSH destination'; return 1; } fi [[ "$action" == --apply ]] || { echo 'Baseline inputs checked; no packages, hostname or SSH configuration changed.'; return 0; } umask 077 exec 9>>/run/lock/platform-host-init.lock; flock -n 9 || { fail 'Host initialization is already running'; return 1; } check_existing_binding /var/lib/platform-bootstrap "$name" apt-get -o DPkg::Lock::Timeout=60 update apt-get -o DPkg::Lock::Timeout=60 install -y --no-install-recommends ca-certificates curl jq git openssl openssh-client iproute2 if ((${#debs[@]})); then apt-get -o DPkg::Lock::Timeout=60 install -y "${debs[@]}"; fi hostnamectl set-hostname "$name" if [[ -n "$public_key" ]]; then install -d -m 0700 /root/.ssh touch /root/.ssh/authorized_keys; chmod 0600 /root/.ssh/authorized_keys if ! grep -Fqx -- "$(cat "$public_key")" /root/.ssh/authorized_keys; then printf '\n' >> /root/.ssh/authorized_keys; cat "$public_key" >> /root/.ssh/authorized_keys; fi fi systemctl enable --now docker docker --host unix:///var/run/docker.sock info >/dev/null docker --host unix:///var/run/docker.sock compose version [[ $(hostname) == "$name" ]] echo 'Baseline and local Docker checked. Verify a fresh SSH connection; firewall/SSH port and network acceptance are separate tasks.' } if [[ ${BASH_SOURCE[0]:-} == "$0" || -z ${BASH_SOURCE[0]:-} ]]; then main "$@"; fi