How to create an extended rockylinux from kickstart?

Dear,

firstly thanks to rockylinux team for your works done, thanks @nazunalika .

I spent lot of time (try, error, try …) to get an iso from kickstart at the end I will share what it worked and what do not worked.
But firstly I am looking 2 things and two questions

  1. how to import host file to sysimage at %post stage
  2. how to enable cuda repo at %post stage
  3. why official kickstart put rocky user into sudoers but they are any rocky user creation statement (like user --name=rocky)?
  4. why root user is locked even if we use the statement rootpw without --lock root user is still locked

What I tried and didn’t worked

  1. how to import host file to sysimage at %post stage
    %post --nochroot I tried to find host files in various dir without succes such as /mnt/sysroot/ and others directories
    I also try to convert the directory to iso with geniso and send this second iso as disk to virt-install without success.
RELEASE_VER='9'
RLTYPE='.4'
ARCH='x86_64'
MEM_SIZE=4096                       # Memory setting in MiB
VCPUS=1                             # CPU Cores count
OS_VARIANT='rocky'"${RELEASE_VER}"  # Select from the output of "osinfo-query os"
LOCATION="https://download.rockylinux.org/stg/rocky/${RELEASE_VER}/BaseOS/${ARCH}/os/"
REMOTE_ISO="https://download.rockylinux.org/pub/rocky/${RELEASE_VER}/isos/${ARCH}/Rocky-${RELEASE_VER}${RLTYPE}-${ARCH}-minimal.iso"
ISO_FILE="${PWD}/Rocky-${RELEASE_VER}${RLTYPE}-${ARCH}-minimal.iso" # Path to ISO file
VM_NAME='server-1'
DISK_SIZE=15
DISK_FILE="./${VM_NAME}.qcow2"
KICKSTART='Rocky-9-Azure-LVM.v1.ks'
BRIDGE_NETWORK='bridge=virbr0'

if [[ -n "${LOCATION_ISO}" && ! -f "${LOCATION_ISO}"  ]]; then
  curl -Lo"${LOCATION_ISO}" "${REMOTE_ISO}"
fi

if [[ -f files.iso ]]; then
  rm files.iso
fi

genisoimage -o files.iso files/


sudo virt-install \
     --name ${VM_NAME} \
     --memory=${MEM_SIZE} \
     --vcpus=${VCPUS} \
     --arch="${ARCH}" \
     --location="${LOCATION}" \
     --disk="path=${DISK_FILE},size=${DISK_SIZE}"  \
     --disk="path=${PWD}/files.iso,device=cdrom" \
     --network="${BRIDGE_NETWORK}" \
     --graphics='none' \
     --os-variant="${OS_VARIANT}" \
     --console='pty,target_type=serial' \
     --initrd-inject "${KICKSTART}" --extra-args "inst.ks=file:/${KICKSTART} console=tty0 console=ttyS0,115200n8"
  1. how to enable cuda repo at %post stage
    a. I tried using repo state with --install
repo --name=cuda-rhel9-x86_64      --install       --baseurl=https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64

The repo is not installed i.e /etc/yum.repos.d/cuda.repo do no exists
b. add at %post stage

dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo
rpm --import                  https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/D42D0685.pub

I got a network error. I done the same things once iso is created from qemu that works well!

Miscellaneous
While the language spec on kickstart is described, the creation of an iso is not well documented.
As example rocky github tell to use mock and livemedia-creator but that didn’t work on my side. I found virt-install more reproducible and works well on rocky9 kickstart.

My current not finished kickstart

#version=DEVEL
# Keyboard layouts
keyboard 'fr'
# Root like user
user --name=someone1 --groups=wheel --gecos='Emergency Admin Account'      --uid=5000 --gid=5000  --iscrypted --password='$6$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# User creation
user --name=someone2                --gecos='Service Account for xxx'    --uid=5001 --gid=5001  --iscrypted --password='$6$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

