Skip to content

Instantly share code, notes, and snippets.

@tdewin
Last active September 10, 2025 13:20
Show Gist options
  • Select an option

  • Save tdewin/17503cdc79cbad61422d59dc6f5788cd to your computer and use it in GitHub Desktop.

Select an option

Save tdewin/17503cdc79cbad61422d59dc6f5788cd to your computer and use it in GitHub Desktop.
VeeamSoftwareAppliance on Fedora with virsh

This is a set of script to automatically deploy some of the components via KVM/virsh

Tested on Fedora 42

echo "deleting $1"
virsh undefine $1 && virsh undefine $1 --nvram --remove-all-storage
#!/bin/bash
RNAME=$1
echo "Executing QR Magic on $RNAME"
SEC=$(date +%s)
HUMAN=1
virsh send-key --domain $RNAME KEY_ENTER
sleep $HUMAN
virsh send-key --domain $RNAME KEY_ENTER
sleep $(($HUMAN*2))
virsh screenshot $RNAME --file qr.png && zbarimg qr.png | grep otpauth > authcode-$RNAME-$SEC.txt
TOTPCODE=$(python3 -c "import oathtool;print(oathtool.generate_otp('$(cat authcode-$RNAME-$SEC.txt | cut -f2 -d=)'))")
sleep $HUMAN
virsh send-key --domain $RNAME KEY_ESC
sleep $HUMAN
echo "Sending $TOTPCODE"
for (( i=0; i<${#TOTPCODE}; i++ )); do
virsh send-key --domain $RNAME --codeset linux --holdtime 500 KEY_LEFTSHIFT "KEY_${TOTPCODE:$i:1}"
sleep $HUMAN
done
sleep $(($HUMAN*2))
virsh send-key --domain $RNAME KEY_ENTER
sudo dnf install zbar-libs zbar
mkdir -p oathtool && curl -o oathtool/__init__.py  https://raw.githubusercontent.com/jaraco/oathtool/refs/heads/main/oathtool/__init__.py
sh qrmagic.sh $RNAME
#!/bin/sh
#$1 < should be the file that was created earlier eg authcode-<vm>-<utime>
#$2 < should be the name of the vm, eg repo002
RNAME=$2
HUMAN=1
TOTPCODE=$(python3 -c "import oathtool;print(oathtool.generate_otp('$(cat $1 | cut -f2 -d=)'))")
echo "Sending $TOTPCODE"
for (( i=0; i<${#TOTPCODE}; i++ )); do
virsh send-key --domain $RNAME --codeset linux --holdtime 500 KEY_LEFTSHIFT "KEY_${TOTPCODE:$i:1}"
sleep $HUMAN
done
sleep $(($HUMAN*2))
virsh send-key --domain $RNAME KEY_ENTER

Setting up network

First we will set up a bridged network. This is not required and you can skip a lot of the config if you don't do it but this way VBR will be available on the network

Use nmcli to show your current network adapter. Since we will move this adapater, you might want to run all of these commands localy and not over ssh

nmcli connection show
sudo nmcli connection add type bridge con-name br0 ifname br0
sudo nmcli connection modify br0 connection.autoconnect yes
sudo nmcli connection add type bridge-slave con-name br0-slave-eth ifname <yournetadapaterfromnmcli>  master br0
sudo nmcli connection modify br0-slave-eth connection.autoconnect yes

sudo nmcli connection delete "Wired connection 1"
sudo nmcli connection up br0

Now will need to disable complete filtering on the bridge so the VMs get all the traffic

cat <<EOF | sudo tee -a /etc/sysctl.d/99-netfilter-bridge.conf
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
EOF
cat <<EOF | sudo tee -a /etc/modules-load.d/br_netfilter.conf 
br_netfilter
EOF

If you reboot now, everything should be set, for the bridge but if you don't want to reboot, you can load everything dynamically

sudo modprobe br_netfilter
sudo sysctl -p /etc/sysctl.d/99-netfilter-bridge.conf
``

Now we need to allow qemu to use this bridge
```bash
echo "allow br0" | sudo tee -a /etc/qemu/bridge.conf 

Finally we can define the network at the hypervisor level

cat <<EOF | tee -a bridgenet.xml
<network>
    <name>br0</name>
    <forward mode="bridge" />
    <bridge name="br0" />
</network>
EOF
virsh net-define bridgenet.xml
virsh net-start br0
virsh net-autostart br0

Setting up the VM

Well this is the easy part since we use a tool called virt-v2v. You do need to install it though. Then you can convert the appliance

Notice that I'm importing into the gnome-boxes storage pool. This is the default for Fedora 42 (latest in 09/2025)

sudo yum install virt-v2v -y
virt-v2v -i ova VeeamSoftwareAppliance_13.0.0.4967_20250822.ova -of qcow2\
 --os gnome-boxes --bridge br0 --on vbr

Fedora also doesn't like the "VNC" protocol and there seems no way to change this with virt-v2v. We will just dump the config, remove the vm, update the config and redefine it

virsh dumpxml vbr  > vbr.xml
xmllint vbr.xml --shell <<EOF
cd //graphics/@type
set spice
save
bye
EOF
virsh undefine --domain vbr --nvram

virsh define vbr.xml 
virsh start vbr

If you now open up gnome boxes, you will see VBR running on your main network

Alternatively, use the iso

sudo dnf install virt-install -y

If you want the data disk on another drive set (eg /dev/sdb mounted to /mnt/backup)

sudo mkdir /mnt/backup/$USER
sudo chown $USER:$USER /mnt/backup/$USER
mkdir /mnt/backup/$USER/data
virsh pool-define-as dataimages dir - - - - /mnt/backup/$USER/data
virsh pool-build dataimages
virsh pool-autostart dataimages

Download the VeeamSoftwareAppliance iso in /home/$USER/iso. We copy the vbr kickstart to modify it and update it on the fly

This just changes the timezone and the keyboard but of course you could also inject the network settings

ISOPATH=/home/$USER/iso 
cd $ISOPATH
mkdir tmp
sudo mount VeeamSoftwareAppliance*.iso tmp
cat tmp/vbr-ks.cfg > vbr-ks-mod.cfg
sed -i 's/keyboard.*/keyboard --xlayouts='"'"be"'"'/' vbr-ks-mod.cfg
sed -i 's/^timezone.*/timezone Europe\/Brussels --utc/' vbr-ks-mod.cfg
sed -i 's/^timesource .*$/timesource --ntp-server time.cloudflare.com --nts/g' vbr-ks-mod.cfg

Let's check how the default vbr installation boots and copy of some of it statements

cat $ISOPATH/tmp/EFI/BOOT/grub.cfg | grep "Backup &" -A 4
submenu 'Veeam Backup & Replication' {
	menuentry 'Install - fresh install, wipes everything (including local backups)' --class fedora --class gnu-linux --class gnu --class os {
		linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=Rocky-9-2-x86_64 inst.ks=hd:LABEL=Rocky-9-2-x86_64:/vbr-ks.cfg quiet
		initrdefi /images/pxeboot/initrd.img
	}

R setup allows you to use the command over and over again to reinstall. --location is required to inject our modified kickstart script and pass the initrd parameters

virsh send-key $RNAME KEY_ENTER, sends a the enter key to confirm that we want to erase all the data on our clean discs. 60 secs might be a bit high but 30 secs is too low. Depends on your hardware.

R=$(($R+1))

RNAME=$(printf "vbr%03d" $R)
virt-install \
-n $RNAME \
--description "$(printf 'Veeam Backup & Replication VBR vbr%03d' $R)" \
--graphics=spice \
--boot=uefi \
--os-variant=rocky9 \
--ram=4096 \
--vcpus=4 \
--disk pool=dataimages,bus=virtio,size=240 \
--disk pool=dataimages,bus=virtio,size=250 \
--location $(ls $ISOPATH/VeeamSoftwareAppliance*.iso) \
--initrd-inject=$ISOPATH/vbr-ks-mod.cfg \
--extra-args="inst.stage2=hd:LABEL=Rocky-9-2-x86_64 inst.ks=file:/vbr-ks-mod.cfg quiet" \
--network network=br0 \
--noautoconsole && sleep 60 && virsh send-key $RNAME KEY_ENTER

echo "waiting";while [ -z "$(virsh domstate $RNAME | grep off)" ];do echo -n ".";sleep 3;done
virsh start $RNAME
sleep 10;virt-viewer $RNAME &

Setting up a hardened repo on the same server

sudo dnf install virt-install -y

If you want the data disk on another drive set (eg /dev/sdb mounted to /mnt/backup)

sudo mkdir /mnt/backup/$USER
sudo chown $USER:$USER /mnt/backup/$USER
mkdir /mnt/backup/$USER/data
virsh pool-define-as dataimages dir - - - - /mnt/backup/$USER/data
virsh pool-build dataimages
virsh pool-autostart dataimages

Download the JeOS (infrastructure) iso os in /home/$USER/iso. We copy the hardened repo kickstart to modify it and update it on the fly

This just changes the timezone and the keyboard but of course you could also inject the network settings

ISOPATH=/home/$USER/iso 
cd $ISOPATH
mkdir tmp
sudo mount *JeOS*.iso tmp
cat tmp/hardened-repo-ks.cfg > hardened-repo-ks-mod.cfg
sed -i 's/keyboard.*/keyboard --xlayouts='"'"be"'"'/' hardened-repo-ks-mod.cfg
sed -i 's/^timezone.*/timezone Europe\/Brussels --utc/' hardened-repo-ks-mod.cfg
sed -i 's/^timesource .*$/timesource --ntp-server time.cloudflare.com --nts/g' hardened-repo-ks-mod.cfg

Let's check how the default hardened repo boots and copy of some of it statements

cat $ISOPATH/tmp/EFI/BOOT/grub.cfg | grep "Hardened Repo" -A 4

should yield something like

submenu 'Veeam Hardened Repository' {
	menuentry 'Install - fresh install, wipes everything (including local backups)' --class fedora --class gnu-linux --class gnu --class os {
		linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=Rocky-9-2-x86_64 inst.ks=hd:LABEL=Rocky-9-2-x86_64:/hardened-repo-ks.cfg quiet
		initrdefi /images/pxeboot/initrd.img
	}

R setup allows you to use the command over and over again to reinstall. --location is required to inject our modified kickstart script and pass the initrd parameters

virsh send-key $RNAME KEY_ENTER, sends a the enter key to confirm that we want to erase all the data on our clean discs. 60 secs might be a bit high but 30 secs is too low. Depends on your hardware.

R=$(($R+1))
IPSTART=80
RNAME=$(printf "repo%03d" $R)

SUBN="192.168.0"
NETCONFIG="network --bootproto=static --ip=$SUBN.$(($IPSTART+$R)) --netmask=255.255.255.0 --gateway=$SUBN.1 --nameserver=$SUBN.1 --hostname=$RNAME"
sed -i "s/^network --bootproto=.*$/$NETCONFIG/g" hardened-repo-ks-mod.cfg

virt-install \
-n $RNAME \
--description "$(printf 'Veeam Backup & Replication REPO repo%03d' $R)" \
--graphics=spice \
--boot=uefi \
--os-variant=rocky9 \
--ram=4096 \
--vcpus=4 \
--disk pool=gnome-boxes,bus=virtio,size=120 \
--disk pool=dataimages,bus=virtio,size=150 \
--location $ISOPATH/VeeamJeOS_13.0.0.4967_20250822.iso \
--initrd-inject=$ISOPATH/hardened-repo-ks-mod.cfg \
--extra-args="inst.stage2=hd:LABEL=Rocky-9-2-x86_64 inst.ks=file:/hardened-repo-ks-mod.cfg quiet" \
--network network=br0 \
--noautoconsole && sleep 60;virsh send-key $RNAME KEY_ENTER

while [ -z "$(virsh domstate $RNAME | grep off)" ];do echo "waiting";sleep 10;done
virsh start $RNAME
sleep 10;virt-viewer $RNAME &
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment