From b5e24333ce2252491b9ad77f185813e3302c383f Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Fri, 4 Sep 2026 19:26:44 +0200 Subject: [PATCH 01/26] Add the missing suse16 profile.yaml Without it osdeploy import cannot generate a profile at all: generate_stock_profiles opens profile.yaml unguarded, and initprofile.sh seds the label into it. The label substitution also still looked for 'sle 15'. --- confluent_osdeploy/suse16/profiles/server/initprofile.sh | 2 +- confluent_osdeploy/suse16/profiles/server/profile.yaml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 confluent_osdeploy/suse16/profiles/server/profile.yaml diff --git a/confluent_osdeploy/suse16/profiles/server/initprofile.sh b/confluent_osdeploy/suse16/profiles/server/initprofile.sh index d973ef39..64ffae11 100644 --- a/confluent_osdeploy/suse16/profiles/server/initprofile.sh +++ b/confluent_osdeploy/suse16/profiles/server/initprofile.sh @@ -7,7 +7,7 @@ if [ "$discnum" != 1 ]; then exit 0; fi if [ -e $2/boot/kernel ]; then exit 0; fi profile=$(basename $2) -sed -i 's/sle 15/SUSE Linux Enterprise 15/; s/opensuse_leap/openSUSE Leap/' $2/profile.yaml +sed -i 's/sle 16/SUSE Linux Enterprise 16/; s/opensuse_leap/openSUSE Leap/' $2/profile.yaml ln -s $1/boot/x86_64/loader/linux $2/boot/kernel && \ ln -s $1/boot/x86_64/loader/initrd $2/boot/initramfs/distribution && \ mkdir -p $2/boot/efi/boot && \ diff --git a/confluent_osdeploy/suse16/profiles/server/profile.yaml b/confluent_osdeploy/suse16/profiles/server/profile.yaml new file mode 100644 index 00000000..a1d9e244 --- /dev/null +++ b/confluent_osdeploy/suse16/profiles/server/profile.yaml @@ -0,0 +1,3 @@ +label: %%DISTRO%% %%VERSION%% %%ARCH%% (Server Profile) +kernelargs: quiet # These arguments are passed to the installer +#installedargs: example # These arguments would be added to the installed system From 398211a6ed87e597b8535f110920f9c81f4964dd Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Fri, 4 Sep 2026 22:00:50 +0200 Subject: [PATCH 02/26] Support diskless boot on SUSE 16 Ported from suse15-diskless, with the differences 16 forces: - dracut symlinks /lib/dracut/hooks to /var/lib/dracut/hooks, so a hook shipped at the old path replaces the symlink with a directory - there is no netconfig or /etc/sysconfig/network to hand the running address to, so the initramfs writes a NetworkManager keyfile instead. Without it NetworkManager claims the interface on its own terms and the tethered root filesystem goes away with the old address, and confignet never gets the chance to refine anything - the discovery loop retries without a delay, so a link that takes a moment to come up can exhaust all 30 tries before the first packet can go anywhere. Keep asking, as the el9 hook already does --- .../confluent_osdeploy.spec.tmpl | 2 +- .../hooks/cmdline/10-confluentdiskless.sh | 273 ++++++++++++++++++ .../profiles/default/scripts/imageboot.sh | 134 +++++++++ .../profiles/default/scripts/onboot.custom | 0 .../default/scripts/onboot.d/.gitignore | 0 .../profiles/default/scripts/onboot.service | 11 + .../profiles/default/scripts/onboot.sh | 33 +++ .../profiles/default/syncfiles | 29 ++ 8 files changed, 481 insertions(+), 1 deletion(-) create mode 100644 confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh create mode 100644 confluent_osdeploy/suse16-diskless/profiles/default/scripts/imageboot.sh create mode 100644 confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.custom create mode 100644 confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.d/.gitignore create mode 100644 confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.service create mode 100644 confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.sh create mode 100644 confluent_osdeploy/suse16-diskless/profiles/default/syncfiles diff --git a/confluent_osdeploy/confluent_osdeploy.spec.tmpl b/confluent_osdeploy/confluent_osdeploy.spec.tmpl index e629f8d3..d69afa04 100644 --- a/confluent_osdeploy/confluent_osdeploy.spec.tmpl +++ b/confluent_osdeploy/confluent_osdeploy.spec.tmpl @@ -49,7 +49,7 @@ for os in rhvh4 el7 genesis el8 suse15 suse16 debian debian13 ubuntu18.04 ubuntu mv ../addons.cpio . cd .. done -for os in el7 el8 suse15 el9 el10 ubuntu20.04 ubuntu22.04 ubuntu24.04 ubuntu26.04; do +for os in el7 el8 suse15 suse16 el9 el10 ubuntu20.04 ubuntu22.04 ubuntu24.04 ubuntu26.04; do mkdir ${os}disklessout cd ${os}disklessout if [ -d ../${os}bin ]; then diff --git a/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh b/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh new file mode 100644 index 00000000..28b1af82 --- /dev/null +++ b/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh @@ -0,0 +1,273 @@ +pcrextendvalue=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2 +pcrhashalgo=sha256 + +set_tpm_hashalgo() { + if [ -n "$confluent_tpm_hashalgo" ]; then + pcrhashalgo="$confluent_tpm_hashalgo" + return 0 + fi + tpm_pcrbanks=$(tpm2_getcap pcrs 2>/dev/null) + for algo in sha256 sha512 sha384; do + # Match only banks that actually have PCRs allocated (a digit in [ ... ]). + if echo "$tpm_pcrbanks" | grep -Eq "$algo:[[:space:]]*\[[^]]*[0-9]"; then + confluent_tpm_hashalgo="$algo" + pcrhashalgo="$algo" + if [[ "$algo" == "sha256" ]]; then + pcrextendvalue=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2 + elif [[ "$algo" == "sha384" ]]; then + pcrextendvalue=20aaa1073c215c8bc97ff8dc509dd63ff09eef9d2dfa4d8ef224ce372d80e5417e53840ef2fb72924c195f69396a27b9 + elif [[ "$algo" == "sha512" ]]; then + pcrextendvalue=77113aa32ac249789c0cdcf24e78efdb81d5be0d878e9a9a750446ecf9b9b3b1c084194eb6187cfd890b8f61a7f2e79eb4d33f6f27827b862367897c8123bceb + fi + return 0 + fi + done + return 1 +} + +get_remote_apikey() { + while [ -z "$confluent_apikey" ]; do + /opt/confluent/bin/clortho $nodename $confluent_mgr > /etc/confluent/confluent.apikey + if grep ^SEALED: /etc/confluent/confluent.apikey > /dev/null; then + # we don't support remote sealed api keys + echo > /etc/confluent/confluent.apikey + fi + confluent_apikey=$(cat /etc/confluent/confluent.apikey) + if [ -z "$confluent_apikey" ]; then + echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$nodename', retrying..." + sleep 10 + else + tmpdir=$(mktemp -d) + cd $tmpdir + set_tpm_hashalgo + tpm2_startauthsession --session=session.ctx + tpm2_policypcr -Q --session=session.ctx --pcr-list="${pcrhashalgo}:15" --policy=pcr15.${pcrhashalgo}.policy + tpm2_createprimary -G ecc -Q --key-context=prim.ctx + (echo -n "CONFLUENT_APIKEY:";cat /etc/confluent/confluent.apikey) | tpm2_create -Q --policy=pcr15.${pcrhashalgo}.policy --public=data.pub --private=data.priv -i - -C prim.ctx + tpm2_load -Q --parent-context=prim.ctx --public=data.pub --private=data.priv --name=confluent.apikey --key-context=data.ctx + tpm2_evictcontrol -Q -c data.ctx + tpm2_flushcontext session.ctx + cd - > /dev/null + rm -rf $tmpdir + fi + done +} + +root=1 +rootok=1 +netroot=confluent +clear +mkdir -p /etc/ssh +mkdir -p /var/tmp/ +mkdir -p /var/lib/empty +mkdir -p /var/empty/sshd +mkdir -p /etc/confluent +sed -i '/^root:x/d' /etc/passwd +echo root:x:0:0::/:/bin/bash >> /etc/passwd +echo sshd:x:30:30:SSH User:/var/empty/sshd:/sbin/nologin >> /etc/passwd + +if ! grep console= /proc/cmdline >& /dev/null; then + autocons=$(/opt/confluent/bin/autocons) + autocons=${autocons##*/} + if [ ! -z "$autocons" ]; then + echo "Automatic console configured for $autocons" + fi +fi +echo "Initializing confluent diskless environment" +echo -n "udevd: " +/usr/lib/systemd/systemd-udevd --daemon +echo -n "Loading drivers..." +udevadm trigger +udevadm trigger --type=devices --action=add +udevadm settle +modprobe ib_ipoib +modprobe ib_umad +modprobe hfi1 +modprobe mlx5_ib +echo "done" +cat > /etc/ssh/sshd_config << EOF +Port 2222 +Subsystem sftp /usr/libexec/openssh/sftp-server +PermitRootLogin yes +AuthorizedKeysFile .ssh/authorized_keys +EOF +mkdir /root/.ssh +mkdir /.ssh +cat /ssh/*pubkey > /root/.ssh/authorized_keys 2>/dev/null +cp /root/.ssh/authorized_keys /.ssh/ +cat /tls/*.pem > /etc/confluent/ca.pem +mkdir -p /var/lib/ca-certificates/ +cat /tls/*.pem > /var/lib/ca-certificates/ca-bundle.pem +TRIES=0 +oldumask=$(umask) +umask 0077 +tpmdir=$(mktemp -d) +cd $tpmdir +lasthdl="" +for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do + tpm2_startauthsession --policy-session --session=session.ctx + set_tpm_hashalgo + tpm2_policypcr -Q --session=session.ctx --pcr-list="${pcrhashalgo}:15" --policy=pcr15.${pcrhashalgo}.policy + unsealeddata=$(tpm2_unseal --auth=session:session.ctx -Q -c $hdl 2>/dev/null) + tpm2_flushcontext session.ctx + if [[ $unsealeddata == "CONFLUENT_APIKEY:"* ]]; then + confluent_apikey=${unsealeddata#CONFLUENT_APIKEY:} + echo $confluent_apikey > /etc/confluent/confluent.apikey + if [ -n "$lasthdl" ]; then + tpm2_evictcontrol -c $lasthdl + fi + lasthdl=$hdl + fi +done +cd - > /dev/null +rm -rf $tpmdir +touch /etc/confluent/confluent.info +cd /sys/class/net +echo -n "Scanning for network configuration..." +while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 >& /dev/null && [ "$TRIES" -lt 30 ]; do + TRIES=$((TRIES + 1)) + for i in *; do + ip link set $i up + done + /opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info + echo -n . +done +# The loop above has no delay, so on a link that takes a moment to come up it +# can burn all 30 tries before the first packet can go anywhere. Keep asking. +TRIES=0 +while ! grep ^NODENAME: /etc/confluent/confluent.info >& /dev/null && [ "$TRIES" -lt 300 ]; do + sleep 0.5 + echo -n . + /opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info + TRIES=$((TRIES + 1)) +done +cd / +nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}') +hostname $nodename +confluent_mgr=$(grep '^EXTMGRINFO:.*1$' /etc/confluent/confluent.info | head -n 1|awk -F': ' '{print $2}' | awk -F'|' '{print $1}') +if [ -z "$confluent_mgr" ]; then + confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info|head -n 1 | awk '{print $2}') +fi +if [[ $confluent_mgr == *%* ]]; then + echo $confluent_mgr | awk -F% '{print $2}' > /tmp/confluent.ifidx + ifidx=$(cat /tmp/confluent.ifidx) + ifname=$(ip link |grep ^$ifidx:|awk '{print $2}') + ifname=${ifname%:} +fi + +ready=0 +while [ $ready = "0" ]; do + get_remote_apikey + if [[ $confluent_mgr == *:* ]] && [[ $confluent_mgr != "["* ]]; then + confluent_mgr="[$confluent_mgr]" + fi + tmperr=$(mktemp) + curl -sSf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg 2> $tmperr + if grep 401 $tmperr > /dev/null; then + confluent_apikey="" + if [ -n "$lasthdl" ]; then + tpm2_evictcontrol -c $lasthdl + fi + confluent_mgr=${confluent_mgr#[} + confluent_mgr=${confluent_mgr%]} + else + ready=1 + fi + rm $tmperr +done +if [ ! -z "$autocons" ] && grep textconsole: true /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi +set_tpm_hashalgo +tpm2_pcrextend 15:${pcrhashalgo}=${pcrextendvalue} +umask $oldumask +# The root filesystem is served over this interface, so NetworkManager must +# adopt the address the initramfs set rather than reconfigure the link out from +# under it. Leave a keyfile for imageboot.sh to place in the image. 15 handed +# wicked an ifcfg file for the same reason. +nameservers="" +nameserversec=0 +while read -r entry; do + if [ $nameserversec = 1 ]; then + if [[ $entry == "-"* ]] && [[ $entry != "- ''" ]]; then + nameservers="$nameservers"${entry#- }";" + continue + fi + fi + nameserversec=0 + if [ "${entry%:*}" = "nameservers" ]; then + nameserversec=1 + continue + fi +done < /etc/confluent/confluent.deploycfg +dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg) +dnsdomain=${dnsdomain#dnsdomain: } +if [ "$dnsdomain" = "null" ]; then + dnsdomain="" +fi +mkdir -p /run/confluent +nmcfg=/run/confluent/$ifname.nmconnection +autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg |awk '{print $2}') +if [ "$autoconfigmethod" = "dhcp" ]; then + echo -n "Attempting to use dhcp to bring up $ifname..." + dhclient $ifname + echo "Complete:" + ip addr show dev $ifname + printf '[connection]\nid=%s\ntype=ethernet\ninterface-name=%s\nautoconnect=true\n\n[ipv4]\nmethod=auto\n' "$ifname" "$ifname" > $nmcfg + printf '\n[ipv6]\nmethod=link-local\n' >> $nmcfg + chmod 600 $nmcfg +else + v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg) + v4addr=${v4addr#ipv4_address: } + v4gw=$(grep ^ipv4_gateway: /etc/confluent/confluent.deploycfg) + v4gw=${v4gw#ipv4_gateway: } + if [ "$v4gw" = "null" ]; then + v4gw="" + fi + v4nm=$(grep ^prefix: /etc/confluent/confluent.deploycfg) + v4nm=${v4nm#prefix: } + echo "Setting up $ifname as static at $v4addr/$v4nm" + ip addr add dev $ifname $v4addr/$v4nm + if [ ! -z "$v4gw" ]; then + ip route add default via $v4gw + fi + printf '[connection]\nid=%s\ntype=ethernet\ninterface-name=%s\nautoconnect=true\n\n[ipv4]\nmethod=manual\naddress1=%s/%s' "$ifname" "$ifname" "$v4addr" "$v4nm" > $nmcfg + if [ ! -z "$v4gw" ]; then + printf ',%s' "$v4gw" >> $nmcfg + fi + printf '\n' >> $nmcfg + if [ ! -z "$nameservers" ]; then + printf 'dns=%s\n' "$nameservers" >> $nmcfg + fi + if [ ! -z "$dnsdomain" ]; then + printf 'dns-search=%s\n' "$dnsdomain" >> $nmcfg + fi + printf '\n[ipv6]\nmethod=link-local\n' >> $nmcfg + chmod 600 $nmcfg +fi + +echo -n "Initializing ssh..." +ssh-keygen -A +for pubkey in /etc/ssh/ssh_host*key.pub; do + certfile=${pubkey/.pub/-cert.pub} + privfile=${pubkey%.pub} + curl -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -d @$pubkey https://$confluent_mgr/confluent-api/self/sshcert > $certfile + if [ -s $certfile ]; then + echo HostCertificate $certfile >> /etc/ssh/sshd_config + fi + echo HostKey $privfile >> /etc/ssh/sshd_config +done +/usr/sbin/sshd +confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg| awk '{print $2}') +confluent_proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg| awk '{print $2}') +confluent_urls="" +for addr in $(grep ^MANAGER: /etc/confluent/confluent.info|awk '{print $2}'|sed -e s/%/%25/); do + if [[ $addr == *:* ]]; then + confluent_urls="$confluent_urls $confluent_proto://[$addr]/confluent-public/os/$confluent_profile/rootimg.sfs" + else + confluent_urls="$confluent_urls $confluent_proto://$addr/confluent-public/os/$confluent_profile/rootimg.sfs" + fi +done +confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}') +mkdir -p /etc/confluent +curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions +. /etc/confluent/functions +source_remote imageboot.sh diff --git a/confluent_osdeploy/suse16-diskless/profiles/default/scripts/imageboot.sh b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/imageboot.sh new file mode 100644 index 00000000..876ecef9 --- /dev/null +++ b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/imageboot.sh @@ -0,0 +1,134 @@ +. /lib/dracut-lib.sh +mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay +if [ "untethered" = "$(getarg confluent_imagemethod)" ]; then + mount -t tmpfs untethered /mnt/remoteimg + curl https://$confluent_mgr/confluent-public/os/$confluent_profile/rootimg.sfs -o /mnt/remoteimg/rootimg.sfs +else + confluent_urls="$confluent_urls https://$confluent_mgr/confluent-public/os/$confluent_profile/rootimg.sfs" + /opt/confluent/bin/urlmount $confluent_urls /mnt/remoteimg +fi +/opt/confluent/bin/confluent_imginfo /mnt/remoteimg/rootimg.sfs > /tmp/rootimg.info +loopdev=$(losetup -f) +export mountsrc=$loopdev +losetup -r $loopdev /mnt/remoteimg/rootimg.sfs +if grep '^Format: confluent_crypted' /tmp/rootimg.info > /dev/null; then + curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/profileprivate/pending/rootimg.key > /tmp/rootimg.key + cipher=$(head -n 1 /tmp/rootimg.key) + key=$(tail -n 1 /tmp/rootimg.key) + len=$(wc -c /mnt/remoteimg/rootimg.sfs | awk '{print $1}') + len=$(((len-4096)/512)) + dmsetup create cryptimg --table "0 $len crypt $cipher $key 0 $loopdev 8" + /opt/confluent/bin/confluent_imginfo /dev/mapper/cryptimg > /tmp/rootimg.info + mountsrc=/dev/mapper/cryptimg +fi +if grep '^Format: squashfs' /tmp/rootimg.info > /dev/null; then + mount -o ro $mountsrc /mnt/remote +elif grep '^Format: confluent_multisquash' /tmp/rootimg.info; then + tail -n +3 /tmp/rootimg.info | awk '{gsub("/", "_"); print "echo 0 " $4 " linear '$mountsrc' " $3 " | dmsetup create mproot" $7}' > /tmp/setupmount.sh + . /tmp/setupmount.sh + cat /tmp/setupmount.sh |awk '{printf "mount /dev/mapper/"$NF" "; sub("mproot", ""); gsub("_", "/"); print "/mnt/remote"$NF}' > /tmp/mountparts.sh + . /tmp/mountparts.sh +fi + +#mount -t tmpfs overlay /mnt/overlay +modprobe zram +memtot=$(grep ^MemTotal: /proc/meminfo|awk '{print $2}') +memtot=$((memtot/2))$(grep ^MemTotal: /proc/meminfo | awk '{print $3'}) +echo $memtot > /sys/block/zram0/disksize +mkfs.xfs /dev/zram0 > /dev/null +mount -o discard /dev/zram0 /mnt/overlay +if [ ! -f /tmp/mountparts.sh ]; then + mkdir -p /mnt/overlay/upper /mnt/overlay/work + mount -t overlay -o upperdir=/mnt/overlay/upper,workdir=/mnt/overlay/work,lowerdir=/mnt/remote disklessroot /sysroot +else + for srcmount in $(cat /tmp/mountparts.sh | awk '{print $3}'); do + mkdir -p /mnt/overlay${srcmount}/upper /mnt/overlay${srcmount}/work + mount -t overlay -o upperdir=/mnt/overlay${srcmount}/upper,workdir=/mnt/overlay${srcmount}/work,lowerdir=${srcmount} disklesspart /sysroot${srcmount#/mnt/remote} + done +fi +mkdir -p /sysroot/etc/ssh +mkdir -p /sysroot/etc/confluent +mkdir -p /sysroot/root/.ssh +cp /root/.ssh/* /sysroot/root/.ssh +chmod 700 /sysroot/root/.ssh +cp /etc/confluent/* /sysroot/etc/confluent/ +cp /etc/ssh/*key* /sysroot/etc/ssh/ +for pubkey in /etc/ssh/ssh_host*key.pub; do + certfile=${pubkey/.pub/-cert.pub} + privfile=${pubkey%.pub} + if [ -s $certfile ]; then + echo HostCertificate $certfile >> /sysroot/etc/ssh/sshd_config + fi + echo HostKey $privfile >> /sysroot/etc/ssh/sshd_config +done + +mkdir -p /sysroot/dev /sysroot/sys /sysroot/proc /sysroot/run +if [ ! -z "$autocons" ]; then + autocons=${autocons%,*} + mkdir -p /run/systemd/generator/getty.target.wants + ln -s /usr/lib/systemd/system/serial-getty@.service /run/systemd/generator/getty.target.wants/serial-getty@${autocons}.service +fi +while [ ! -e /sysroot/sbin/init ]; do + echo "Failed to access root filesystem or it is missing /sbin/init" + echo "System should be accessible through ssh at port 2222 with the appropriate key" + while [ ! -e /sysroot/sbin/init ]; do + sleep 1 + done +done +rootpassword=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg) +rootpassword=${rootpassword#rootpassword: } +if [ "$rootpassword" = "null" ]; then + rootpassword="" +fi + +if [ ! -z "$rootpassword" ]; then + sed -i "s@root:[^:]*:@root:$rootpassword:@" /sysroot/etc/shadow +fi +for i in /ssh/*.ca; do + echo '@cert-authority *' $(cat $i) >> /sysroot/etc/ssh/ssh_known_hosts +done +echo HostbasedAuthentication yes >> /sysroot/etc/ssh/sshd_config +echo HostbasedUsesNameFromPacketOnly yes >> /sysroot/etc/ssh/sshd_config +echo IgnoreRhosts no >> /sysroot/etc/ssh/sshd_config +sshconf=/sysroot/etc/ssh/ssh_config +if [ -d /sysroot/etc/ssh/ssh_config.d/ ]; then + sshconf=/sysroot/etc/ssh/ssh_config.d/01-confluent.conf +fi +echo 'Host *' >> $sshconf +echo ' HostbasedAuthentication yes' >> $sshconf +echo ' EnableSSHKeysign yes' >> $sshconf +echo ' HostbasedKeyTypes *ed25519*' >> $sshconf +curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/nodelist > /sysroot/etc/ssh/shosts.equiv +cp /sysroot/etc/ssh/shosts.equiv /sysroot/root/.shosts +chmod 600 /sysroot/etc/ssh/*_key +chroot /sysroot cat /etc/confluent/ca.pem >> /sysroot/var/lib/ca-certificates/ca-bundle.pem +curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.service > /sysroot/etc/systemd/system/onboot.service +mkdir -p /sysroot/opt/confluent/bin +curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.sh > /sysroot/opt/confluent/bin/onboot.sh +chmod +x /sysroot/opt/confluent/bin/onboot.sh +cp /opt/confluent/bin/apiclient /sysroot/opt/confluent/bin +ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service +cp /etc/confluent/functions /sysroot/etc/confluent/functions + +# Hand NetworkManager the connection the initramfs is already using. Without +# this it claims the interface on its own terms and the tethered root +# filesystem goes away with the old address. confignet refines it at onboot. +mkdir -p /sysroot/etc/NetworkManager/system-connections +for nmcfg in /run/confluent/*.nmconnection; do + [ -e "$nmcfg" ] || continue + cp $nmcfg /sysroot/etc/NetworkManager/system-connections/ + chmod 600 /sysroot/etc/NetworkManager/system-connections/$(basename $nmcfg) +done +if grep installtodisk /proc/cmdline > /dev/null; then + . /etc/confluent/functions + run_remote installimage + exec reboot -f +fi +mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs +ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/ +if [ -d /lib/firmware ]; then + mv /lib/firmware /lib/firmware-ramfs +fi +ln -s /sysroot/lib/firmware /lib/firmware +chroot /sysroot chkstat --system --set --noheader > /dev/null +exec /opt/confluent/bin/start_root diff --git a/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.custom b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.custom new file mode 100644 index 00000000..e69de29b diff --git a/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.d/.gitignore b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.d/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.service b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.service new file mode 100644 index 00000000..f9235033 --- /dev/null +++ b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.service @@ -0,0 +1,11 @@ +[Unit] +Description=Confluent onboot hook +Requires=network-online.target +After=network-online.target + +[Service] +ExecStart=/opt/confluent/bin/onboot.sh + +[Install] +WantedBy=multi-user.target + diff --git a/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.sh b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.sh new file mode 100644 index 00000000..3b434ae0 --- /dev/null +++ b/confluent_osdeploy/suse16-diskless/profiles/default/scripts/onboot.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# This script is executed on each boot as it is +# completed. It is best to edit the middle of the file as +# noted below so custom commands are executed before +# the script notifies confluent that install is fully complete. + +nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}') +confluent_apikey=$(cat /etc/confluent/confluent.apikey) +confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}') +confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}') +timedatectl set-timezone $(grep ^timezone: /etc/confluent/confluent.deploycfg|awk '{print $2}') +export nodename confluent_mgr confluent_profile +. /etc/confluent/functions +mkdir -p /var/log/confluent +chmod 700 /var/log/confluent +exec >> /var/log/confluent/confluent-onboot.log +exec 2>> /var/log/confluent/confluent-onboot.log +chmod 600 /var/log/confluent/confluent-onboot.log +tail -f /var/log/confluent/confluent-onboot.log > /dev/console & +logshowpid=$! + +run_remote_python syncfileclient +run_remote_python confignet +run_remote onboot.custom +# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh +run_remote_parts onboot.d + +# Induce execution of remote configuration, e.g. ansible plays in ansible/onboot.d/ +run_remote_config onboot.d + +printf 'state: booted\nstatus: booted' | curl -X POST --data-binary @- -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus +kill $logshowpid diff --git a/confluent_osdeploy/suse16-diskless/profiles/default/syncfiles b/confluent_osdeploy/suse16-diskless/profiles/default/syncfiles new file mode 100644 index 00000000..4ae543f0 --- /dev/null +++ b/confluent_osdeploy/suse16-diskless/profiles/default/syncfiles @@ -0,0 +1,29 @@ +# It is advised to avoid /var/lib/confluent/public as a source for syncing. /var/lib/confluent/public +# is served without authentication and thus any sensitive content would be a risk. If wanting to host +# syncfiles on a common share, it is suggested to have /var/lib/confluent be the share and use some other +# subdirectory other than public. +# +# Syncing is performed as the 'confluent' user, so all source files must be accessible by the confluent user. +# +# This file lists files to synchronize or merge to the deployed systems from the deployment server +# To specify taking /some/path/hosts on the deployment server and duplicating it to /etc/hosts: +# Note particularly the use of '->' to delineate source from target. +# /some/path/hosts -> /etc/hosts + +# If wanting to simply use the same path for source and destinaiton, the -> may be skipped: +# /etc/hosts + +# More function is available, for example to limit the entry to run only on n1 through n8, and to set +# owner, group, and permissions in octal notation: +# /example/source -> n1-n8:/etc/target (owner=root,group=root,permissions=600) + +# Entries under APPENDONCE: will be added to specified target, only if the target does not already +# contain the data in the source already in its entirety. This allows append in a fashion that +# is friendly to being run repeatedly + +# Entries under MERGE: will attempt to be intelligently merged. This supports /etc/group and /etc/passwd +# Any supporting entries in /etc/shadow or /etc/gshadow are added automatically, with password disabled +# It also will not inject 'system' ids (under 1,000 usually) as those tend to be local and rpm managed. +# MERGE: +# /etc/passwd +# /etc/group From 5645fb5cfdeae8ab0df042d1fd7d7b523123be13 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Fri, 4 Sep 2026 22:01:01 +0200 Subject: [PATCH 03/26] Build SUSE 16 images with imgutil SuseHandler refused anything but 15.x. What 16 needed beyond widening it: - its repo urls are written in terms of ${releasever}, which zypper resolves from the target root's os-release, a file that does not exist yet when the first packages go in - its repos name a zypper service backed by a package-provided directory the target root does not have, so zypper discarded every one of them as an orphan - there is no mkinitrd to work out which kernel to build for, and bare dracut would build for the build host's running kernel - the efi payloads moved out of /usr/lib64/efi, arping out of /usr/sbin, nsswitch.conf and protocols under /usr/etc, and the presets enable sshd already - the module list predated virtio, so an image built for a KVM guest had no network at all, and dm-crypt could not allocate a transform for the encrypted image without the aes-xts modules - urlmount still links libpthread, an empty stub since glibc 2.34 that nothing else in the initramfs pulls in --- imgutil/confluent_imgutil.spec.tmpl | 2 +- imgutil/imgutil | 66 ++++++++++++++++++++++++----- imgutil/suse16/dracut/install | 39 +++++++++++++++++ imgutil/suse16/dracut/installkernel | 18 ++++++++ imgutil/suse16/pkglist | 22 ++++++++++ 5 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 imgutil/suse16/dracut/install create mode 100644 imgutil/suse16/dracut/installkernel create mode 100644 imgutil/suse16/pkglist diff --git a/imgutil/confluent_imgutil.spec.tmpl b/imgutil/confluent_imgutil.spec.tmpl index 3d5f90ca..47ca7232 100644 --- a/imgutil/confluent_imgutil.spec.tmpl +++ b/imgutil/confluent_imgutil.spec.tmpl @@ -34,7 +34,7 @@ mkdir -p opt/confluent/lib/imgutil mkdir -p opt/confluent/bin mv imgutil opt/confluent/bin/ chmod a+x opt/confluent/bin/imgutil -mv ubuntu* suse15 el7 el9 el10 el8 opt/confluent/lib/imgutil/ +mv ubuntu* suse15 suse16 el7 el9 el10 el8 opt/confluent/lib/imgutil/ mkdir -p opt/confluent/share/licenses/confluent_imgutil cp LICENSE opt/confluent/share/licenses/confluent_imgutil diff --git a/imgutil/imgutil b/imgutil/imgutil index cfa5492d..c03992a7 100644 --- a/imgutil/imgutil +++ b/imgutil/imgutil @@ -588,9 +588,10 @@ class SuseHandler(OsHandler): def __init__(self, name, version, arch, args): if not isinstance(version, str): version = version.decode('utf8') - if not version.startswith('15.'): + major = version.split('.', 1)[0] + if major not in ('15', '16'): raise Exception('Unsupported Suse version {}'.format(version)) - self.oscategory = 'suse15' + self.oscategory = 'suse{0}'.format(major) super().__init__(name, version, arch, args) self.zyppargs = [] self.sources = [] @@ -610,6 +611,15 @@ class SuseHandler(OsHandler): if enterprise: self.sources.append('file://' + os.path.join(sourcepath, 'Product-HPC')) + def zypper_cmd(self): + # 16's stock repo urls are written in terms of ${releasever}, which + # zypper resolves from the target root's os-release. That file does not + # exist yet when the first packages are installed, so say it outright. + cmd = ['zypper', '-R', self.targpath] + if self.version: + cmd.extend(['--releasever', self.version]) + return cmd + def prep_root(self, args): gpgkeys = [] mkdirp(self.targpath) @@ -617,8 +627,19 @@ class SuseHandler(OsHandler): gpgkeys = glob.glob('/usr/lib/rpm/gnupg/keys/*.asc') targzypp = os.path.join(self.targpath, 'etc/zypp') mkdirp(targzypp) - shutil.copytree( - '/etc/zypp/repos.d/', os.path.join(targzypp, 'repos.d')) + # 16 declares its repositories through a zypper service backed by + # a package-provided directory the target root does not have. Drop + # the service line so each definition stands on its own, otherwise + # zypper discards every one of them as an orphan. + targrepos = os.path.join(targzypp, 'repos.d') + mkdirp(targrepos) + for repofile in glob.glob('/etc/zypp/repos.d/*'): + with open(repofile) as repoin: + cfg = [line for line in repoin.read().splitlines() + if not line.startswith('service=')] + with open(os.path.join( + targrepos, os.path.basename(repofile)), 'w') as repoout: + repoout.write('\n'.join(cfg) + '\n') idx = 1 for source in self.sources: if not source: @@ -626,7 +647,7 @@ class SuseHandler(OsHandler): if source.startswith('file://'): gpgpath = source.replace('file://', '') gpgkeys.extend(glob.glob(os.path.join(gpgpath, '*/gpg-pubkey*.asc'))) - subprocess.check_call(['zypper', '-R', self.targpath, 'ar', source, 'source-{}'.format(idx)]) + subprocess.check_call(self.zypper_cmd() + ['ar', source, 'source-{}'.format(idx)]) idx += 1 if gpgkeys: addkeycmd = ['rpm', '--root', self.targpath, '--import'] + gpgkeys @@ -638,7 +659,7 @@ class SuseHandler(OsHandler): if not source.startswith('/') and os.path.exists(os.path.abspath(source)): source = os.path.abspath(source) source = 'file://' + source - subprocess.check_call(['zypper', '-R', self.targpath, 'ar', source, 'source-{}'.format(idx)]) + subprocess.check_call(self.zypper_cmd() + ['ar', source, 'source-{}'.format(idx)]) idx += 1 mydir = get_mydir(self.oscategory) mkdirp(os.path.join(self.targpath, 'usr/lib/dracut/modules.d')) @@ -652,10 +673,15 @@ class SuseHandler(OsHandler): cmd.extend(glob.glob(os.path.join(targdir, '*'))) subprocess.check_call(cmd) if self._interactive: - subprocess.check_call(['zypper', '-R', self.targpath, 'install'] + self.zyppargs) + subprocess.check_call(self.zypper_cmd() + ['install'] + self.zyppargs) else: - subprocess.check_call(['zypper', '-n', '-R', self.targpath, 'install'] + self.zyppargs) - os.symlink('/usr/lib/systemd/system/sshd.service', os.path.join(self.targpath, 'etc/systemd/system/multi-user.target.wants/sshd.service')) + subprocess.check_call(self.zypper_cmd() + ['-n', 'install'] + self.zyppargs) + # 16's presets enable sshd already, so the link is often there + sshdwant = os.path.join( + self.targpath, 'etc/systemd/system/multi-user.target.wants/sshd.service') + mkdirp(os.path.dirname(sshdwant)) + if not os.path.lexists(sshdwant): + os.symlink('/usr/lib/systemd/system/sshd.service', sshdwant) with open(os.path.join(self.targpath, 'etc/permissions.local'), 'a') as permout: permout.write( '/usr/lib/ssh/ssh-keysign root:ssh_keys 2711\n' @@ -675,7 +701,12 @@ class SuseHandler(OsHandler): if os.path.exists(os.path.join(self.targpath, 'sbin/mkinitrd')): args.cmd = ['mkinitrd'] else: - args.cmd = ['dracut', '-f'] + # dracut alone would build for the running kernel, which is the + # build host's, not the one just installed into the image. 16 has + # no mkinitrd to work that out, so name the kernel and the output. + kver = sorted(os.listdir(os.path.join(self.targpath, 'lib/modules')), + key=versionize_string)[-1] + args.cmd = ['dracut', '-f', '/boot/initrd-{0}'.format(kver), kver] run_constrainedx(fancy_chroot, (args, self.targpath)) @@ -1297,7 +1328,7 @@ def fingerprint_source_suse(files, sourcepath, args): if ': ' not in line: continue key, val = line.split(': ') - if key == 'category' and val == 'suse15': + if key == 'category' and val in ('suse15', 'suse16'): issuse = True if key == 'name': osname, ver, arch = val.split('-') @@ -1739,6 +1770,15 @@ def gather_bootloader(outdir, rootpath='/'): shimlocation = os.path.join(rootpath, 'usr/lib/shim/shimx64.efi.signed') if not os.path.exists(shimlocation): shimlocation = os.path.join(rootpath, 'usr/lib/shim/shimaa64.efi.signed.latest') + if not os.path.exists(shimlocation): + # SUSE 16 moved the efi payloads out of /usr/lib64/efi + shimlocation = os.path.join(rootpath, 'usr/share/efi/x86_64/shim.efi') + shimdestfilename = 'BOOTX64.EFI' + if not os.path.exists(shimlocation): + aa64shim = os.path.join(rootpath, 'usr/share/efi/aarch64/shim.efi') + if os.path.exists(aa64shim): + shimlocation = aa64shim + shimdestfilename = 'BOOTAA64.EFI' mkdirp(os.path.join(outdir, 'boot/efi/boot')) shutil.copyfile(shimlocation, os.path.join(outdir, 'boot/efi/boot/{0}'.format(shimdestfilename))) for maybemokmanager in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*/mmx64.efi')): @@ -1747,6 +1787,8 @@ def gather_bootloader(outdir, rootpath='/'): else: if os.path.exists(os.path.join(rootpath, 'usr/lib/shim/mmx64.efi')): shutil.copyfile(os.path.join(rootpath, 'usr/lib/shim/mmx64.efi'), os.path.join(outdir, 'boot/efi/boot/mmx64.efi')) + elif os.path.exists(os.path.join(rootpath, 'usr/share/efi/x86_64/MokManager.efi')): + shutil.copyfile(os.path.join(rootpath, 'usr/share/efi/x86_64/MokManager.efi'), os.path.join(outdir, 'boot/efi/boot/mmx64.efi')) for maybemokmanager in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*/mmaa64.efi')): shutil.copyfile(maybemokmanager, os.path.join(outdir, 'boot/efi/boot/mmaa64.efi')) break @@ -1769,6 +1811,8 @@ def gather_bootloader(outdir, rootpath='/'): grubbin = os.path.join(rootpath, 'usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed') if not os.path.exists(grubbin): grubbin = os.path.join(rootpath, 'usr/lib/grub/arm64-efi/monolithic/grubaa64.efi') + if not os.path.exists(grubbin): + grubbin = os.path.join(rootpath, 'usr/share/efi/x86_64/grub.efi') if not os.path.exists(grubbin): grubs = os.path.join(rootpath, 'boot/efi/EFI/*/grubx64.efi') grubs = glob.glob(grubs) diff --git a/imgutil/suse16/dracut/install b/imgutil/suse16/dracut/install new file mode 100644 index 00000000..67819080 --- /dev/null +++ b/imgutil/suse16/dracut/install @@ -0,0 +1,39 @@ +dracut_install mktemp +dracut_install /usr/lib64/libtss2-tcti-device.so.* +dracut_install tpm2_create tpm2_pcrread tpm2_createpolicy tpm2_createprimary +dracut_install tpm2_load tpm2_unseal tpm2_getcap tpm2_evictcontrol +dracut_install tpm2_pcrextend tpm2_policypcr tpm2_flushcontext tpm2_startauthsession +dracut_install curl openssl tar cpio gzip lsmod ethtool xz lsmod ethtool +dracut_install modprobe touch echo cut wc bash uniq grep ip hostname +dracut_install awk egrep dirname expr sort +dracut_install ssh sshd reboot parted mkfs mkfs.ext4 mkfs.xfs xfs_db mkswap +dracut_install efibootmgr uuidgen +dracut_install du df ssh-keygen scp clear dhclient +dracut_install /lib64/libnss_dns.so.2 # glibc dropped the versioned name +dracut_install /lib64/libnss_compat* +dracut_install /usr/lib64/libnl-3.so.200 +# 16 ships the stock config defaults under /usr/etc; only services stayed put +dracut_install /usr/etc/nsswitch.conf /etc/services /usr/etc/protocols +dracut_install chmod whoami head tail basename tr +dracut_install arping logger hostnamectl # 16 moved arping to /usr/bin +inst /bin/bash /bin/sh +dracut_install /lib64/libfuse.so.2 # symlink, dracut brings the target along +# urlmount still links libpthread, which since glibc 2.34 is an empty stub that +# nothing else in the initramfs pulls in +dracut_install /lib64/libpthread.so.0 +dracut_install chown chroot dd expr kill parted rsync sort blockdev findfs insmod lvm +dracut_install /usr/lib/udev/rules.d/10-dm.rules /usr/sbin/dmsetup /usr/lib/udev/rules.d/95-dm-notify.rules +dracut_install /usr/lib/systemd/network/99-default.link +dracut_install losetup # multipart support + +#this would be nfs with lock, but not needed, go nolock +#dracut_install mount.nfs rpcbind rpc.statd /etc/netconfig sm-notify +#dracut_install mount.nfs /etc/netconfig +inst /usr/lib/dracut/modules.d/40network/net-lib.sh /lib/net-lib.sh + + + +# network mount, and disk imaging helpers can come from a second stage +# this is narrowly focused on getting network up and fetching images +# and those images may opt to do something with cloning or whatever + diff --git a/imgutil/suse16/dracut/installkernel b/imgutil/suse16/dracut/installkernel new file mode 100644 index 00000000..46f2c335 --- /dev/null +++ b/imgutil/suse16/dracut/installkernel @@ -0,0 +1,18 @@ +#!/bin/bash +instmods nfsv3 nfs_acl nfsv4 dns_resolver lockd fscache sunrpc +instmods e1000 e1000e igb sfc mlx5_ib mlx5_core mlx4_en cxgb3 cxgb4 tg3 bnx2 bnx2x bna ixgb ixgbe qlge mptsas mpt2sas mpt3sas megaraid_sas ahci xhci-hcd sd_mod pmcraid be2net vfat ext3 ext4 usb_storage scsi_wait_scan ipmi_si ipmi_devintf qlcnic xfs +instmods nvme +instmods cdc_ether r8152 +instmods r8169 +instmods vmxnet3 virtio_net +instmods virtio_scsi vmw_pvscsi +instmods xen-netfront xen-blkfront xen-pcifront +instmods mptctl +instmods mlx4_ib mlx5_ub ib_umad ib_ipoib +instmods ice i40e hfi1 bnxt_en qed qede +instmods dm-mod dm-log raid0 raid1 raid10 raid456 dm-raid dm-thin-pool dm-crypt dm-snapshot linear dm-era +# imgutil packs encrypted by default and dm-crypt cannot allocate the transform +# without these; the stock dracut crypt module is not in the diskless image +instmods aes_generic aesni_intel xts crypto_simd +# nfs root and optionally gocryptfs +instmods fuse overlay squashfs loop zram diff --git a/imgutil/suse16/pkglist b/imgutil/suse16/pkglist new file mode 100644 index 00000000..7f7e582a --- /dev/null +++ b/imgutil/suse16/pkglist @@ -0,0 +1,22 @@ +hostname +curl +irqbalance +less +sudo +tuned +xfsprogs +e2fsprogs +chrony net-tools rsync tar util-linux python3 tar dracut ethtool parted openssl bash rpm iputils lvm2 efibootmgr attr +dhcp-client +zypper +grub2-x86_64-efi +libfuse2 +openssh +shim +kernel-default +tpm2.0-tools +# tpm2.0-tools only requires tctildr, and the diskless dracut module +# installs the device tcti explicitly +libtss2-tcti-device0 +NetworkManager # 16 dropped wicked; confignet writes nmconnections +vim From 9dd58026988d7314045fdea68b5562377074f441 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Sat, 5 Sep 2026 02:12:18 +0200 Subject: [PATCH 04/26] Fill in the spots the SUSE 16 work missed - the aarch64 osdeploy spec builds the stateful suse16 addons but its diskless loop was never extended, so the aarch64 rpm shipped suse16 without suse16-diskless and a packed image got a dangling addons.cpio. - imgutil's builddeb keeps its own copy of the directory list that confluent_imgutil.spec.tmpl has, and it had learned about neither suse16 nor el10. - gather_bootloader gained a /usr/share/efi fallback for shim on both architectures but only for x86_64 on grub, so an aarch64 root found a shim and then died copying grub. Finally, rewriting repos.d file by file rather than copying the tree meant a subdirectory or a file that is not valid UTF-8 aborted the build before any package was installed, which also regressed SUSE 15. Pass anything that is not a plain text repo definition through untouched and restore the modes on the ones that are rewritten. --- .../confluent_osdeploy-aarch64.spec.tmpl | 2 +- imgutil/builddeb | 2 +- imgutil/imgutil | 26 +++++++++++++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/confluent_osdeploy/confluent_osdeploy-aarch64.spec.tmpl b/confluent_osdeploy/confluent_osdeploy-aarch64.spec.tmpl index df07dd23..17f572cd 100644 --- a/confluent_osdeploy/confluent_osdeploy-aarch64.spec.tmpl +++ b/confluent_osdeploy/confluent_osdeploy-aarch64.spec.tmpl @@ -47,7 +47,7 @@ for os in rhvh4 el7 genesis el8 suse15 suse16 debian debian13 ubuntu20.04 ubuntu mv ../addons.cpio . cd .. done -for os in el7 el8 suse15 el9 el10 ubuntu20.04 ubuntu22.04 ubuntu24.04 ubuntu26.04; do +for os in el7 el8 suse15 suse16 el9 el10 ubuntu20.04 ubuntu22.04 ubuntu24.04 ubuntu26.04; do mkdir ${os}disklessout cd ${os}disklessout if [ -d ../${os}bin ]; then diff --git a/imgutil/builddeb b/imgutil/builddeb index 930b22ed..64da5a2d 100755 --- a/imgutil/builddeb +++ b/imgutil/builddeb @@ -13,7 +13,7 @@ mkdir -p deb/confluent_imgutil_$VERSION/opt/confluent/lib/imgutil mkdir -p deb/confluent_imgutil_$VERSION/opt/confluent/bin mv imgutil deb/confluent_imgutil_$VERSION/opt/confluent/bin/ chmod a+x deb/confluent_imgutil_$VERSION/opt/confluent/bin/imgutil -mv ubuntu* suse15 el7 el9 el8 deb/confluent_imgutil_$VERSION/opt/confluent/lib/imgutil/ +mv ubuntu* suse15 suse16 el7 el9 el10 el8 deb/confluent_imgutil_$VERSION/opt/confluent/lib/imgutil/ mkdir -p deb/confluent_imgutil_$VERSION/opt/confluent/share/licenses/confluent_imgutil cp LICENSE deb/confluent_imgutil_$VERSION/opt/confluent/share/licenses/confluent_imgutil sed -e 's/#VERSION#/'$VERSION/ control.tmpl > deb/confluent_imgutil_$VERSION/DEBIAN/control diff --git a/imgutil/imgutil b/imgutil/imgutil index c03992a7..7f0e0506 100644 --- a/imgutil/imgutil +++ b/imgutil/imgutil @@ -634,12 +634,22 @@ class SuseHandler(OsHandler): targrepos = os.path.join(targzypp, 'repos.d') mkdirp(targrepos) for repofile in glob.glob('/etc/zypp/repos.d/*'): - with open(repofile) as repoin: - cfg = [line for line in repoin.read().splitlines() - if not line.startswith('service=')] - with open(os.path.join( - targrepos, os.path.basename(repofile)), 'w') as repoout: + targrepo = os.path.join(targrepos, os.path.basename(repofile)) + # Anything that is not a plain text repo definition is passed + # through as-is rather than rewritten + if os.path.isdir(repofile): + shutil.copytree(repofile, targrepo) + continue + try: + with open(repofile) as repoin: + cfg = [line for line in repoin.read().splitlines() + if not line.startswith('service=')] + except (IsADirectoryError, UnicodeDecodeError): + shutil.copy2(repofile, targrepo) + continue + with open(targrepo, 'w') as repoout: repoout.write('\n'.join(cfg) + '\n') + shutil.copystat(repofile, targrepo) idx = 1 for source in self.sources: if not source: @@ -1813,6 +1823,12 @@ def gather_bootloader(outdir, rootpath='/'): grubbin = os.path.join(rootpath, 'usr/lib/grub/arm64-efi/monolithic/grubaa64.efi') if not os.path.exists(grubbin): grubbin = os.path.join(rootpath, 'usr/share/efi/x86_64/grub.efi') + if not os.path.exists(grubbin): + # same relocation as the shim above, on the other architecture + aa64grub = os.path.join(rootpath, 'usr/share/efi/aarch64/grub.efi') + if os.path.exists(aa64grub): + grubbin = aa64grub + grubdestfilename = 'grubaa64.efi' if not os.path.exists(grubbin): grubs = os.path.join(rootpath, 'boot/efi/EFI/*/grubx64.efi') grubs = glob.glob(grubs) From 939a46d8d10d7353a31be06f5521ce2645168cd7 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Sat, 5 Sep 2026 00:49:00 +0200 Subject: [PATCH 05/26] Fix SUSE 16 diskless boot --- .../hooks/cmdline/10-confluentdiskless.sh | 20 +++++++++++++------ imgutil/suse16/pkglist | 5 ++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh b/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh index 28b1af82..e4f9c0f3 100644 --- a/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh +++ b/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh @@ -205,15 +205,23 @@ if [ "$dnsdomain" = "null" ]; then fi mkdir -p /run/confluent nmcfg=/run/confluent/$ifname.nmconnection +linktype=$(ip link show dev "$ifname" | grep link/ | awk '{print $1}') +if [ "$linktype" = link/infiniband ]; then + linktype=infiniband +else + linktype=ethernet +fi +printf '[connection]\nid=%s\ntype=%s\ninterface-name=%s\nautoconnect=true\n' "$ifname" "$linktype" "$ifname" > $nmcfg +if [ "$linktype" = infiniband ]; then + printf '\n[infiniband]\ntransport-mode=datagram\n' >> $nmcfg +fi autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg |awk '{print $2}') if [ "$autoconfigmethod" = "dhcp" ]; then echo -n "Attempting to use dhcp to bring up $ifname..." dhclient $ifname echo "Complete:" ip addr show dev $ifname - printf '[connection]\nid=%s\ntype=ethernet\ninterface-name=%s\nautoconnect=true\n\n[ipv4]\nmethod=auto\n' "$ifname" "$ifname" > $nmcfg - printf '\n[ipv6]\nmethod=link-local\n' >> $nmcfg - chmod 600 $nmcfg + printf '\n[ipv4]\nmethod=auto\n' >> $nmcfg else v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg) v4addr=${v4addr#ipv4_address: } @@ -229,7 +237,7 @@ else if [ ! -z "$v4gw" ]; then ip route add default via $v4gw fi - printf '[connection]\nid=%s\ntype=ethernet\ninterface-name=%s\nautoconnect=true\n\n[ipv4]\nmethod=manual\naddress1=%s/%s' "$ifname" "$ifname" "$v4addr" "$v4nm" > $nmcfg + printf '\n[ipv4]\nmethod=manual\naddress1=%s/%s' "$v4addr" "$v4nm" >> $nmcfg if [ ! -z "$v4gw" ]; then printf ',%s' "$v4gw" >> $nmcfg fi @@ -240,9 +248,9 @@ else if [ ! -z "$dnsdomain" ]; then printf 'dns-search=%s\n' "$dnsdomain" >> $nmcfg fi - printf '\n[ipv6]\nmethod=link-local\n' >> $nmcfg - chmod 600 $nmcfg fi +printf '\n[ipv6]\nmethod=link-local\n' >> $nmcfg +chmod 600 $nmcfg echo -n "Initializing ssh..." ssh-keygen -A diff --git a/imgutil/suse16/pkglist b/imgutil/suse16/pkglist index 7f7e582a..461a855c 100644 --- a/imgutil/suse16/pkglist +++ b/imgutil/suse16/pkglist @@ -9,7 +9,6 @@ e2fsprogs chrony net-tools rsync tar util-linux python3 tar dracut ethtool parted openssl bash rpm iputils lvm2 efibootmgr attr dhcp-client zypper -grub2-x86_64-efi libfuse2 openssh shim @@ -20,3 +19,7 @@ tpm2.0-tools libtss2-tcti-device0 NetworkManager # 16 dropped wicked; confignet writes nmconnections vim +%onlyarch x86_64 +grub2-x86_64-efi +%onlyarch aarch64 +grub2-arm64-efi From 9089d3700b669b0e0564e64ca9a847aa7700ef4b Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:05:20 +0200 Subject: [PATCH 06/26] Find ssh-keysign where SUSE 16 puts it The permissions.local rule named /usr/lib/ssh, so keysign kept mode 0755 and hostbased auth failed with 'could not open any host key'. --- imgutil/imgutil | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imgutil/imgutil b/imgutil/imgutil index 7f0e0506..e9bd1f5c 100644 --- a/imgutil/imgutil +++ b/imgutil/imgutil @@ -692,13 +692,17 @@ class SuseHandler(OsHandler): mkdirp(os.path.dirname(sshdwant)) if not os.path.lexists(sshdwant): os.symlink('/usr/lib/systemd/system/sshd.service', sshdwant) + # 16 moved ssh-keysign to libexec + keysign = 'usr/libexec/ssh/ssh-keysign' + if not os.path.exists(os.path.join(self.targpath, keysign)): + keysign = 'usr/lib/ssh/ssh-keysign' with open(os.path.join(self.targpath, 'etc/permissions.local'), 'a') as permout: permout.write( - '/usr/lib/ssh/ssh-keysign root:ssh_keys 2711\n' + '/{0} root:ssh_keys 2711\n' '/etc/ssh/ssh_host_dsa_key root:ssh_keys 640\n' '/etc/ssh/ssh_host_ecdsa_key root:ssh_keys 640\n' '/etc/ssh/ssh_host_ed25519_key root:ssh_keys 640\n' - '/etc/ssh/ssh_host_rsa_key root:ssh_keys 640\n' + '/etc/ssh/ssh_host_rsa_key root:ssh_keys 640\n'.format(keysign) ) args.cmd = ['groupadd', 'ssh_keys'] run_constrainedx(fancy_chroot, (args, From dfa80306b5faad3d9a8ced7346d54015f0ca6cbf Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:05:20 +0200 Subject: [PATCH 07/26] Install timezone data in SUSE 16 images Without it onboot.sh cannot apply deployment.timezone. --- imgutil/suse16/pkglist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgutil/suse16/pkglist b/imgutil/suse16/pkglist index 461a855c..7fd474a6 100644 --- a/imgutil/suse16/pkglist +++ b/imgutil/suse16/pkglist @@ -6,7 +6,7 @@ sudo tuned xfsprogs e2fsprogs -chrony net-tools rsync tar util-linux python3 tar dracut ethtool parted openssl bash rpm iputils lvm2 efibootmgr attr +chrony timezone net-tools rsync tar util-linux python3 tar dracut ethtool parted openssl bash rpm iputils lvm2 efibootmgr attr dhcp-client zypper libfuse2 From fd9bdc00c31e9ec323f8fea16eccf4fd8d11f595 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:15:29 +0200 Subject: [PATCH 08/26] Ship the post.custom stub for SUSE 16 post.sh already runs it, so every install logged a 404 for it. --- .../suse16/profiles/server/scripts/post.custom | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 confluent_osdeploy/suse16/profiles/server/scripts/post.custom diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/post.custom b/confluent_osdeploy/suse16/profiles/server/scripts/post.custom new file mode 100644 index 00000000..a841271b --- /dev/null +++ b/confluent_osdeploy/suse16/profiles/server/scripts/post.custom @@ -0,0 +1,16 @@ +#!/bin/sh + +# This script runs at the end of install in the installed system +# but still under the installer kernel. + +# This is a good place to run most customizations that do not have any +# dependency upon the install target kernel being active. + +# If there are dependencies on the kernel (drivers or special filesystems) +# then firstboot.sh would be the script to customize. + +. /etc/confluent/functions + +# Examples: +# run_remote script.sh +# run_remote_python script.py From 6a77e91bf9756ae3abd58cfa5b2b24359923477c Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:15:51 +0200 Subject: [PATCH 09/26] Give SUSE 16 the customization hooks SUSE 15 has firstboot.custom and the empty pre.d, post.d, firstboot.d and ansible directories were left out, so the documented drop-in points did not exist. --- .../server/ansible/firstboot.d/README.txt | 29 +++++++++++++++++++ .../profiles/server/ansible/post.d/README.txt | 29 +++++++++++++++++++ .../profiles/server/scripts/firstboot.custom | 9 ++++++ .../server/scripts/firstboot.d/.gitignore | 0 .../profiles/server/scripts/firstboot.sh | 2 ++ .../profiles/server/scripts/post.d/.gitignore | 0 .../profiles/server/scripts/pre.d/.gitignore | 0 7 files changed, 69 insertions(+) create mode 100644 confluent_osdeploy/suse16/profiles/server/ansible/firstboot.d/README.txt create mode 100644 confluent_osdeploy/suse16/profiles/server/ansible/post.d/README.txt create mode 100644 confluent_osdeploy/suse16/profiles/server/scripts/firstboot.custom create mode 100644 confluent_osdeploy/suse16/profiles/server/scripts/firstboot.d/.gitignore create mode 100644 confluent_osdeploy/suse16/profiles/server/scripts/post.d/.gitignore create mode 100644 confluent_osdeploy/suse16/profiles/server/scripts/pre.d/.gitignore diff --git a/confluent_osdeploy/suse16/profiles/server/ansible/firstboot.d/README.txt b/confluent_osdeploy/suse16/profiles/server/ansible/firstboot.d/README.txt new file mode 100644 index 00000000..ad6fc712 --- /dev/null +++ b/confluent_osdeploy/suse16/profiles/server/ansible/firstboot.d/README.txt @@ -0,0 +1,29 @@ +Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the +appropriate phase of the install process. + +Alternatively, plays may be placed in /var/lib/confluent/private/os//ansible/. +This prevents public clients from being able to read the plays, which is not necessary for them to function, +and may protect them from divulging material contained in the plays or associated roles. + +The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically +requesting the playbooks be executed. + +Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than +running content under scripts/ which ask much less of the deployment server + +Here is an example of what a playbook would look like broadly: + +- name: Example + gather_facts: no + tasks: + - name: Example1 + lineinfile: + path: /etc/hosts + line: 1.2.3.4 test1 + create: yes + - name: Example2 + lineinfile: + path: /etc/hosts + line: 1.2.3.5 test2 + create: yes + diff --git a/confluent_osdeploy/suse16/profiles/server/ansible/post.d/README.txt b/confluent_osdeploy/suse16/profiles/server/ansible/post.d/README.txt new file mode 100644 index 00000000..ad6fc712 --- /dev/null +++ b/confluent_osdeploy/suse16/profiles/server/ansible/post.d/README.txt @@ -0,0 +1,29 @@ +Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the +appropriate phase of the install process. + +Alternatively, plays may be placed in /var/lib/confluent/private/os//ansible/. +This prevents public clients from being able to read the plays, which is not necessary for them to function, +and may protect them from divulging material contained in the plays or associated roles. + +The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically +requesting the playbooks be executed. + +Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than +running content under scripts/ which ask much less of the deployment server + +Here is an example of what a playbook would look like broadly: + +- name: Example + gather_facts: no + tasks: + - name: Example1 + lineinfile: + path: /etc/hosts + line: 1.2.3.4 test1 + create: yes + - name: Example2 + lineinfile: + path: /etc/hosts + line: 1.2.3.5 test2 + create: yes + diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.custom b/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.custom new file mode 100644 index 00000000..e2b7f793 --- /dev/null +++ b/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.custom @@ -0,0 +1,9 @@ +#!/bin/sh + +# This script runs at the end of the final boot + +. /etc/confluent/functions + +# Custom scripts may go here +# run_remote example.sh +# run_remote_python example.py diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.d/.gitignore b/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.d/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh b/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh index 2f3cdab3..6e3d3b7e 100644 --- a/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh +++ b/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh @@ -29,6 +29,8 @@ done export confluent_mgr run_remote_python confignet +run_remote firstboot.custom + # Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh run_remote_parts firstboot.d diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/post.d/.gitignore b/confluent_osdeploy/suse16/profiles/server/scripts/post.d/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/pre.d/.gitignore b/confluent_osdeploy/suse16/profiles/server/scripts/pre.d/.gitignore new file mode 100644 index 00000000..e69de29b From ace72d428a84fb6c330530fd61125064923d7fa0 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:27:39 +0200 Subject: [PATCH 10/26] Ship the syncfiles template for SUSE 16 post.sh runs syncfileclient, but there was no template to edit. --- .../suse16/profiles/server/syncfiles | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 confluent_osdeploy/suse16/profiles/server/syncfiles diff --git a/confluent_osdeploy/suse16/profiles/server/syncfiles b/confluent_osdeploy/suse16/profiles/server/syncfiles new file mode 100644 index 00000000..4ae543f0 --- /dev/null +++ b/confluent_osdeploy/suse16/profiles/server/syncfiles @@ -0,0 +1,29 @@ +# It is advised to avoid /var/lib/confluent/public as a source for syncing. /var/lib/confluent/public +# is served without authentication and thus any sensitive content would be a risk. If wanting to host +# syncfiles on a common share, it is suggested to have /var/lib/confluent be the share and use some other +# subdirectory other than public. +# +# Syncing is performed as the 'confluent' user, so all source files must be accessible by the confluent user. +# +# This file lists files to synchronize or merge to the deployed systems from the deployment server +# To specify taking /some/path/hosts on the deployment server and duplicating it to /etc/hosts: +# Note particularly the use of '->' to delineate source from target. +# /some/path/hosts -> /etc/hosts + +# If wanting to simply use the same path for source and destinaiton, the -> may be skipped: +# /etc/hosts + +# More function is available, for example to limit the entry to run only on n1 through n8, and to set +# owner, group, and permissions in octal notation: +# /example/source -> n1-n8:/etc/target (owner=root,group=root,permissions=600) + +# Entries under APPENDONCE: will be added to specified target, only if the target does not already +# contain the data in the source already in its entirety. This allows append in a fashion that +# is friendly to being run repeatedly + +# Entries under MERGE: will attempt to be intelligently merged. This supports /etc/group and /etc/passwd +# Any supporting entries in /etc/shadow or /etc/gshadow are added automatically, with password disabled +# It also will not inject 'system' ids (under 1,000 usually) as those tend to be local and rpm managed. +# MERGE: +# /etc/passwd +# /etc/group From 737c761be2fd7181a7fd4d383b29c46ef7dd1594 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:39:30 +0200 Subject: [PATCH 11/26] Set up hostbased ssh on installed SUSE 16 systems prechroot.sh had setupssh.sh commented out and copied only the keys inline, so nodes came up without shosts.equiv, without the CA in ssh_known_hosts and without a setuid ssh-keysign. --- .../profiles/server/scripts/prechroot.sh | 7 +-- .../profiles/server/scripts/setupssh.sh | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 confluent_osdeploy/suse16/profiles/server/scripts/setupssh.sh diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/prechroot.sh b/confluent_osdeploy/suse16/profiles/server/scripts/prechroot.sh index f6e28b37..9cbfac66 100644 --- a/confluent_osdeploy/suse16/profiles/server/scripts/prechroot.sh +++ b/confluent_osdeploy/suse16/profiles/server/scripts/prechroot.sh @@ -26,12 +26,7 @@ if [ -e "$instnmcfg" ]; then cp $instnmcfg /mnt/etc/NetworkManager/system-connections/ fi -#run_remote setupssh.sh -cp /etc/ssh/ssh*key* /mnt/etc/ssh/ -mkdir -p /mnt/etc/ssh/sshd_config.d/ -cp /etc/ssh/sshd_config.d/20_hostkeys.conf /mnt/etc/ssh/sshd_config.d/ -mkdir -p /mnt/root/.ssh/ -cp /root/.ssh/authorized_keys /mnt/root/.ssh/ +run_remote setupssh.sh echo Port 22 >> /etc/ssh/sshd_config.d/00-chroot.conf echo Port 2222 >> /etc/ssh/sshd_config.d/00-chroot.conf diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/setupssh.sh b/confluent_osdeploy/suse16/profiles/server/scripts/setupssh.sh new file mode 100644 index 00000000..ad4c1bde --- /dev/null +++ b/confluent_osdeploy/suse16/profiles/server/scripts/setupssh.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Carry over install-time ssh material into installed system + +mkdir -p /mnt/root/.ssh/ +chmod 700 /mnt/root/.ssh/ +cp /root/.ssh/authorized_keys /mnt/root/.ssh/ +chmod 600 /mnt/root/.ssh/authorized_keys +cp /etc/ssh/ssh*key* /mnt/etc/ssh/ +mkdir -p /mnt/etc/ssh/sshd_config.d/ +# pre.sh put the host certificates confluent signed into this drop-in +cp /etc/ssh/sshd_config.d/20_hostkeys.conf /mnt/etc/ssh/sshd_config.d/ +# the initramfs wrote the confluent CA into the installer's known hosts +cp /etc/ssh/ssh_known_hosts /mnt/etc/ssh/ + +# Enable ~/.shosts, for the sake of root user, who is forbidden from using shosts.equiv +cat >> /mnt/etc/ssh/sshd_config.d/90-confluent.conf << EOF +IgnoreRhosts no +HostbasedAuthentication yes +HostbasedUsesNameFromPacketOnly yes +EOF + +sshconf=/mnt/etc/ssh/ssh_config +if [ -d /mnt/etc/ssh/ssh_config.d/ ]; then + sshconf=/mnt/etc/ssh/ssh_config.d/01-confluent.conf +fi +echo Host '*' >> $sshconf +echo " HostbasedAuthentication yes" >> $sshconf +echo " EnableSSHKeysign yes" >> $sshconf +# Limit the attempts of using host key. This prevents client from using 3 or 4 +# authentication attempts through host based attempts +echo " HostbasedKeyTypes *ed25519*" >> $sshconf + +# In SUSE platform, setuid for ssh-keysign is required for host based, +# and also must be opted into. 16 moved the helper to libexec. +keysign=/usr/libexec/ssh/ssh-keysign +if [ ! -e /mnt$keysign ]; then + keysign=/usr/lib/ssh/ssh-keysign +fi +echo $keysign root:root 4711 >> /mnt/etc/permissions.local +chmod 4711 /mnt$keysign + +# Download list of nodes from confluent, and put it into shosts.equiv (for most users) and .shosts (for root) +python3 /opt/confluent/bin/apiclient /confluent-api/self/nodelist | sed -e 's/^- //' > /mnt/etc/ssh/shosts.equiv +cp /mnt/etc/ssh/shosts.equiv /mnt/root/.shosts From 9e8fc106a11f44de8350d79bc770f01aaef2f0be Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:56:47 +0200 Subject: [PATCH 12/26] Drop the anaconda leftovers from the SUSE 16 hook agama takes inst.install_url and inst.script, set just above. --- .../var/lib/dracut/hooks/pre-trigger/01-confluent.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh index 18bbad89..a7054689 100644 --- a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh +++ b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh @@ -283,17 +283,11 @@ if [ -z "$ISOSRC" ]; then echo root=live:$proto://$mgr/confluent-public/os/$profilename/distribution/1/LiveOS/squashfs.img >> /etc/cmdline.d/01-confluent.conf echo -n " "inst.install_url=$proto://$mgr/confluent-public/os/$profilename/distribution/1/install >> /run/agama/cmdline.d/agama.conf echo -n " "inst.script=$proto://$mgr/confluent-public/os/$profilename/autoinstall.sh" " >> /run/agama/cmdline.d/agama.conf - #echo inst.=$proto://$mgr/confluent-public/os/$profilename/distribution >> /etc/cmdline.d/01-confluent.conf - #root=anaconda-net:$proto://$mgr/confluent-public/os/$profilename/distribution - #export root netroot=livenet:$proto://$mgr/confluent-public/os/$profilename/distribution/1/LiveOS/squashfs.img else echo inst.repo=cdrom:$ISOSRC >> /etc/cmdline.d/01-confluent.conf fi -#echo inst.ks=$proto://$mgr/confluent-public/os/$profilename/kickstart >> /etc/cmdline.d/01-confluent.conf -#kickstart=$proto://$mgr/confluent-public/os/$profilename/kickstart -#export kickstart autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg) autoconfigmethod=${autoconfigmethod#ipv4_method: } if [ "$autoconfigmethod" = "dhcp" ]; then From be6c7a37948850c51ab40ad600adaaa735ba31dd Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 02:56:54 +0200 Subject: [PATCH 13/26] Load the InfiniBand modules for SUSE 16 installs They stayed commented out when the hook was forked from el8, so an IPoIB-only node had no path to the deploy server. The diskless hook loads them already. --- .../var/lib/dracut/hooks/pre-trigger/01-confluent.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh index a7054689..dcff8c59 100644 --- a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh +++ b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh @@ -14,10 +14,10 @@ fi udevadm trigger udevadm trigger --type=devices --action=add udevadm settle -#modprobe ib_ipoib -#modprobe ib_umad -#modprobe hfi1 -#modprobe mlx5_ib +modprobe ib_ipoib +modprobe ib_umad +modprobe hfi1 +modprobe mlx5_ib vlaninfo=$(getarg vlan) if [ ! -z "$vlaninfo" ]; then From a0cd0ac5f2a42ef8b977bdc8d18e0b48cc8bba01 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 03:07:36 +0200 Subject: [PATCH 14/26] Verify the deploy server when registering a SUSE 16 node The -k made the --capath on the same line pointless. el8 makes the identical call without it. --- .../initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh index dcff8c59..7b45bb7a 100644 --- a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh +++ b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh @@ -128,7 +128,7 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then echo 'MANAGER: '$deploysrv >> /etc/confluent/confluent.info done for deployer in $deploysrvs; do - if curl --capath /tls/ -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_CRYPTHMAC: $(cat $hmacfile)" -d@$passcrypt -k https://$deployer/confluent-api/self/registerapikey; then + if curl --capath /tls/ -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_CRYPTHMAC: $(cat $hmacfile)" -d@$passcrypt https://$deployer/confluent-api/self/registerapikey; then cp $passfile /etc/confluent/confluent.apikey confluent_apikey=$(cat /etc/confluent/confluent.apikey) curl --capath /tls/ -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$deployer/confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg From 6cd3805058113e839fc066b53a50920e599f7c16 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 03:12:29 +0200 Subject: [PATCH 15/26] Drop the EL vendor names from the SUSE 16 hook Neither Oracle nor Red Hat can be the first word of a SUSE PRETTY_NAME. --- .../var/lib/dracut/hooks/pre-trigger/01-confluent.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh index 7b45bb7a..b6433769 100644 --- a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh +++ b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh @@ -272,11 +272,6 @@ fi if [ "$ID" = "dracut" ]; then ID=$(echo $PRETTY_NAME|awk '{print $1}') VERSION_ID=$(echo $VERSION|awk '{print $1}') - if [ "$ID" = "Oracle" ]; then - ID=OL - elif [ "$ID" = "Red" ]; then - ID=RHEL - fi fi ISOSRC=$(blkid -t TYPE=iso9660|grep -Ei ' LABEL="'$ID-$VERSION_ID|sed -e s/:.*//) if [ -z "$ISOSRC" ]; then From 571076b211bce69f5bfb1e7fa77edfb7527396cf Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 03:45:36 +0200 Subject: [PATCH 16/26] Always netboot the SUSE 16 installer The attached-media branch could never be taken: the label pattern built from os-release is opensuse-leap-16.0 or sles-16.0, while the media is labelled Install-Leap-16.0-x86_64 and Install-SUSE-SLE-16-x86_64. Had it matched, it would have written an inst.repo to the dracut cmdline that agama does not read, and skipped inst.script entirely. --- .../dracut/hooks/pre-trigger/01-confluent.sh | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh index b6433769..7b6745fc 100644 --- a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh +++ b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh @@ -268,21 +268,10 @@ if [ "$textconsole" = "true" ] && ! grep console= /proc/cmdline > /dev/null; the fi fi -. /etc/os-release -if [ "$ID" = "dracut" ]; then - ID=$(echo $PRETTY_NAME|awk '{print $1}') - VERSION_ID=$(echo $VERSION|awk '{print $1}') -fi -ISOSRC=$(blkid -t TYPE=iso9660|grep -Ei ' LABEL="'$ID-$VERSION_ID|sed -e s/:.*//) -if [ -z "$ISOSRC" ]; then - echo root=live:$proto://$mgr/confluent-public/os/$profilename/distribution/1/LiveOS/squashfs.img >> /etc/cmdline.d/01-confluent.conf - echo -n " "inst.install_url=$proto://$mgr/confluent-public/os/$profilename/distribution/1/install >> /run/agama/cmdline.d/agama.conf - echo -n " "inst.script=$proto://$mgr/confluent-public/os/$profilename/autoinstall.sh" " >> /run/agama/cmdline.d/agama.conf - netroot=livenet:$proto://$mgr/confluent-public/os/$profilename/distribution/1/LiveOS/squashfs.img - -else - echo inst.repo=cdrom:$ISOSRC >> /etc/cmdline.d/01-confluent.conf -fi +echo root=live:$proto://$mgr/confluent-public/os/$profilename/distribution/1/LiveOS/squashfs.img >> /etc/cmdline.d/01-confluent.conf +echo -n " "inst.install_url=$proto://$mgr/confluent-public/os/$profilename/distribution/1/install >> /run/agama/cmdline.d/agama.conf +echo -n " "inst.script=$proto://$mgr/confluent-public/os/$profilename/autoinstall.sh" " >> /run/agama/cmdline.d/agama.conf +netroot=livenet:$proto://$mgr/confluent-public/os/$profilename/distribution/1/LiveOS/squashfs.img autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg) autoconfigmethod=${autoconfigmethod#ipv4_method: } if [ "$autoconfigmethod" = "dhcp" ]; then From f3ea32bcd35bfe8b1ec92acc9292fe0550228f49 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 04:35:41 +0200 Subject: [PATCH 17/26] Say autoinstall where the SUSE 16 pre script means it The comment came from 15, which rewrote an autoyast profile. --- confluent_osdeploy/suse16/profiles/server/scripts/pre.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh b/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh index 87132945..431b618d 100644 --- a/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh +++ b/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh @@ -1,7 +1,7 @@ #!/bin/bash # This script runs before the installer executes, and sets up ssh during install as well -# as rewriting the autoyast file with any substitutions prior to it being evaluated for real +# as rewriting the autoinstall file with any substitutions prior to it being evaluated for real exec >> /tmp/confluent-pre.log exec 2>> /tmp/confluent-pre.log From f742626226c77cfef65dde569ae76b6631257066 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 05:05:37 +0200 Subject: [PATCH 18/26] Stop the SUSE 16 install when agama rejects the config The default Etc/UTC is not in the tzdata list agama validates against, so the load failed and the install fell through to agama defaults and still reported completion. Normalize it and halt if the load fails. --- confluent_osdeploy/suse16/profiles/server/autoinstall.sh | 5 ++++- confluent_osdeploy/suse16/profiles/server/scripts/pre.sh | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/confluent_osdeploy/suse16/profiles/server/autoinstall.sh b/confluent_osdeploy/suse16/profiles/server/autoinstall.sh index 81f492a1..dbc4d8b7 100644 --- a/confluent_osdeploy/suse16/profiles/server/autoinstall.sh +++ b/confluent_osdeploy/suse16/profiles/server/autoinstall.sh @@ -24,6 +24,9 @@ python3 /opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/pre.s chmod +x /tmp/pre.sh /tmp/pre.sh > $serialcons 2>&1 kill $tailpid -agama config load /tmp/autoinstall.json <> $serialcons > $serialcons 2>&1 +if ! agama config load /tmp/autoinstall.json <> $serialcons > $serialcons 2>&1; then + echo "****Failed to load the install configuration, halting install" > $serialcons + while :; do sleep 86400; done +fi agama install <> $serialcons > $serialcons 2>&1 agama finish <> $serialcons > $serialcons 2>&1 diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh b/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh index 431b618d..66adf2d9 100644 --- a/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh +++ b/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh @@ -38,6 +38,9 @@ sed -i s!%%DEPLOYER%%!$deployserver!g /tmp/autoinstall.json sed -i s!%%PROFILE%%!$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')!g /tmp/autoinstall.json sed -i s!%%ROOTPASSWORD%%!$(grep ^rootpassword: /etc/confluent/confluent.deploycfg|awk '{print $2}')!g /tmp/autoinstall.json sed -i s!%%NODENAME%%!$(hostname)!g /tmp/autoinstall.json +python3 /opt/confluent/bin/apiclient /confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml +blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //') +sed -i 's!%%INSTALLEDARGS%%!'"$blargs"'!g' /tmp/autoinstall.json python3 /opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/getinstalldisk > /tmp/getinstalldisk locale=$(grep ^locale: /etc/confluent/confluent.deploycfg) locale=${locale#locale: } @@ -45,6 +48,10 @@ keymap=$(grep ^keymap: /etc/confluent/confluent.deploycfg) keymap=${keymap#keymap: } tz=$(grep ^timezone: /etc/confluent/confluent.deploycfg) tz=${tz#timezone: } +# agama checks against the tzdata list, which carries UTC but no Etc/ zones +if [ "$tz" = "Etc/UTC" ]; then + tz=UTC +fi sed -i 's!%%TIMEZONE%%!'$tz'!g' /tmp/autoinstall.json sed -i 's!%%LOCALE%%!'$locale'!g' /tmp/autoinstall.json sed -i 's!%%KEYMAP%%!'$keymap'!g' /tmp/autoinstall.json From 20563d3c3ad898751a0ae34c8914399a0c896760 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 05:05:37 +0200 Subject: [PATCH 19/26] Apply installedargs on SUSE 16 Every other profile feeds it to the bootloader; agama takes it as bootloader.extraKernelParams. --- confluent_osdeploy/suse16/profiles/server/autoinstall.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/confluent_osdeploy/suse16/profiles/server/autoinstall.json b/confluent_osdeploy/suse16/profiles/server/autoinstall.json index 8bce21fd..a954e0e0 100644 --- a/confluent_osdeploy/suse16/profiles/server/autoinstall.json +++ b/confluent_osdeploy/suse16/profiles/server/autoinstall.json @@ -16,6 +16,9 @@ "product": { "id": "%%PRODUCT%%" }, + "bootloader": { + "extraKernelParams": "%%INSTALLEDARGS%%" + }, "storage": { "drives": [ { From 2e0323e3337e6b8c297bddeea3977433b587e67a Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 05:05:37 +0200 Subject: [PATCH 20/26] Run the SUSE 16 firstboot service only once 15 got this from AutoYaST init-scripts. 16 enables its own unit, so it has to disable it the way el8 and the diskless profiles do. --- confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh b/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh index 6e3d3b7e..7216132e 100644 --- a/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh +++ b/confluent_osdeploy/suse16/profiles/server/scripts/firstboot.sh @@ -38,3 +38,6 @@ run_remote_parts firstboot.d run_remote_config firstboot.d python3 /opt/confluent/bin/apiclient /confluent-api/self/updatestatus -d 'status: complete' + +systemctl disable confluent-firstboot +rm /etc/systemd/system/confluent-firstboot.service From edf98e317775869bc968ae8b84ddef1cb41e21b1 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 06:05:56 +0200 Subject: [PATCH 21/26] Lock root on SUSE 16 when no password is set The deploycfg carries the literal 'null', which went into the profile as a password hash, so the installed root account reported a usable password instead of a locked one. 15 substitutes '!' for this; the sed delimiter has to move off '!' to carry it. --- confluent_osdeploy/suse16/profiles/server/scripts/pre.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh b/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh index 66adf2d9..4d8a6a93 100644 --- a/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh +++ b/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh @@ -36,7 +36,12 @@ hostname $(grep ^NODENAME: /etc/confluent/confluent.info|awk '{print $2}') run_remote_parts pre.d sed -i s!%%DEPLOYER%%!$deployserver!g /tmp/autoinstall.json sed -i s!%%PROFILE%%!$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')!g /tmp/autoinstall.json -sed -i s!%%ROOTPASSWORD%%!$(grep ^rootpassword: /etc/confluent/confluent.deploycfg|awk '{print $2}')!g /tmp/autoinstall.json +rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg|awk '{print $2}') +if [ "$rootpw" = "null" ]; then + # lock the account, as 15 does. ! is a marker, not a hash anything matches + rootpw='!' +fi +sed -i 's@%%ROOTPASSWORD%%@'"$rootpw"'@g' /tmp/autoinstall.json sed -i s!%%NODENAME%%!$(hostname)!g /tmp/autoinstall.json python3 /opt/confluent/bin/apiclient /confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //') From d7a25a99336954583dce95fe96609566810e89df Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 06:42:08 +0200 Subject: [PATCH 22/26] Use dhcpcd for SUSE 16 diskless SLES 16 ships no ISC dhclient and nothing provides dhcp-client, so the image could not be built from SLES media at all. Leap carries dhcpcd too, so one client covers both. el10 made the same move when RHEL dropped dhclient. --- .../var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh | 2 +- imgutil/suse16/dracut/install | 2 +- imgutil/suse16/pkglist | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh b/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh index e4f9c0f3..a2ff4cab 100644 --- a/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh +++ b/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh @@ -218,7 +218,7 @@ fi autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg |awk '{print $2}') if [ "$autoconfigmethod" = "dhcp" ]; then echo -n "Attempting to use dhcp to bring up $ifname..." - dhclient $ifname + dhcpcd $ifname echo "Complete:" ip addr show dev $ifname printf '\n[ipv4]\nmethod=auto\n' >> $nmcfg diff --git a/imgutil/suse16/dracut/install b/imgutil/suse16/dracut/install index 67819080..c7ffd755 100644 --- a/imgutil/suse16/dracut/install +++ b/imgutil/suse16/dracut/install @@ -8,7 +8,7 @@ dracut_install modprobe touch echo cut wc bash uniq grep ip hostname dracut_install awk egrep dirname expr sort dracut_install ssh sshd reboot parted mkfs mkfs.ext4 mkfs.xfs xfs_db mkswap dracut_install efibootmgr uuidgen -dracut_install du df ssh-keygen scp clear dhclient +dracut_install du df ssh-keygen scp clear dhcpcd dracut_install /lib64/libnss_dns.so.2 # glibc dropped the versioned name dracut_install /lib64/libnss_compat* dracut_install /usr/lib64/libnl-3.so.200 diff --git a/imgutil/suse16/pkglist b/imgutil/suse16/pkglist index 7fd474a6..1755ddde 100644 --- a/imgutil/suse16/pkglist +++ b/imgutil/suse16/pkglist @@ -7,7 +7,9 @@ tuned xfsprogs e2fsprogs chrony timezone net-tools rsync tar util-linux python3 tar dracut ethtool parted openssl bash rpm iputils lvm2 efibootmgr attr -dhcp-client +dhcpcd +# dhcp-client used to pull this in; the diskless hook needs ip regardless +iproute2 zypper libfuse2 openssh From d069328dd2f645e6a74f454dbe1fb2697590649c Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 07:04:22 +0200 Subject: [PATCH 23/26] Put the sshd helpers in the SUSE 16 diskless initramfs OpenSSH 10 splits each connection into sshd-session and that into sshd-auth, so the initramfs sshd on 2222 could not serve a single session. el10 added sshd-session for the same reason. --- imgutil/suse16/dracut/install | 3 +++ 1 file changed, 3 insertions(+) diff --git a/imgutil/suse16/dracut/install b/imgutil/suse16/dracut/install index c7ffd755..07b537ba 100644 --- a/imgutil/suse16/dracut/install +++ b/imgutil/suse16/dracut/install @@ -7,6 +7,9 @@ dracut_install curl openssl tar cpio gzip lsmod ethtool xz lsmod ethtool dracut_install modprobe touch echo cut wc bash uniq grep ip hostname dracut_install awk egrep dirname expr sort dracut_install ssh sshd reboot parted mkfs mkfs.ext4 mkfs.xfs xfs_db mkswap +# 10 execs a helper per connection, and that helper execs another to +# authenticate. Without both, the initramfs sshd accepts nothing. +dracut_install /usr/libexec/ssh/sshd-session /usr/libexec/ssh/sshd-auth dracut_install efibootmgr uuidgen dracut_install du df ssh-keygen scp clear dhcpcd dracut_install /lib64/libnss_dns.so.2 # glibc dropped the versioned name From c66d7bcb7e56f30907b3b9278dd84cbbe869636f Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 13:19:36 +0200 Subject: [PATCH 24/26] Give the SUSE 16 initramfs libkmod udev's kmod builtin dlopens libkmod, and dracut installs it from an inst_libdir_file line in a module-setup.sh rather than by following NEEDED. Which module carries that line moved: the dracut on SLE 16 media declares it only in 00systemd, which the diskless module set never loads, so the image came up with no libkmod, udev autoloaded nothing, and the guest reached the network scan with only loopback. Leap's newer dracut also declares it in 95udev-rules, which base depends on, which is why Leap was unaffected. --- imgutil/suse16/dracut/install | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/imgutil/suse16/dracut/install b/imgutil/suse16/dracut/install index 07b537ba..253a2c72 100644 --- a/imgutil/suse16/dracut/install +++ b/imgutil/suse16/dracut/install @@ -12,6 +12,10 @@ dracut_install ssh sshd reboot parted mkfs mkfs.ext4 mkfs.xfs xfs_db mkswap dracut_install /usr/libexec/ssh/sshd-session /usr/libexec/ssh/sshd-auth dracut_install efibootmgr uuidgen dracut_install du df ssh-keygen scp clear dhcpcd +# udev's kmod builtin dlopens this. The dracut on SLE 16 media declares it +# only in 00systemd, which the diskless module set never loads, so without +# this line udev autoloads nothing at all +dracut_install /usr/lib64/libkmod.so.2 dracut_install /lib64/libnss_dns.so.2 # glibc dropped the versioned name dracut_install /lib64/libnss_compat* dracut_install /usr/lib64/libnl-3.so.200 From 583e2fa32113554cf0b601197a3cd3b33a8c5382 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Mon, 7 Sep 2026 13:19:36 +0200 Subject: [PATCH 25/26] Import the signing key SLE 16 media ships Building a SUSE 16 image from SLE media failed every package with "key ID fec28eaf09d9ea69: NOKEY". Leap publishes that key as gpg-pubkey-*.asc, which the existing glob picks up; SLE publishes the same key only as repodata/repomd.xml.key, so nothing was imported. 15 media carries both spellings, so this changes nothing there. --- imgutil/imgutil | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgutil/imgutil b/imgutil/imgutil index e9bd1f5c..fc73e3de 100644 --- a/imgutil/imgutil +++ b/imgutil/imgutil @@ -657,6 +657,8 @@ class SuseHandler(OsHandler): if source.startswith('file://'): gpgpath = source.replace('file://', '') gpgkeys.extend(glob.glob(os.path.join(gpgpath, '*/gpg-pubkey*.asc'))) + # SLE media publishes the signing key only as the repo key + gpgkeys.extend(glob.glob(os.path.join(gpgpath, '*/repodata/repomd.xml.key'))) subprocess.check_call(self.zypper_cmd() + ['ar', source, 'source-{}'.format(idx)]) idx += 1 if gpgkeys: From 399ce08680402bbb6fdbb33c9c25a11dec160125 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Tue, 8 Sep 2026 16:01:43 +0200 Subject: [PATCH 26/26] Stop loading InfiniBand modules the SUSE 16 media lacks This reverts commit be6c7a37948850c51ab40ad600adaaa735ba31dd. They do not exist in Leap/SLE 16 installer initrd. The diskless hook keeps its copies. imgutil's installkernel instmods all four, so they are in that image and the calls do work there. --- .../var/lib/dracut/hooks/pre-trigger/01-confluent.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh index 7b6745fc..a5bd3968 100644 --- a/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh +++ b/confluent_osdeploy/suse16/initramfs/var/lib/dracut/hooks/pre-trigger/01-confluent.sh @@ -14,10 +14,10 @@ fi udevadm trigger udevadm trigger --type=devices --action=add udevadm settle -modprobe ib_ipoib -modprobe ib_umad -modprobe hfi1 -modprobe mlx5_ib +#modprobe ib_ipoib +#modprobe ib_umad +#modprobe hfi1 +#modprobe mlx5_ib vlaninfo=$(getarg vlan) if [ ! -z "$vlaninfo" ]; then