# System language
lang en_US.UTF-8

# Shutdown after installation
shutdown

# System timezone
timezone Europe/Paris

# Use text mode install
text

# Network information
network  --bootproto=dhcp --device=link --activate

# Use network installation
url --url="https://download.rockylinux.org/stg/rocky/9/BaseOS/$basearch/os/"
# Repositories
repo --name=epel                          --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=epel-9&arch=x86_64
repo --name=epel-next                     --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=epel-next-9&arch=x86_64
repo --name=cuda-rhel9-x86_64             --baseurl=https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64
repo --name=packages-microsoft-com-prod   --baseurl=https://packages.microsoft.com/rhel/9.0/prod
repo --name=rocky-BaseOS-9.4              --mirrorlist=https://mirrors.rockylinux.org/mirrorlist?repo=rocky-BaseOS-9.4&arch=x86_64
repo --name=rocky-Extra-OS-9.4            --mirrorlist=https://mirrors.rockylinux.org/mirrorlist?repo=plus-9.4&arch=x86_64
repo --name=rocky-Appstream-9.4           --mirrorlist=https://mirrors.rockylinux.org/mirrorlist?repo=rocky-AppStream-9.4&arch=x86_64
repo --name=rocky-CRB-9.4                 --mirrorlist=https://mirrors.rockylinux.org/mirrorlist?repo=rocky-CRB-source-9.4&arch=x86_64


# Firewall configuration
firewall --enabled --service=ssh
firstboot --disable
# SELinux configuration
selinux --enforcing

# System services
services --disabled="kdump,rhsmcertd" --enabled="NetworkManager,sshd,rsyslog,chronyd,cloud-init,cloud-init-local,cloud-config,cloud-final,cockpit.socket,rngd,waagent"
# System bootloader configuration
bootloader --append="rootdelay=300 console=ttyS0 earlyprintk=ttyS0  no_timer_check crashkernel=auto net.ifnames=0" --location=mbr --timeout=1
# Clear the Master Boot Record
#zerombr
# Partition clearing information
#clearpart --all --initlabel --disklabel=gpt
# Disk partitioning information
part /boot/efi --fstype="efi" --onpart=vda1
part /boot --fstype="xfs" --label=boot --onpart=vda2
part prepboot --fstype="prepboot" --onpart=vda3
part biosboot --fstype="biosboot" --onpart=vda4
part pv.01 --grow --ondisk=vda --onpart=vda5
volgroup rocky pv.01
logvol / --grow --size=8000 --mkfsoptions="-m bigtime=0,inobtcount=0" --name=root --vgname=rocky

%pre
# Clear the Master Boot Record
dd if=/dev/zero of=/dev/vda bs=512 count=1
# Create a new GPT partition table
parted /dev/vda mklabel gpt
# Create a partition for /boot/efi
parted /dev/vda mkpart primary fat32 1MiB 100MiB
parted /dev/vda set 1 boot on
# Create a partition for /boot
parted /dev/vda mkpart primary xfs 100MiB 1100MiB
# Create a partition for prep
parted /dev/vda mkpart primary 1100MiB 1104MiB
# Create a partition for bios_grub
parted /dev/vda mkpart primary 1104MiB 1105MiB
# Create a partition for LVM
parted /dev/vda mkpart primary ext2 1106MiB 10.7GB
parted /dev/vda set 5 lvm on

%end

%post --erroronfail  --log=/root/ks-post.log
set -x
# Attempting to force legacy BIOS boot if we boot from UEFI
if [ "$(arch)" = "x86_64"  ]; then
  dnf install grub2-pc-modules grub2-pc -y
  grub2-install --target=i386-pc /dev/vda
fi

# Ensure that the pmbr_boot flag is off
parted /dev/vda disk_set pmbr_boot off

# Common Cloud Tweaks
# setup systemd to boot to the right runlevel
rm -f /etc/systemd/system/default.target
ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
echo .

