Skip to main content

How to customize VM and cloud images with guestfish

Need to tweak your cloud and virtual machine images to comply with company policies or other requirements? Give guestfish a try.
Image
How to capture terminal sessions using the script commandPhoto by Sora Shimazaki from Pexels

Photo by Sora Shimazaki from Pexels

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 guestfish command:

$ 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 /root directory.

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 /etc/shadow file:

><fs> vi /etc/shadow
[...]
root:$1$9pskY4to$DQT/NOOjQT7E.t.NKIzJr0:18367:0:99999:7:::
[...]

Write the custom message of the day to the /etc/motd file:

><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 /etc/hostname file:

><fs> write /etc/hostname "custom-server.example.com"
><fs> cat /etc/hostname
custom-server.example.com

The /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:

Image
import VM via Cockpit
(Alexon Oliveira CC BY-SA 4.0)

After creating the virtual machine, check that it is running:

Image
Running VM in Cockpit
(Alexon Oliveira CC BY-SA 4.0)

The virtual machine is functional and ready for access:

Image
access the VM via Cockpit
(Alexon Oliveira CC BY-SA 4.0)

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 alexon@192.168.100.3
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
alexon@192.168.100.3'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.

Wrap up

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.

Topics:   Virtualization   Cloud   Linux administration  
Author’s photo

Alexon Oliveira

Alexon has been working as a Senior Technical Account Manager at Red Hat since 2018, working in the Customer Success organization focusing on Infrastructure and Management, Integration and Automation, Cloud Computing, and Storage Solutions. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.