Implementing UEFI support with Satellite 6.2
Satellite 6 does not currently support UEFI, but thanks to John Herr from Red Hat and the Foreman Hooks plug-in, it is possible to achieve some level of integration for bare-metal or virtualized provisioning. This tutorial is for Red Hat Enterprise Linux 7.1 running Satellite 6.1 and using Grub2 for PXE booting systems (instead of traditional PXELinux). This example configuration uses Satellite 6 with Capsule running on the same server. Required conditions:
- Satellite 6.2 running on Red Hat Enterprise Linux 7.1+
- Both DHCP and TFTP running on Satellite Server (not possible with external Capsule)
- Provisioned clients - RHEL7 (for RHEL6 filename options must point to Grub1 EFI loader instead - this can be only used in homogeneous environment e.g. either all clients RHEL7 or all clients RHEL6)
Satellite 6.3 is coming out with native UEFI support, therefore this workaround is not required anymore with this version.
INSTALL THE PACKAGES THAT CONTAIN THE EFI BOOT FILES
# yum -y install shim grub2-efi
COPY THE FILES TO THE /VAR/LIB/TFTPBOOT DIRECTORY
# cp /boot/efi/EFI/redhat/{shim,grubx64}.efi /var/lib/tftpboot
# cd /var/lib/tftpboot
# chcon --reference chain.c32 {shim,grubx64}.efi
CREATE THE UEFI BOOT MENU
This menu loads the configuration file from the /var/lib/tftpboot/uefi directory that matches the MAC address of the system being booted.
# echo 'configfile uefi/${net_default_mac}' > /var/lib/tftpboot/grub.cfg
# mkdir /var/lib/tftpboot/uefi
# setfacl -m u:foreman:rwx /var/lib/tftpboot/uefi
CONFIGURE DHCP
DHCP is configured to specify the value of "filename" according to the architecture option returned by the client. Edit the /etc/dhcp/dhcpd.conf file and change the PXE Handoff section to read as follows.
# PXE Handoff
next-server 1.2.3.4;
option architecture code 93 = unsigned integer 16 ;
if option architecture = 00:00 {
filename "pxelinux.0";
} elsif option architecture = 00:09 {
filename "shim.efi";
} elsif option architecture = 00:07 {
filename "shim.efi";
} elsif option architecture = 00:06 {
filename "shim.efi";
} else {
filename "pxelinux.0";
}
RESTART THE DHCPD DAEMON
# systemctl restart dhcpd
INSTALL THE HOOKS PLUG-IN
# yum -y install tfm-rubygem-foreman_hooks
# katello-service restart
In Satellite 6.1 the package is named rubygem-foreman_hooks.
INSTALL JGREP UTILITY
This small utility is used in the scripts, but it is not shipped with Satellite 6. You can install from Rubygems:
# gem install jgrep
Fetching: jgrep-1.4.1.gem (100%)
Successfully installed jgrep-1.4.1
Parsing documentation for jgrep-1.4.1
Installing ri documentation for jgrep-1.4.1
1 gem installed
# which jgrep
/usr/local/bin/jgrep
During Satellite upgrades, make sure the utility is working properly and reinstall if needed. To test if jgrep utility works try this:
# echo '{}' | jgrep
PREPARE THE ENVIRONMENT FOR THE HOOKS
# cd /usr/share/foreman/config/hooks/
# mkdir -p /usr/share/foreman/config/hooks/host/managed/{after_build,before_provision}
CREATE THE HOOK THAT WILL RUN AFTER THE HOST ENTERS BUILD MODE
Place the following in the /usr/share/foreman/config/hooks/host/managed/after_build/50-uefi.sh file
#!/bin/bash
# update, create, before_destroy, etc.
HOOK_EVENT=$1
# to_s representation of the object, e.g. host's fqdn
HOOK_OBJECT=$2
HOOK_OBJECT_FILE=$(mktemp -t foreman_hooks.XXXXXXXXXX)
trap "rm -f $HOOK_OBJECT_FILE" EXIT
cat > $HOOK_OBJECT_FILE
hook_data() {
if [ $# -eq 1 ]; then
jgrep -s "$1" < $HOOK_OBJECT_FILE
else
jgrep "$*" < $HOOK_OBJECT_FILE
fi
}
pxe_dir=/var/lib/tftpboot/pxelinux.cfg
uefi_dir=/var/lib/tftpboot/uefi
leases=/var/lib/dhcpd/dhcpd.leases
mac=$( hook_data host.mac )
dash_mac=$( hook_data host.mac | tr ':' '-' )
name=$( hook_data host.name )
sed -i "/supersede server.filename/d" ${leases}
ks_info=$( sed -n 's/.*\sks=\(.*\)/\1/p' ${pxe_dir}/??-${dash_mac} )
kernel=$( sed -n 's/\s*kernel\s*\(.*\)/\1/p' ${pxe_dir}/??-${dash_mac} )
initrd=$( sed -n 's/.*\sinitrd=\(.*\)/\1/p' ${pxe_dir}/??-${dash_mac} | sed 's/\s.*//' )
[[ ! -d ${uefi_dir} ]] && mkdir -p ${uefi_dir}
cat << EOF > ${uefi_dir}/${mac}
set timeout=5
menuentry 'Provision ${name}' {
linuxefi ${kernel} ip=dhcp ks=${ks_info}
initrdefi ${initrd}
}
EOF
echo "UEFI HOOK: deployed ${uefi_dir}/${mac}"
Test the hook first simulating deployment of fake host named "test" with MAC address AA:BB:CC:DD:EE:FF:
# cat >/var/lib/tftpboot/pxelinux.cfg/01-AA-BB-CC-DD-EE-FF <<FAKE
DEFAULT linux
LABEL linux
KERNEL boot/vmlinuz
APPEND initrd=boot/initrd.img ks=http://fake/unattended/provision?token=b9a9bacd-28ab-42a9-8266-27996eaee322 network ks.sendmac
IPAPPEND 2
FAKE
# /usr/share/foreman/config/hooks/host/managed/after_build/50-uefi.sh create test
{"host": {"mac": "AA:BB:CC:DD:EE:FF", "name": "test"}}
CTRL+D
UEFI HOOK: deployed /var/lib/tftpboot/uefi/AA:BB:CC:DD:EE:FF
There should be no errors in the above command, except the "deployed" message. The following file should be created, it can be removed now:
# cat /var/lib/tftpboot/uefi/AA:BB:CC:DD:EE:FF
set timeout=5
menuentry 'Provision test' {
linuxefi ip=dhcp ks=http://fake/unattended/provision?token=b9a9bacd-28ab-42a9-8266-27996eaee322 network ks.sendmac
initrdefi boot/initrd.img
}
# rm /var/lib/tftpboot/uefi/AA:BB:CC:DD:EE:FF
CREATE THE HOOK THAT WILL RUN AFTER THE HOST IS INSTALLED
Place the following in the /usr/share/foreman/config/hooks/host/managed/before_provision/60-uefi-after.sh file
#!/bin/bash
# update, create, before_destroy etc.
HOOK_EVENT=$1
# to_s representation of the object, e.g. host's fqdn
HOOK_OBJECT=$2
HOOK_OBJECT_FILE=$(mktemp -t foreman_hooks.XXXXXXXXXX)
trap "rm -f $HOOK_OBJECT_FILE" EXIT
cat > $HOOK_OBJECT_FILE
cp $HOOK_OBJECT_FILE /tmp/out.after.object
hook_data() {
if [ $# -eq 1 ]; then
jgrep -s "$1" < $HOOK_OBJECT_FILE
else
jgrep "$*" < $HOOK_OBJECT_FILE
fi
}
pxe_dir=/var/lib/tftpboot/pxelinux.cfg
uefi_dir=/var/lib/tftpboot/uefi
mac=$( hook_data host.mac )
dash_mac=$( hook_data host.mac | tr ':' '-' )
name=$( hook_data host.name )
cat << EOF > ${uefi_dir}/${mac}
set timeout=5
menuentry 'Localboot ${name}' {
insmod part_msdos
insmod chain
configfile (hd0,gpt1)/efi/redhat/grub.cfg
}
EOF
echo "UEFI HOOK: deployed ${uefi_dir}/${mac}"
Test it the similar way:
# /usr/share/foreman/config/hooks/host/managed/before_provision/60-uefi-after.sh create test
{"host": {"mac": "AA:BB:CC:DD:EE:FF", "name": "test"}}
UEFI HOOK: deployed /var/lib/tftpboot/uefi/AA:BB:CC:DD:EE:FF
# cat /var/lib/tftpboot/uefi/AA:BB:CC:DD:EE:FF
set timeout=5
menuentry 'Localboot test' {
insmod part_msdos
insmod chain
configfile (hd0,gpt1)/efi/redhat/grub.cfg
}
# rm /var/lib/tftpboot/uefi/AA:BB:CC:DD:EE:FF
Set the file permissions and correct SELinux file contexts:
# chmod 755 /usr/share/foreman/config/hooks/host/managed/after_build/50-uefi.sh
# chmod 755 /usr/share/foreman/config/hooks/host/managed/before_provision/60-uefi-after.sh
# restorecon -RvvF /usr/share/foreman/config/hooks
Because the hooks require permission to modify the /var/lib/dhcpd.leases file, set a facl to allow it:
# setfacl -R -m u:foreman:rwx /var/lib/dhcpd
When a new host is created and enters "build mode" in Satellite 6, the PXE configuration on the TFTP configuration is modified to use Grub2 for both UEFI and non-UEFI systems instead of the default PXELinux (which does not support UEFI yet).
Comments
should be 'katello-service restart' not 'foreman' There is no Foreman service on Satellite 6.
Hi Lukas,
unfortunately this doesn't work for us. The Client gets stucked in the grup prompt aufter loading shim.efi and grubx64.efi.
It seems that grub.cfg couldn't be read. These files are all in the same directory.
Any idea whats going wrong?
thanks Torsten
Hello, check permissions of grub.cfg as well as SELinux context and denials and system logs. Increase verbosity of the TFTP server to see more.
thanks for quick response.
selinux is on permissive mode. grub.cfg has 644 permissions.
tftpd Output:
Aug 28 14:32:01 lsins01p xinetd[3135]: START: tftp pid=6939 from=10.144.25.110
Aug 28 14:32:01 lsins01p in.tftpd[6940]: RRQ from 10.144.25.110 filename shim.efi
Aug 28 14:32:01 lsins01p in.tftpd[6940]: tftp: client does not accept options
Aug 28 14:32:01 lsins01p in.tftpd[6941]: RRQ from 10.144.25.110 filename shim.efi
Aug 28 14:32:02 lsins01p in.tftpd[6942]: RRQ from 10.144.25.110 filename grubx64.efi
Looks all good but no request for grub.cfg !?
Hi.
I face the same issue.
I have fixed the selinux issue with the follwoing commands.
chown foreman:foreman /var/lib/tftpboot/uefi/
chcon system_u:object_r:passenger_tmp_t:s0 /var/lib/tftpboot/uefi/
thanks Aleks, but doesn't work for me :-(
I guess we're running in this problem: http://lists.gnu.org/archive/html/grub-devel/2012-04/msg00136.html
On grub prompt Iam getting this error: timeout: could not resolve Hardware address
But net_ls_addr shows mac and ip !
dear Torsten.
I have not even a timout. Grub cli waits.
When I type in boot i get
error: you Need to load a kernel first.
I have the same exact issue.
Basically if I use the grub mentioned in this doc (grubx64.efi), it works well on a HP server (a DL360 Gen9) but the same grub doesn't boot a virtual machine on an up to date ESX: the VM gets stuck and I don't even get a grub prompt.
While with an old grub 1 (grub-x86_64.efi compiled on a Fedora 15, grub version 0.97-69.fc15), the VM boots but not the hardware.
A tcpdump shows that with the latter file, the HP server doesn't even try to request a grub.cfg config file.
Bottom line, efi booting with pxe is a mess because at the moment, it seems I need at least two versions of grub to boot just one kind of server and one kind of virtual machine.
In the case of a VMWare virtual machine, would it be a feasible workaround to boot it in BIOS (legacy) mode? If not, please do file a ticket with Red Hat support including the detail of your setup.
ok, I am getting the error running:
grub> linuxefi boot/RedHat-7.1-x86_64-vmlinuz
error: timeout: could not resolve Hardware address
net_ls_addr Looks good:
grub> net_ls_addr
efinet0 <my-mac> <my-ip>
With tcpdump filtering with the ether-addr of my Client I can see this:
17:11:28.615521 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has defaultrouter.maydomain tell myclient.mydomain , length 46
Which means, the grub-Client is asking for the mac-addr of the defaultrouter.
And now???
do you have in your /var/lib/tftpboot/uefi/<MAC> a value after ks= ?
Do you have a /var/lib/tftpboot/pxelinux.cfg/<MAC> with '-' ?
I have not the second file
Please add in the document that you nee to install jgrep
http://jgrep.org/#install
yum install http://yum.theforeman.org/plugins/1.11/el7/x86_64/tfm-rubygem-jgrep-1.3.3-7.el7.noarch.rpm
Thanks I will add this.
Iam stucked earlier.
grubx64.efi is loaded and after this IP connectivity doesn't work.
So I can't ping the client IP. But output of net_ls_addr and net_ls_routes in grub looks ok.
I think thats the Problem and so tftp for grub.cfg doesn't work.
Our VMs running on Hyper-V. Any experience with that?
Hi Torsten,
I work together with Aleks on this.
We indeed are using Hyper-V too!
Are you using Gen1 or Gen2 VMs?
Hi Udo,
ok, good to hear that we're not alone ;-)
We're using Gen2 VMs.
Any special paramter we should set for the VMs?
thanks
Hi Torsten.
do you have the possibility to use bare metal or vmware with uefi?
We will also try to be able to try it.
br aleks
unfortunately I have no esx host or bare metal possibilities.
Do you have also HyperV Gen2?
And which Version of grubx64.efi / shim.efi ?
I have:
-rwxr-xr-x. 1 root root 1069520 Aug 31 07:55 grubx64.efi
-rwxr-xr-x. 1 root root 1285808 Aug 31 07:18 shim.efi
comming from rpm: grub2-efi-2.02-0.16.el7.x86_64 and shim-0.7-8.el7_0.x86_64
thanks,
Torsten
Yes HyperV Gen2 without secure boot.
ls -lart {grubx64.efi,shim.efi}
-rw-r--r--. 1 root root 1285808 Aug 28 10:58 shim.efi
-rw-r--r--. 1 root root 1069520 Aug 28 10:58 grubx64.efi
rpm -qa|egrep 'efi|shim'
efivar-libs-0.11-1.el7.x86_64
efibootmgr-0.8.0-5.el7.x86_64
shim-0.7-8.el7_0.x86_64
shim-unsigned-0.7-8.el7_0.x86_64
grub2-efi-2.02-0.16.el7.x86_64
your files looks younger
hm, but filesize and rpm Versions are the same.
I guess the root cause is that my grub has no ip connectivity. net_ls_addr tells me the right IP, but I can't ping it (from the same subnet).
This is strange:
net_ls_addr: efinet0 <mymac> <myip>
I delete it: net_del_addr efinet 0
net_ls_addr is empty
Than I cant add it again:
net_add_addr efinet0 <myip> <mynetmask>
error: Card not found !?
But net_ls_cards shows me efinet0 !
There is something wrong with the IP stack in grubx64.efi or doesn't work with HyperV !?
How can I debug this with grub?
good question. I don't know ;-/
There currently seem to be a problem with GRUB2 and it's networking ability on at least the HyperV gen2 platform. The symptoms are typically that GRUB loads and you get a GRUB prompt but the config file won't load. Or anything else for that matter either.
Inspecting the network traffic reveals some ARP queries, but no response.
This issue is actively being worked on but if you do encoutner the issue, please file a ticket with Red Hat Support.
I started working on official UEFI support in Foreman / Satellite. We are tracking the request as https://bugzilla.redhat.com/show_bug.cgi?id=1145653 and the initial design is available at http://projects.theforeman.org/projects/foreman/wiki/PXE_Booting_UEFI
Hello,
if you have problems with VMWare VMs, try to downgrade the menu.c32 to version from 3.x: https://github.com/cobbler/cobbler.github.io/tree/master/loaders -> https://github.com/cobbler/cobbler.github.io/blob/master/loaders/menu.c32-3.86
Instead of modifying leases file (the filename change) which looks quite dirty, the script could use omapi command line tool to override the DHCP lease with correct entries. No confiration reload (dhcpd restart) is needed then for each reservation made.
Has anyone figured out how to make iPXE work with UEFI? My customer is requiring it and there doesn't seem to be a clean way to make iPXE work with it.
UEFI support in iPXE upstream project is quite fresh, we expect issues with it and nobody tested it so far (from the engineering team AFAIK). We offer PXE-less discovery instead in Satellite 6.1.5+ which is UEFI/BIOS ready and tested. It offers similar functionality as Bootdisk.
This workaround will work for Satellite 6.2, the filename of hooks plugin have changed tho:
Relevant KBASE article: https://access.redhat.com/solutions/2674001
An alternative way of installing JGrep is simply to install it from rubygems:
gem install jgrep
This installs into /usr/local/bin. During Satellite Server upgrades, this gem needs to be reinstalled!
hi lukas,
i did a local install of jgrep, did work fine via command line execution, but not at all if executed via foreman hooks, stripped it completely and replaced it by "simple" sed regexp. also had to sanitize the dhcpd static lease configuration, as the MS HyperV Gen2 TFTP client was very picky and just stopped working on unknown DHCP parameters.
so, for anyone unable to wait for satellite 6.3, this worked for me:
$ cat /usr/share/foreman/config/hooks/host/managed/after_build/50-uefi.sh;
!/bin/bash update, create, before_destroy, etc.PXE_DIR=/var/lib/tftpboot/pxelinux.cfg UEFI_DIR=/var/lib/tftpboot/uefi LEASES=/var/lib/dhcpd/dhcpd.leases DATE="$( date +%Y%m%d%H%M%S )"; LOG="/var/log/foreman/hooks.log";
echo "${DATE}: UEFI HOOK: started: $*" | tee -a "${LOG}";
HOOK_EVENT=$1
to_s representation of the object, e.g. host's fqdnHOOK_OBJECT=$2 HOOK_OBJECT_FILE=$(mktemp -t foreman_hooks.XXXXXXXXXX) trap "rm -f $HOOK_OBJECT_FILE" EXIT cat > $HOOK_OBJECT_FILE HOOK_OBJECT_VARS="$( < ${HOOK_OBJECT_FILE} )";
echo "${HOOK_OBJECT_VARS}" | sed 's%."mac":"([^"]).%\1%' | tr [A-Z] [a-z] | tee -a "${LOG}"; NAME="$( echo "${HOOK_OBJECT_VARS}" | sed 's%."name":"([^"]).%\1%' | tr [A-Z] [a-z] )"; MAC="$( echo "${HOOK_OBJECT_VARS}" | sed 's%."mac":"([^"]).*%\1%' | tr [A-Z] [a-z] )"; DASH_MAC="$( echo "${MAC}" | tr ':' '-' )";
Microsoft HyperV UEFI PXE DHCP option sanitation: sed -i '/supersede server.filename\s=./d' ${LEASES};sed -i 's%supersede server.filename\s=.%supersede server.filename = "shim.efi";%' ${LEASES}; sed -i '/^.server.next-server\s=.*/d' ${LEASES}; sudo /usr/bin/systemctl restart dhcpd;
KS_INFO=$( sed -n 's/.\sks=(.)/\1/p' ${PXE_DIR}/??-${DASH_MAC} ) KERNEL=$( sed -n 's/\sKERNEL\s(.)/\1/p' ${PXE_DIR}/??-${DASH_MAC} ) INITRD=$( sed -n 's/.\sinitrd=(.)/\1/p' ${PXE_DIR}/??-${DASH_MAC} | sed 's/\s.//' )
cat << EOF > ${UEFI_DIR}/${MAC} set timeout=8 menuentry 'Provision ${NAME}' { linuxefi ${KERNEL} ip=dhcp ks=${KS_INFO} initrdefi ${INITRD} } EOF echo "${DATE}: UEFI HOOK: deployed ${UEFI_DIR}/${MAC}" | tee -a "${LOG}";
cat "/usr/share/foreman/config/hooks/host/managed/before_provision/60-uefi-after.sh";
!/bin/bash before_provisionPXE_DIR=/var/lib/tftpboot/pxelinux.cfg UEFI_DIR=/var/lib/tftpboot/uefi LEASES=/var/lib/dhcpd/dhcpd.leases DATE="$( date +%Y%m%d%H%M%S )"; LOG="/var/log/foreman/hooks.log";
echo "${DATE}: UEFI HOOK: started: $*" | tee -a "${LOG}";
HOOK_EVENT=$1
to_s representation of the object, e.g. host's fqdnHOOK_OBJECT=$2 HOOK_OBJECT_FILE=$(mktemp -t foreman_hooks.XXXXXXXXXX) trap "rm -f $HOOK_OBJECT_FILE" EXIT cat > $HOOK_OBJECT_FILE HOOK_OBJECT_VARS="$( < ${HOOK_OBJECT_FILE} )";
echo "${HOOK_OBJECT_VARS}" | sed 's%."mac":"([^"]).%\1%' | tr [A-Z] [a-z] | tee -a "${LOG}"; NAME="$( echo "${HOOK_OBJECT_VARS}" | sed 's%."name":"([^"]).%\1%' | tr [A-Z] [a-z] )"; MAC="$( echo "${HOOK_OBJECT_VARS}" | sed 's%."mac":"([^"]).*%\1%' | tr [A-Z] [a-z] )"; DASH_MAC="$( echo "${MAC}" | tr ':' '-' )";
Microsoft HyperV UEFI PXE DHCP option sanitation: sed -i '/supersede server.filename\s=./d' ${LEASES};sed -i 's%supersede server.filename\s=.%supersede server.filename = "shim.efi";%' ${LEASES}; sed -i '/^.server.next-server\s=.*/d' ${LEASES}; sudo /usr/bin/systemctl restart dhcpd;
KS_INFO=$( sed -n 's/.\sks=(.)/\1/p' ${PXE_DIR}/??-${DASH_MAC} ) KERNEL=$( sed -n 's/\sKERNEL\s(.)/\1/p' ${PXE_DIR}/??-${DASH_MAC} ) INITRD=$( sed -n 's/.\sinitrd=(.)/\1/p' ${PXE_DIR}/??-${DASH_MAC} | sed 's/\s.//' )
cat << EOF > ${UEFI_DIR}/${MAC} set timeout=8 menuentry 'Localboot ${NAME}' { insmod part_msdos insmod chain configfile (hd0,gpt1)/efi/redhat/grub.cfg } EOF echo "${DATE}: UEFI HOOK: local switch ${UEFI_DIR}/${MAC}" | tee -a "${LOG}";
[root@rsatp0v01 ~]# echo 'foreman ALL = (root) NOPASSWD : /usr/bin/systemctl restart dhcpd' > /etc/sudoers.d/foreman-uefi-hook; [root@rsatp0v01 ~]# touch /var/log/foremanxy/hooks.log; [root@rsatp0v01 ~]# chown 'foreman:foreman' /var/log/foreman/hooks.log; [root@rsatp0v01 ~]# chown 'foreman:foreman' /usr/share/foreman/config/hooks/host/managed/after_build/50-uefi.sh; [root@rsatp0v01 ~]# mkdir /usr/share/foreman/config/hooks/host/managed/create/; [root@rsatp0v01 ~]# cp -ai /usr/share/foreman/config/hooks/host/managed/after_build/50-uefi.sh /usr/share/foreman/config/hooks/host/managed/create/; [root@rsatp0v01 ~]# systemctl restart 'httpd'; [root@rsatp0v01 ~]# chown 'foreman:foreman' /var/lib/tftpboot/uefi/;
Will this process work for satellite 6.2.13 without tftp, dhcp or pxe?