# remove linux-firmware as we're virt and it's half a gig
dnf -C -y remove linux-firmware

# Remove firewalld; it is required to be present for install/image building.
# but we dont ship it in cloud
dnf -C -y remove firewalld --setopt="clean_requirements_on_remove=1"
dnf -C -y remove avahi\* 
sed -i '/^#NAutoVTs=.*/ a\
NAutoVTs=0' /etc/systemd/logind.conf

echo "virtual-guest" > /etc/tuned/active_profile

cat << EOF | tee -a /etc/NetworkManager/conf.d/dhcp-timeout.conf
# Configure dhcp timeout to 300s by default
[connection]
ipv4.dhcp-timeout=300
EOF

cat > /etc/sysconfig/network << EOF
NETWORKING=yes
NOZEROCONF=yes
EOF

# Remove build-time resolvers to fix #16948
truncate -s 0 /etc/resolv.conf

# generic localhost names
cat > /etc/hosts << EOF
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

EOF
echo .

# azure settings
# Configure waagent for cloud-init
sed -i 's/Provisioning.UseCloudInit=n/Provisioning.UseCloudInit=y/g' /etc/waagent.conf
sed -i 's/Provisioning.Enabled=y/Provisioning.Enabled=n/g' /etc/waagent.conf

# Azure: handle sr-iov and networkmanager
cat << EOF | tee -a /etc/udev/rules.d/68-azure-sriov-nm-unmanaged.rules
# Accelerated Networking on Azure exposes a new SRIOV interface to the VM.
# This interface is transparently bonded to the synthetic interface,
# so NetworkManager should just ignore any SRIOV interfaces.
SUBSYSTEM=="net", DRIVERS=="hv_pci", ACTION=="add", ENV{NM_UNMANAGED}="1"
EOF

# Azure: Time sync for linux
## Setup udev rule for ptp_hyperv
cat << EOF | tee -a /etc/udev/rules.d/98-hyperv-ptp.rules
## See: https://docs.microsoft.com/en-us/azure/virtual-machines/linux/time-sync#check-for-ptp-clock-source
SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK += "ptp_hyperv"
EOF

# Configure chrony to use ptp_hyperv
cat << EOF | tee -a /etc/chrony.conf
# Setup hyperv PTP device as refclock
refclock PHC /dev/ptp_hyperv poll 3 dpoll -2 offset 0 stratum 2
EOF

# Azure: Blacklist modules
cat << EOF | tee -a /etc/modprobe.d/azure-blacklist.conf
blacklist amdgpu
blacklist nouveau
blacklist radeon
EOF

# Azure: cloud-init customizations for Hyperv
cat << EOF | tee /etc/cloud/cloud.cfg.d/10-azure-kvp.cfg
# Enable logging to the Hyper-V kvp in Azure
reporting:
  logging:
    type: log
  telemetry:
    type: hyperv
EOF

# Kernel and Drivers
# Add drivers when building in VMWare, Vbox, or KVM (KVM)
cat << EOF | tee -a /etc/dracut.conf.d/80-azure.conf
add_drivers+=" hv_vmbus hv_netvsc hv_storvsc "
EOF

dracut -f -v

cat <<EOL > /etc/sysconfig/kernel
# UPDATEDEFAULT specifies if new-kernel-pkg should make
# new kernels the default
UPDATEDEFAULT=yes

# DEFAULTKERNEL specifies the default kernel package type
DEFAULTKERNEL=kernel
EOL

# make sure firstboot doesn't start
echo "RUN_FIRSTBOOT=NO" > /etc/sysconfig/firstboot

# rocky cloud user
sed -i '1i # Modified for cloud image' /etc/cloud/cloud.cfg
sed -i 's/name: cloud-user/name: rocky/g' /etc/cloud/cloud.cfg
echo -e 'rocky\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers

