The Ops Community ⚙️

Joseph D. Marhee
Joseph D. Marhee

Posted on

Preparing a FreeBSD Cloud Image with Cloud-Init

I use Kernel-based Virtual Machines or KVM as my primary virtualization method in my development environment, and use cloud-init to prepare my virtual machine images. There's a variety of ways to procure cloud images, but a lot of times these registries are focused on Linux images, or hypervisor technology like VMWare or VirtualBox, and while normally useful for me at work (where I primarily work on Linux systems), I was very excited to find a well maintained site for BSD cloud images.

After downloading the image I want (in this case freebsd-13.0-zfs.qcow2) to my image path in /var/lib/libvirt/images, I create a project directory for my cloud-init scripts in ~/freebsd-cidata:

instance-id: freebsd
local-hostname: freebsd
Enter fullscreen mode Exit fullscreen mode

in a file called meta-data, and in user-data:

#cloud-config
users:
  - name: root
    lock_passwd: false
    hashed_passwd: {PASSWD_HASH}
    ssh_pwauth: true
  - name: freebsd
    ssh_authorized_keys:
      - ssh-rsa ...
    hashed_passwd: {PASSWD_HASH}
    groups: wheel
    ssh_pwauth: true

write_files:
  - path: /usr/local/etc/sudoers
    content: |
      %wheel ALL=(ALL) NOPASSWD: ALL
    append: true
Enter fullscreen mode Exit fullscreen mode

This user-data updates the root password, creates a freebsd user, adds that user to the wheel group, adds its SSH public keys, and then updates the sudoers file to allow wheel group passwordless sudo access (common pattern in cloud images, but this can be done any way you'd like). I recommend taking a look at the cloud-init example for other options and automation you can bake into your user-data file.

For example, on my Ubuntu images, I like to do a little more that I am doing above for the selected FreeBSD image:

#cloud-config
users:
  - name: ubuntu
    ssh_authorized_keys:
      - ssh-rsa ...
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    groups: sudo
    shell: /bin/bash
runcmd:
  - hostnamectl set-hostname $(openssl rand -hex 6)
  - echo PubkeyAuthentication yes | sudo tee -a /etc/ssh/sshd_config
  - echo PubkeyAcceptedKeyTypes=+ssh-rsa | sudo tee -a /etc/ssh/sshd_config
  - systemctl restart ssh
write_files:
- content: |
    Ubuntu 22.04 LTS \n \l
    IPv4: \4{enp1s0}
  path: /etc/issue
permissions: '0644'
Enter fullscreen mode Exit fullscreen mode

where in this case, on Ubuntu systems, I like to generate a random hostname, things like updating SSH configuration options, and updating the console to show the IP address of the instance.

I like to use a password for the users, if you prefer only to use SSH keys, you can add a new list item for each key under ssh_authorized_keys, but if you want to set a password as well, you can create the password hash using:

mkpasswd --method=SHA-512 --rounds=4096
Enter fullscreen mode Exit fullscreen mode

and populating that value in your user-data file.

Once those files are saved, you can create the cloud-init iso:

sudo genisoimage -output /var/lib/libvirt/images/cidata-freebsd.iso -V cidata -r -J ./user-data ./meta-data
Enter fullscreen mode Exit fullscreen mode

You can then, back in your /var/lib/libvirt/images directory, proceed to create the VM, first by creating a clone of your cloud image for use on the new VM:

sudo qemu-img create -b freebsd-13.0-zfs.qcow2 -f qcow2 -F qcow2 ${your VM name}.img 80G
Enter fullscreen mode Exit fullscreen mode

and then optionally using a too like virt-install to automate some of the work of using libvirt with KVM, consume both the disk image and the cloud-init ISO file along with your other VM specifications:

sudo virt-install --name=${VM_NAME} --ram=4196 --vcpus=2 --import --disk path=${VM_NAME}.img,format=qcow2 --disk path=cidata-freebsd.iso,device=cdrom --os-variant=freebsd13.0 --network bridge=br0,model=virtio --graphics vnc,listen=0.0.0.0 --noautoconsole
Enter fullscreen mode Exit fullscreen mode

You can then view the spin up by running virsh list, getting the VM ID, and attaching the console to watch for cloud-init errors, virsh console ${ID}. The console output will contain a field like this:

/usr/local/bin/cloud-init startingCloud-init v. 21.2 running 'init' at Sat, 22 Oct 2022 19:41:45 +0000. Up 13.766839027404785 seconds.
ci-info: ++++++++++++++++++++++++++++++Net device info++++++++++++++++++++++++++++++
ci-info: +--------+------+----------------+------------+-------+-------------------+
ci-info: | Device |  Up  |    Address     |    Mask    | Scope |     Hw-Address    |
ci-info: +--------+------+----------------+------------+-------+-------------------+
ci-info: |  lo0   | True |   127.0.0.1    | 0xff000000 |   .   |         .         |
ci-info: |  lo0   | True |    ::1/128     |     .      |   .   |         .         |
ci-info: |  lo0   | True | fe80::1%lo0/64 |     .      |  0x2  |         .         |
ci-info: | vtnet0 | True | 192.168.0.115  | 0xffffff00 |   .   | 52:54:00:f4:32:c2 |
ci-info: +--------+------+----------------+------------+-------+-------------------+
ci-info:
Enter fullscreen mode Exit fullscreen mode

containing your instance networking information, so you can attempt to connect via SSH (if you don't know the instance IP), or if you set a password, you can proceed to login from this console as well.

Top comments (1)

Collapse
 
goneri profile image
Gonéri Le Bouder

Thank you Joseph for mentioning BSD-Cloud-image.org, it's really appreciated! I would suggest to give a try to Virt-lightning which allow you to run any Cloud image locally through a simple CLI. It will handle the metadata part for you.