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/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..a2ff4cab --- /dev/null +++ b/confluent_osdeploy/suse16-diskless/initramfs/var/lib/dracut/hooks/cmdline/10-confluentdiskless.sh @@ -0,0 +1,281 @@ +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 +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..." + dhcpcd $ifname + echo "Complete:" + ip addr show dev $ifname + printf '\n[ipv4]\nmethod=auto\n' >> $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 '\n[ipv4]\nmethod=manual\naddress1=%s/%s' "$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 +fi +printf '\n[ipv6]\nmethod=link-local\n' >> $nmcfg +chmod 600 $nmcfg + +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 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..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 @@ -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 @@ -268,32 +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}') - 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 - 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 +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 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/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": [ { 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/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 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..7216132e 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 @@ -36,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 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 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 diff --git a/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh b/confluent_osdeploy/suse16/profiles/server/scripts/pre.sh index 87132945..4d8a6a93 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 @@ -36,8 +36,16 @@ 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: //') +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 +53,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 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 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 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/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..fc73e3de 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,29 @@ 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/*'): + 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: @@ -626,7 +657,9 @@ 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)]) + # 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: addkeycmd = ['rpm', '--root', self.targpath, '--import'] + gpgkeys @@ -638,7 +671,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,17 +685,26 @@ 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) + # 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, @@ -675,7 +717,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 +1344,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 +1786,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 +1803,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 +1827,14 @@ 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): + # 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) diff --git a/imgutil/suse16/dracut/install b/imgutil/suse16/dracut/install new file mode 100644 index 00000000..253a2c72 --- /dev/null +++ b/imgutil/suse16/dracut/install @@ -0,0 +1,46 @@ +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 +# 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 +# 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 +# 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..1755ddde --- /dev/null +++ b/imgutil/suse16/pkglist @@ -0,0 +1,27 @@ +hostname +curl +irqbalance +less +sudo +tuned +xfsprogs +e2fsprogs +chrony timezone net-tools rsync tar util-linux python3 tar dracut ethtool parted openssl bash rpm iputils lvm2 efibootmgr attr +dhcpcd +# dhcp-client used to pull this in; the diskless hook needs ip regardless +iproute2 +zypper +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 +%onlyarch x86_64 +grub2-x86_64-efi +%onlyarch aarch64 +grub2-arm64-efi