# this shouldn't be the case, but we'll do it anyway
sed -i 's|^enabled=1|enabled=0|' /etc/yum/pluginconf.d/product-id.conf
sed -i 's|^enabled=1|enabled=0|' /etc/yum/pluginconf.d/subscription-manager.conf
reboot

dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo
rpm --import                  https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/D42D0685.pub
dnf install kernel-devel-$(uname -r) kernel-headers-$(uname -r)
dnf install nvidia-driver nvidia-settings cuda-driver
dnf clean all
truncate -c -s 0 /var/log/dnf.log

# XXX instance type markers - MUST match Rocky Infra expectation
echo 'azure' > /etc/yum/vars/infra

# sudoer
cat <<'EOF'> /etc/sudoers.d/default
someone1 ALL=(ALL) ALL
EOF

# banner
cat <<'EOF'> /etc/profile.d/banner.sh
#!/usr/bin/bash

NONE="\033[m"
WHITE="\033[1;37m"
GREEN="\033[1;32m"
RED="\033[0;32;31m"
YELLOW="\033[1;33m"
BLUE="\033[34m"
CYAN="\033[36m"
LIGHT_GREEN="\033[1;32m"
LIGHT_RED="\033[1;31m"


hostName="$(uname -n)"
diskSpace="$(df -Ph)"
# memory shown in Gigabyte
memoryTotal="$(free -t -g | grep Total | awk '{printf("%'"'"'d",$2);}')"
memoryUsed="$(free -t -g | grep Total | awk '{printf("%'"'"'d",$3);}')"
memoryFree="$(free -t -g | grep Total | awk '{printf("%'"'"'d",$4);}')"
cpu_model="$(grep -Po -m1 'model name\s*:\s\K.*$' /proc/cpuinfo)"
threads="$(grep -c processor /proc/cpuinfo)"
#gpu_model="$(glxinfo | grep -Po 'Device:\s*\K[a-zA-Z0-9 ]+')"
#gpu_total_memory="$(glxinfo | grep -Po 'Dedicated video memory:\s*\K[a-zA-Z0-9 ]+')"
#gpu_available_memory="$(glxinfo | grep -Po 'Total available memory  #:\s*\K[a-zA-Z0-9 ]+')"

message1='Welcome
'
message2='
                         |    |
,-.-.,---..    ,,---.,---|    |---.,   .    ,   .,---..   .
| | ||   | \  / |---'"'"'|   |    |   ||   |    |   ||   ||   |
` '"'"' '"'"'`---'"'"'  `'"'"'  `---'"'"'`---'"'"'    `---'"'"'`---|    `---|`---'"'"'`---'"'"'
                                   `---'"'"'    `---'"'"'
'

message3="
${CYAN}Hostname:${NONE} ${hostName}
${CYAN}Memory  :${NONE} ${memoryUsed} / ${memoryTotal} GB
${CYAN}CPU     :${NONE} ${cpu_model} | ${CYAN}THREADS     :${NONE} ${threads}

${RED}  ::::::::::::::::::::::::::::::::::-RULES-::::::::::::::::::::::::::::::::::${NONE}
${GREEN}     This is a private system that you are not to give out access to anyone
    without permission from the admin. No illegal files or activity. Stay,
    in your home directory, keep the system clean, and make regular backups.
     ${YELLOW}-==  DISABLE YOUR PROGRAMS FROM KEEPING SENSITIVE LOGS OR HISTORY ==-${NONE}

"

printf "${BLUE}${message1}"
printf "${LIGHT_RED}${message2}"
printf "${message3}"
EOF
chmod +x /etc/profile.d/banner

# Azure Cleanup
sudo rm -f /var/log/waagent.log
sudo cloud-init clean
waagent -force -deprovision+user

# Common cleanup
rm -f ~/.bash_history
export HISTSIZE=0

