Most sysadmins are used to dealing with base, guest, or gold images to provision new virtual machines (VM) or cloud instances in their traditional virtualization or cloud environments. The appeal of using these images is their slim size, standardization, simplicity, and basic configurations, from which it is possible to perform pre- or post-provisioning customization. Much of the customization takes place post-provisioning.
However, in some scenarios, admins must customize certain images in advance to adhere to company policies, such as ensuring that certain packages or tools are available at deployment time, setting security restrictions, or even making small tweaks that make server administration easier. There are different tools with different scopes to achieve this goal, such as diskimage-builder, virt-customize, cloud-init, and others. But if you only need to make minor adjustments to your image, the guestfish tool is a time saver.
I'll explain how to use this simple and versatile tool to make small customizations.
Setting the prerequisites
According to the official documentation, "guestfish is a shell and command-line tool for examining and modifying virtual machine filesystems. It uses libguestfs and exposes all of the functionality of the guestfs API."
For this demonstration, I am using a small KVM server with the
libguestfs-tools package installed because it provides the
$ rpm -qa | grep libguestfs-tools libguestfs-tools-c-1.40.2-27.module+el8.4.0+9282+0bdec052.x86_64 libguestfs-tools-1.40.2-27.module+el8.4.0+9282+0bdec052.noarch
I'll also download the Red Hat Enterprise Linux 8.4 Update KVM Guest Image from the Red Hat Customer Portal to the same machine:
$ qemu-img info rhel-8.4-x86_64-kvm.qcow2 image: rhel-8.4-x86_64-kvm.qcow2 file format: qcow2 virtual size: 10 GiB (10737418240 bytes) disk size: 694 MiB cluster_size: 65536 Format specific information: compat: 0.10 refcount bits: 16 $ qemu-img measure rhel-8.4-x86_64-kvm.qcow2 required size: 10737418240 fully allocated size: 10737418240
For this demonstration, I created the following pre-configuration prerequisites that I want my image to have post-deployment:
- The root user must have password 123456.
- There must be a common user named alexon with password 123456 with sudo privileges.
- It should have the hostname custom-server.example.com.
- It must use a static IP configuration of 192.168.100.3 in the 192.168.100.x network, with 192.168.100.1 as the gateway, and 192.168.1.3 as the DNS server.
- It should show a custom message of the day at login.
- A custom tool should be available in the
So, I'll get down to business and customize my image with these settings.
[ Learn more in the free eBook Red Hat OpenShift and Kubernetes ... what's the difference? ]
Make image customizations
Guestfish has its own shell and command line that you can access by running
guestfish. You can check the available commands inside this shell and execute every change you want in your image, or you can use
guestfish directly from the operating system's shell with the desired parameters, even as a scripting tool. For a complete list of acceptable parameters and commands, check the official documentation:
$ sudo guestfish Welcome to guestfish, the guest filesystem shell for editing virtual machine filesystems and disk images. Type: 'help' for help on commands 'man' to read the manual 'quit' to quit the shell ><fs> help Add disk images to examine using the '-a' or '-d' options or the 'add' command. Or create a new disk image using '-N', or the 'alloc' or 'sparse' commands. Once you have done this, use the 'run' command. For more information about a command, use 'help cmd'. To read the manual, type 'man'. ><fs> quit
Before proceeding with the customization, create an encrypted and hashed password for the root user to use inside the image:
$ openssl passwd -1 123456 $1$9pskY4to$DQT/NOOjQT7E.t.NKIzJr0
Next, edit the image. You can add read-write permissions, enable networking, and automatically mount filesystems:
$ sudo guestfish --rw --network -i -a rhel-8.4-x86_64-kvm.qcow2 Welcome to guestfish, the guest filesystem shell for editing virtual machine filesystems and disk images. Type: 'help' for help on commands 'man' to read the manual 'quit' to quit the shell Operating system: Red Hat Enterprise Linux 8.4 (Ootpa) /dev/sda3 mounted on / /dev/sda2 mounted on /boot/efi ><fs> quit
But to better demonstrate how image manipulation works inside the guestfish shell (and for fun), add the image with read-write permissions and do the rest inside it. (Note: The image cannot be used by any running VM or instance at this time):
$ sudo guestfish -w -a rhel-8.4-x86_64-kvm.qcow2 Welcome to guestfish, the guest filesystem shell for editing virtual machine filesystems and disk images. Type: 'help' for help on commands 'man' to read the manual 'quit' to quit the shell ><fs> set-network true ><fs> run 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00 ><fs> list-filesystems /dev/sda1: unknown /dev/sda2: vfat /dev/sda3: xfs ><fs> mount /dev/sda3 /
Remember that encrypted password you created for the root user? It's time to use it inside the
><fs> vi /etc/shadow [...] root:$1$9pskY4to$DQT/NOOjQT7E.t.NKIzJr0:18367:0:99999:7::: [...]
Write the custom message of the day to the
><fs> cat /etc/motd ><fs> write /etc/motd "Demo Server customized with guestfish for Enable SysAdmin" ><fs> chmod 0644 /etc/motd ><fs> cat /etc/motd Demo Server customized with guestfish for Enable Sysadmin
Set the hostname inside the
><fs> write /etc/hostname "custom-server.example.com" ><fs> cat /etc/hostname custom-server.example.com
/etc/sysconfig/network-scripts/ifcfg-eth0 should be defined with the required networking information:
><fs> ls /etc/sysconfig/network-scripts/ ><fs> touch /etc/sysconfig/network-scripts/ifcfg-eth0 ><fs> edit /etc/sysconfig/network-scripts/ifcfg-eth0 [...] TYPE=Ethernet PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no NAME=eth0 DEVICE=eth0 ONBOOT=yes IPADDR=192.168.100.3 PREFIX=24 GATEWAY=192.168.100.1 DNS1=192.168.1.3 DOMAIN=example.local IPV6_PRIVACY=no [...]
There is a tarball with a custom tool in the KVM host machine. Import and extract its contents to the
/root directory of the image:
><fs> tar-in /tmp/images/mycustom-tools.tar /root/ ><fs> ls /root/ .bash_logout .bash_profile .bashrc .cshrc .tcshrc mycustom-tools ><fs> ls /root/mycustom-tools/ tool.sh
Create the required common user using system commands, as follows:
><fs> command "adduser -G wheel -p 123456 -c 'Alexon Oliveira' alexon" ><fs> cat /etc/passwd | grep alexon alexon:x:1000:1000:Alexon Oliveira:/home/alexon:/bin/bash
Finally, to avoid problems with SELinux contexts, you must run a relabel and sync the disk so that any writes are flushed through to the underlying disk image. After that, you can exit the image edition:
><fs> selinux-relabel /etc/selinux/targeted/contexts/files/file_contexts / ><fs> sync ><fs> exit
Check that the image size has barely changed:
$ qemu-img info rhel-8.4-x86_64-kvm.qcow2 image: rhel-8.4-x86_64-kvm.qcow2 file format: qcow2 virtual size: 10 GiB (10737418240 bytes) disk size: 788 MiB cluster_size: 65536 Format specific information: compat: 0.10 refcount bits: 16 $ qemu-img measure rhel-8.4-x86_64-kvm.qcow2 required size: 10737418240 fully allocated size: 10737418240
Test the customized image
Did all the effort work out? I'll put it to the test. I'll use Cockpit machines to create a virtual machine from the customized image:
After creating the virtual machine, check that it is running:
The virtual machine is functional and ready for access:
The first test is to validate that the static IP was set correctly and that I can access the deployed server with the pre-created common user and its password:
$ ssh email@example.com The authenticity of host 192.168.100.3 can't be established. ECDSA key fingerprint is SHA256:7mBMhpf+t7Ip[...]A6R+TkIqvrDiC04wI. Are you sure you want to continue connecting? yes firstname.lastname@example.org's password: Demo Server customized with guestfish for Enable SysAdmin Activate the web console with systemctl enable --now cockpit.socket Last login: Mon Nov 1 17:25:47 2021
Bingo! And I also can see the custom message of the day at login. What about the custom tool? Here it is:
[alexon]$ sudo ls /root/ We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things: #1) Respect the privacy of others. #2) Think before you type. #3) With great power comes great responsibility. [sudo] password for alexon: mycustom-tools
The networking configuration is set properly:
[alexon]$ ip a show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:1e:b7:89 brd ff:ff:ff:ff:ff:ff inet 192.168.100.3/24 brd 192.168.100.255 scope global noprefixroute eth0 valid_lft forever preferred_lft forever inet6 fe80::5054:ff:fe1e:b789/64 scope link noprefixroute valid_lft forever preferred_lft forever [alexon]$ ip r default via 192.168.100.1 dev eth0 proto static metric 100 192.168.100.0/24 dev eth0 proto kernel scope link src 192.168.100.3 metric 100
The DNS server is set correctly, too:
[alexon]$ cat /etc/resolv.conf # Generated by NetworkManager search example.local nameserver 192.168.1.3
Also, the hostname is the one I defined earlier:
[alexon]$ hostnamectl Static hostname: custom-server.example.com Icon name: computer-vm Chassis: vm Machine ID: 53b979b8960b45af9ba5cdd94b14cb6b Boot ID: aa3622b750a64047821290e96b05126d Virtualization: kvm Operating System: Red Hat Enterprise Linux 8.4 (Ootpa) CPE OS Name: cpe:/o:redhat:enterprise_linux:8.4:GA Kernel: Linux 4.18.0-305.el8.x86_64 Architecture: x86-64
Last but not least, I'll test the root access with the password set above:
[alexon]$ id alexon uid=1000(alexon) gid=1000(alexon) groups=1000(alexon),10(wheel) [alexon]$ su - Password: Last login: Mon Nov 1 17:35:58 EST 2021 from 192.168.100.1 on pts/0 [root]# whoami root
And just like that, I used a simple but powerful tool to do minor tweaking in the base image and deployed a new server from it, and I can deploy many other servers, too. I can use other utilities together with the image to leverage these customizations. But for small tweaks, guestfish serves administrators well.
When working with base images for deployment in virtualization and cloud environments, especially in KVM and OpenStack, certain customizations are required to meet company policies. For minor customizations, the guestfish tool is a great ally and to help you deliver tailored services. Now that you know how to do it, put it to good use.