rm -f /var/lib/systemd/random-seed
rm -rf /root/anaconda-ks.cfg
rm -rf /root/install.log
rm -rf /root/install.log.syslog
rm -rf "/var/lib/yum/*"
rm -rf "/var/log/anaconda*"
rm -rf /var/log/yum.log

# Wipe machineid
cat /dev/null > /etc/machine-id

# Fix selinux
touch /var/log/cron
touch /var/log/boot.log
mkdir -p /var/cache/yum
/usr/sbin/fixfiles -R -a restore

# LVM Variant Fix, remove system.devices
rm -fv /etc/lvm/devices/system.devices

# Configure and enable the swap though zram
cat <<'EOF'> /etc/systemd/zram-generator.conf
[zram0]
zram-fraction=0.5
max-zram-size=4096
EOF
systemctl daemon-reload && systemctl enable --now /dev/zram0
poweroff
%end

%packages
@core
@headless-management
@server-product
acpid
automake
bzip2
cloud-init
cloud-utils-growpart
dhcp-client
dkms
dnf-utils
dnf-plugins-core
dracut-config-generic
epel-release
epel-next-release
firewalld
gcc
gcc-c++
gdisk
grub2
git
grub2-tools
hyperv-daemons
kernel
kernel-headers
kernel-devel
kmod
libcublas-devel-12-4-12.4.5.8
libcufile-devel-12-4-1.9.1.3
libglvnd-opengl
libglvnd-glx
libglvnd-devel
make
net-tools
NetworkManager
pciutils
pkgconfig
rng-tools
rsync
shadow-utils
sudo
tar
tuned
WALinuxAgent
yum-utils
zram-generator
-aic94xx-firmware
-alsa-firmware
-alsa-lib
-alsa-tools-firmware
-biosdevname
-iprutils
-ivtv-firmware
-iwl100-firmware
-iwl1000-firmware
-iwl105-firmware
-iwl135-firmware
-iwl2000-firmware
-iwl2030-firmware
-iwl3160-firmware
-iwl3945-firmware
-iwl4965-firmware
-iwl5000-firmware
-iwl5150-firmware
-iwl6000-firmware
-iwl6000g2a-firmware
-iwl6000g2b-firmware
-iwl6050-firmware
-iwl7260-firmware
-langpacks-*
-langpacks-en
-libertas-sd8686-firmware
-libertas-sd8787-firmware
-libertas-usb8388-firmware
-plymouth
-qemu-guest-agent

%end

Why are you trying to use a kickstart to make an ISO image? This sounds very counterproductive. Why not just include the kickstart on the ISO itself, which is the standard procedure? (Use mkksiso to do this)

Doing genisoimage or whatever you want to do against /mnt/sysimage is not going to get you the result you think you’re going to get.

If you are trying to make a “live image” of some special installation you want, then you’re going to need to do some extra work. See our workstation/kde/mate/etc kickstarts for an idea of what to do, and use livemedia-creator to make the ISO.

As you’ve pointed out, using the dnf commands will do this. dnf config-manager --add-repo. Or better yet, since the repo files are available from nvidia…

wget -O /etc/yum.repos.d/cuda-rhel9.repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo

If you are getting a network error, then your installation environment has no internet access and you need to remedy that.

The cloud-init package takes care of this step for us. Adding the rocky user before cloud-init causes strange issues that it’s better to let cloud-init do it.

We use passwd -d (to delete the password) and passwd -l (to lock the account) for root. See here. They are in our kickstarts.

livemedia-creator is specifically for live images, like our KDE or Workstation ISO’s. You are trying to fit our cloud image kickstarts into that mold, which won’t work without some modifications.


What this feels like is you have a specific goal in mind but you are going the wrong way about it. If you are trying to create an installable ISO image, then a live image is your best bet. Like I said above, you would need to do some extra modifications to make this work, and it’s really not that difficult to do. There are plenty of other kickstarts we have that show working examples.