diff --git a/.gitignore b/.gitignore index 859059f..9f9c34f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.img *.qcow2 +./openbsd-cloud-image \ No newline at end of file diff --git a/env_scripts/functions.sh b/env_scripts/functions.sh index f54aae2..befc775 100644 --- a/env_scripts/functions.sh +++ b/env_scripts/functions.sh @@ -1,6 +1,11 @@ -#!/bin/env bash +#!/usr/bin/env -S bash # Functions +pause() +{ + read -s -n 1 -p "Press any key to continue . . ." + echo "" +} check_host_os() { @@ -13,48 +18,93 @@ check_host_os() } -show_vm_menu() +generate_openbsd_image() { - # Show dinamic menu - echo "Select VM OS:" - for entry in $(jq -r '.os_variants[] | @base64' "$OS_JSON_FILE"); do - decoded=$(echo "$entry" | base64 --decode) - id=$(echo "$decoded" | jq -r .id) - name=$(echo "$decoded" | jq -r .name) - echo "$id. $name" - done - - # ID_MAX calculation - ID_MAX=$(jq -r '[.os_variants[].id] | max' "$OS_JSON_FILE") - - # Read input - read -r -p "Enter your choice [1-${ID_MAX}]: " answer - if ! [[ "$answer" =~ ^[0-9]+$ ]] || (( answer < 1 || answer > ID_MAX )); then - echo "Invalid option. Please enter a number between 1 and ${ID_MAX}." + local CURRENT_PATH="$PWD" + VM_BASE_IMAGE_NAME=${VM_BASE_IMAGE%%.*} + VM_BASE_IMAGE_EXTENSION=${VM_BASE_IMAGE#*.} + git clone https://github.com/hcartiaux/openbsd-cloud-image.git + cd openbsd-cloud-image + ./build_openbsd_qcow2.sh \ + --image-file ${VM_BASE_IMAGE_NAME}.${VM_BASE_IMAGE_EXTENSION} \ + --disklabel custom/disklabel.cloud \ + --size ${VM_DISK_SIZE} \ + -b + if ! test -f "${VM_BASE_DIR}/images/${VM_HOSTNAME}.${VM_DISK_EXTENSION}"; then + mv images/${VM_BASE_IMAGE_NAME}.${VM_BASE_IMAGE_EXTENSION} ${VM_BASE_DIR}/images/${VM_HOSTNAME}.${VM_DISK_EXTENSION} + sudo chown -R $USER:libvirt-qemu "${VM_BASE_DIR}/images/${VM_HOSTNAME}.${VM_DISK_EXTENSION}" + cd ${CURRENT_PATH} + rm -r openbsd-cloud-image + else + echo "${VM_BASE_DIR}/images/${VM_HOSTNAME}.${VM_DISK_EXTENSION} already exists. Delete VM with "delete" option" + cd ${CURRENT_PATH} + rm -r openbsd-cloud-image exit 1 fi - - selected=$(jq -r ".os_variants[] | select(.id == $answer)" "$OS_JSON_FILE") - - if [ -z "$selected" ]; then - echo "Invalid option." - exit 1 - fi - - # Asignar variables - VM_OS_VARIANT=$(echo "$selected" | jq -r .variant) - VM_OS_TYPE=$(echo "$selected" | jq -r .os_type) - VM_BASE_IMAGE_URL=$(echo "$selected" | jq -r .url) - VM_BASE_IMAGE=$(echo "$selected" | jq -r .origin_image_name) - VM_BOOT_TYPE=$(echo "$selected" | jq -r .boot_type) - VM_CHECKSUMS_URL=$(echo "$selected" | jq -r .md5sum) } +show_vm_menu() { + # Display dynamic OS selection menu + echo "Select VM OS:" + echo "--------------" + + # Array to store valid IDs for validation + VALID_IDS=() + while IFS= read -r entry; do + DECODED=$(echo "$entry" | base64 --decode) + ID=$(echo "$DECODED" | jq -r '.id') + NAME=$(echo "$DECODED" | jq -r '.name') + printf "%2s. %s\n" "$ID" "$NAME" + VALID_IDS+=("$ID") + done < <(jq -r '.os_variants[] | @base64' "$OS_JSON_FILE") + + # Calculate max ID for range validation + ID_MAX=$(jq -r '[.os_variants[].id] | max' "$OS_JSON_FILE") + ID_MIN=$(jq -r '[.os_variants[].id] | min' "$OS_JSON_FILE") + + # Read user input + read -r -p "Enter your choice [${ID_MIN}-${ID_MAX}]: " CHOICE + + # Validate input: must be a number and within range + if ! [[ "$CHOICE" =~ ^[0-9]+$ ]]; then + echo "Error: Please enter a valid number." >&2 + exit 1 + fi + + if (( CHOICE < ID_MIN || CHOICE > ID_MAX )); then + echo "Error: Please enter a number between ${ID_MIN} and ${ID_MAX}." >&2 + exit 1 + fi + + # Get selected OS variant + SELECTED=$(jq -r ".os_variants[] | select(.id == ${CHOICE})" "$OS_JSON_FILE") + + if [ -z "$SELECTED" ]; then + echo "Error: Invalid selection." >&2 + exit 1 + fi + + # Export variables in uppercase + VM_OS_VARIANT=$(echo "$SELECTED" | jq -r '.variant') + VM_OS_TYPE=$(echo "$SELECTED" | jq -r '.os_type') + VM_BASE_IMAGE_URL=$(echo "$SELECTED" | jq -r '.url') + VM_BASE_IMAGE=$(echo "$SELECTED" | jq -r '.origin_image_name') + VM_BOOT_TYPE=$(echo "$SELECTED" | jq -r '.boot_type') + VM_CHECKSUMS_URL=$(echo "$SELECTED" | jq -r '.md5sum') + + # Optional: Debug + # echo "Selected OS variant: ${VM_OS_VARIANT}" +} compare_checksum() { CHECKSUM_TMP_FOLDER=$(mktemp) - curl -s -o "${CHECKSUM_TMP_FOLDER}" "${VM_CHECKSUMS_URL}" - if [[ "$VM_OS_TYPE" == "freebsd" ]]; then + + wget -L \ + --user-agent="Mozilla/5.0 (X11; Linux x86_64)" \ + -O "${CHECKSUM_TMP_FOLDER}" \ + "${VM_CHECKSUMS_URL}" + + if [[ "$VM_OS_TYPE" == "BSD" && "${VM_OS_VARIANT}" == *"freebsd"* ]]; then if [[ "${VM_BASE_IMAGE}" == *"zfs"* ]]; then VM_BASE_IMAGE_CHECKSUM=$(grep "FreeBSD-14.3-STABLE-amd64-BASIC-CLOUDINIT" "${CHECKSUM_TMP_FOLDER}" | grep "zfs.qcow2.xz" | awk '{print $4}') else @@ -63,7 +113,7 @@ compare_checksum() else VM_BASE_IMAGE_CHECKSUM=$(grep "$VM_BASE_IMAGE_NAME.${VM_BASE_IMAGE_EXTENSION}" "${CHECKSUM_TMP_FOLDER}" | awk '{print $1}') fi - if [[ "${VM_CHECKSUMS_URL}" == *"SHA256"* ]]; then + if [[ "${VM_CHECKSUMS_URL}" == *"SHA256"* || "${VM_CHECKSUMS_URL}" == *"sha256"* ]]; then HASH_CMD="sha256sum" elif [[ "${VM_CHECKSUMS_URL}" == *"SHA512"* ]]; then HASH_CMD="sha512sum" @@ -100,13 +150,13 @@ vm_net_get_ip() # Obtener la dirección MAC de la interfaz de red MAC_VM=$(vm_net_get_mac $VM) if [[ -z "$MAC_VM" ]]; then - echo "Error: No se pudo encontrar la dirección MAC para '$VM'" + echo "Error: The MAC address could not be found for '$VM'" return 1 fi # Obtener la dirección IP a partir de la dirección MAC VM_IP_ADDRESS=$(arp -a | grep "$MAC_VM" | awk '{ print $2 }' | sed 's/[()]//g') if [[ -z "$VM_IP_ADDRESS" ]]; then - echo "Error: No se pudo encontrar la dirección IP para la dirección MAC '$MAC_VM'" + echo "Error: Could not find IP address for MAC address '$MAC_VM'" return 1 fi echo "$VM_IP_ADDRESS" @@ -170,7 +220,6 @@ vm_connect() vm_delete () { local VM=$1 - echo "VM: $VM" if [[ -f "$VM_IMAGE_PATH" ]]; then # Safely remove the VM with confirmation read -p "Are you sure you want to remove the VM '$VM' (y/N)? " confirm @@ -195,7 +244,7 @@ vm_delete () } vm_download_base_image() { - if [[ "$VM_OS_TYPE" == "freebsd" ]]; then + if [[ "$VM_OS_TYPE" == "BSD" && "${VM_OS_VARIANT}" == *"freebsd"* ]]; then if [[ "${VM_BASE_IMAGE}" == *"zfs"* ]]; then VM_BASE_IMAGE_NAME="${VM_OS_VARIANT}-zfs" else @@ -208,15 +257,17 @@ vm_download_base_image() fi VM_BASE_IMAGE_LOCATION="${VM_BASE_DIR}/${VM_BASE_IMAGES}/${VM_BASE_IMAGE_NAME}.${VM_BASE_IMAGE_EXTENSION}" if ! test -f "${VM_BASE_IMAGE_LOCATION}"; then - wget -O "${VM_BASE_IMAGE_LOCATION}" ${VM_BASE_IMAGE_URL} + wget -L \ + --user-agent="Mozilla/5.0 (X11; Linux x86_64)" \ + -O "${VM_BASE_IMAGE_LOCATION}" \ + ${VM_BASE_IMAGE_URL} fi } vm_create_guest_image() { - echo "Creating a qcow2 image file ${VM_BASE_DIR}/images/${VM_HOSTNAME}.${VM_DISK_EXTENSION} that uses the cloud image file ${VM_BASE_IMAGE_LOCATION} as its base" - if [[ "$VM_OS_TYPE" == "freebsd" ]]; then + if [[ "$VM_OS_TYPE" == "BSD" && "${VM_OS_VARIANT}" == *"freebsd"* ]]; then if ! test -f "${VM_BASE_DIR}/images/${VM_HOSTNAME}.qcow"; then xz -d ${VM_BASE_IMAGE_LOCATION} fi @@ -248,81 +299,104 @@ vm_generate_ssh_hey() #rm "${VM_BASE_DIR}/ssh/${VM_HOSTNAME}".pub.txt } +# vm_gen_user_data() +# { +# VM_USER_PASS=$(tr -dc A-Za-z0-9 "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" +# #cloud-config +# hostname: ${VM_HOSTNAME} +# package_reboot_if_required: true +# package_update: true +# package_upgrade: true +# packages: +# - sudo +# - vim +# ssh_pwauth: false +# users: +# - name: root +# lock_passwd: false +# hashed_passwd: ${VM_ROOT_PASS_HASH} +# - name: ${VM_USERNAME} +# ssh_authorized_keys: +# - ${SSH_PUB_KEY} +# lock_passwd: true +# groups: wheel +# shell: /bin/tcsh + +# write_files: +# - path: /usr/local/etc/sudoers +# content: | +# %wheel ALL=(ALL) NOPASSWD: ALL +# append: true +# EOF +# #OPENBSD +# elif [[ "$VM_OS_TYPE" == "BSD" && "${VM_OS_VARIANT}" == *"openbsd"* ]]; then +# #"disable_root": true +# cat < "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" +# #cloud-config +# "hostname": ${VM_HOSTNAME} +# "package_upgrade": true +# "packages": +# - "bash" +# - "vim--no_x11" +# "ssh_pwauth": false +# "users": +# - "name": ${VM_USERNAME} +# "sudo": "ALL=(ALL) NOPASSWD:ALL" +# "groups": wheel +# "hashed_passwd": "!" +# "lock_passwd": true +# "shell": "/usr/local/bin/bash" +# "ssh_authorized_keys": +# - ${SSH_PUB_KEY} +# - "name": "root" +# "hashed_passwd": "!" +# "lock_passwd": true +# write_files: +# - path: /etc/sudoers +# content: | +# %wheel ALL=(ALL) NOPASSWD: ALL +# append: true +# EOF +# else +# cat < "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" +# #cloud-config +# hostname: ${VM_HOSTNAME} +# # manage_etc_hosts: false +# ssh_pwauth: true +# disable_root: true +# users: +# - name: ${VM_USERNAME} +# hashed_passwd: ${VM_USER_PASS_HASH} +# sudo: ALL=(ALL) NOPASSWD:ALL +# shell: /bin/bash +# lock-passwd: false +# ssh_authorized_keys: +# - ${SSH_PUB_KEY} +# EOF +# fi +# } + + vm_gen_user_data() { -VM_USER_PASS=$(tr -dc A-Za-z0-9 "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" -#cloud-config -hostname: ${VM_HOSTNAME} -package_reboot_if_required: true -package_update: true -package_upgrade: true -packages: -- sudo -- vim -ssh_pwauth: false -users: - - name: root - lock_passwd: false - hashed_passwd: ${VM_ROOT_PASS_HASH} - - name: ${VM_USERNAME} - ssh_authorized_keys: - - ${SSH_PUB_KEY} - lock_passwd: true - groups: wheel - shell: /bin/tcsh -write_files: - - path: /usr/local/etc/sudoers - content: | - %wheel ALL=(ALL) NOPASSWD: ALL - append: true -EOF -#LINUX GUEST -else -cat < "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" -#cloud-config -hostname: ${VM_HOSTNAME} -# manage_etc_hosts: false -ssh_pwauth: true -disable_root: true -users: -- name: ${VM_USERNAME} - hashed_passwd: ${VM_USER_PASS_HASH} - sudo: ALL=(ALL) NOPASSWD:ALL - shell: /bin/bash - lock-passwd: false - ssh_authorized_keys: - - ${SSH_PUB_KEY} -EOF -fi + if [[ "$VM_OS_TYPE" == "BSD" && "${VM_OS_VARIANT}" == *"freebsd"* ]]; then + VM_USER_DATA_FILE="files/freebsd-user-data" + elif [[ "$VM_OS_TYPE" == "BSD" && "${VM_OS_VARIANT}" == *"openbsd"* ]]; then + VM_USER_DATA_FILE="files/openbsd-user-data" + else + VM_USER_DATA_FILE="files/linux-user-data" + fi + cp ${VM_USER_DATA_FILE} "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" + sed -i "s|__SSH_PUB_KEY__|${SSH_PUB_KEY}|g" "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" + sed -i "s|__VM_USERNAME__|${VM_USERNAME}|g" "$VM_BASE_DIR/init/${VM_HOSTNAME}-user-data" } - -# vm_gen_user_data() -# { -# VM_USER_DATA_FILE=files/user-data -# VM_USER_PASS=$(tr -dc A-Za-z0-9 "${VM_BASE_DIR}/xml/${VM_HOSTNAME}.xml" - echo "Root password: $VM_ROOT_PASS" - echo "User password: $VM_USER_PASS" + clear + echo "VM ${VM_HOSTNAME} Created!" + echo "NOTE: It may take some time for the virtual machine to be available if it is a BSD flavor. You can check the status of the virtual machine with the following command:" + echo "root pass is(only for BSD flavour): ${VM_USER_PASS}" + echo "user pass is: ${VM_USER_PASS}" + echo "virsh console ${VM_HOSTNAME} --safe" } diff --git a/env_scripts/newer_os.sh b/env_scripts/newer_os.sh index 372bdd4..4ff2b2e 100644 --- a/env_scripts/newer_os.sh +++ b/env_scripts/newer_os.sh @@ -1,3 +1,4 @@ #!/bin/env bash GUEST_OS_TYPE_DEBIAN="debian13" -GUEST_OS_TYPE_FREEBSD="freebsd14.2" \ No newline at end of file +GUEST_OS_TYPE_FREEBSD="freebsd14.2" +GUEST_OS_TYPE_OPENBSD="openbsd7.6" diff --git a/env_scripts/older_os.sh b/env_scripts/older_os.sh index 40e6eda..3e24073 100644 --- a/env_scripts/older_os.sh +++ b/env_scripts/older_os.sh @@ -1,3 +1,4 @@ #!/bin/env bash GUEST_OS_TYPE_DEBIAN="debian11" GUEST_OS_TYPE_FREEBSD="freebsd13.1" +GUEST_OS_TYPE_OPENBSD="openbsd7.0" diff --git a/files/freebsd-user-data b/files/freebsd-user-data new file mode 100644 index 0000000..7938668 --- /dev/null +++ b/files/freebsd-user-data @@ -0,0 +1,25 @@ +#cloud-config +package_reboot_if_required: true +package_update: true +package_upgrade: true +packages: +- sudo +- vim +ssh_pwauth: false +users: +- name: __VM_USERNAME__ + sudo: "ALL=(ALL) NOPASSWD:ALL" + groups: wheel + hashed_passwd: "!" + lock_passwd: true + shell: /bin/tcsh + ssh_authorized_keys: + - __SSH_PUB_KEY__ +- name: root + hashed_passwd: "!" + lock_passwd: true +write_files: + - path: /usr/local/etc/sudoers + content: | + %wheel ALL=(ALL) NOPASSWD: ALL + append: true \ No newline at end of file diff --git a/files/linux-user-data b/files/linux-user-data new file mode 100644 index 0000000..fcff5f0 --- /dev/null +++ b/files/linux-user-data @@ -0,0 +1,14 @@ +#cloud-config +ssh_pwauth: true +disable_root: true +package_reboot_if_required: true +package_update: true +package_upgrade: true +users: +- name: __VM_USERNAME__ + ssh_authorized_keys: + - __SSH_PUB_KEY__ + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + groups: sudo + shell: /bin/bash + lock-passwd: true diff --git a/files/openbsd-user-data b/files/openbsd-user-data new file mode 100644 index 0000000..c4d7cd3 --- /dev/null +++ b/files/openbsd-user-data @@ -0,0 +1,25 @@ +#cloud-config +package_reboot_if_required: true +package_update: true +package_upgrade: true +packages: +- sudo +- vim +ssh_pwauth: false +users: +- name: __VM_USERNAME__ + sudo: "ALL=(ALL) NOPASSWD:ALL" + groups: wheel + hashed_passwd: "!" + lock_passwd: true + shell: /usr/local/bin/bash + ssh_authorized_keys: + - __SSH_PUB_KEY__ +- name: root + hashed_passwd: "!" + lock_passwd: true +write_files: + - path: /etc/sudoers + content: | + %wheel ALL=(ALL) NOPASSWD: ALL + append: true \ No newline at end of file diff --git a/files/os_options.json b/files/os_options.json index 32de598..ed48516 100644 --- a/files/os_options.json +++ b/files/os_options.json @@ -74,7 +74,7 @@ { "id": 8, "name": "FreeBSD 14.3 UFS", - "os_type": "freebsd", + "os_type": "BSD", "variant": "freebsd14.2", "url": "https://download.freebsd.org/ftp/snapshots/VM-IMAGES/14.3-STABLE/amd64/Latest/FreeBSD-14.3-STABLE-amd64-BASIC-CLOUDINIT-ufs.qcow2.xz", "origin_image_name": "FreeBSD-14.3-STABLE-amd64-BASIC-CLOUDINIT-ufs.qcow2.xz", @@ -83,11 +83,20 @@ { "id": 9, "name": "FreeBSD 14.3 ZFS", - "os_type": "freebsd", + "os_type": "BSD", "variant": "freebsd14.2", "url": "https://download.freebsd.org/ftp/snapshots/VM-IMAGES/14.3-STABLE/amd64/Latest/FreeBSD-14.3-STABLE-amd64-BASIC-CLOUDINIT-zfs.qcow2.xz", "origin_image_name": "FreeBSD-14.3-STABLE-amd64-BASIC-CLOUDINIT-zfs.qcow2.xz", "md5sum": "https://download.freebsd.org/ftp/snapshots/VM-IMAGES/14.3-STABLE/amd64/Latest/CHECKSUM.SHA512" - } + } , + { + "id": 10, + "name": "OpenBSD 7.7 generic", + "os_type": "BSD", + "variant": "openbsd7.6", + "url": "", + "origin_image_name": "openbsd-generic.qcow2", + "md5sum": "" + } ] } diff --git a/files/user-data b/files/user-data deleted file mode 100644 index 7e7b119..0000000 --- a/files/user-data +++ /dev/null @@ -1,12 +0,0 @@ -ssh_pwauth: true -disable_root: true -users: -- name: user - ssh_authorized_keys: - - __SSH_KEY__ - sudo: ["ALL=(ALL) NOPASSWD:ALL"] - groups: sudo - shell: /bin/bash - hashed_passwd: __USER_PASSWORD__ - lock-passwd: false - diff --git a/vm_manage.sh b/vm_manage.sh index 78d11cd..1177329 100755 --- a/vm_manage.sh +++ b/vm_manage.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env -S bash source env_scripts/common.sh source env_scripts/functions.sh @@ -9,30 +9,41 @@ VM_DISK_SIZE=10 # Function to display usage message usage() { - echo "Usage: $0 create -n NAME [-b BRIDGE] [-r RAM] [-c VCPUS] [-s DISK] [-v]" - echo " $0 delete -n NAME" - echo " $0 info -n NAME" - echo " $0 connect -n NAME" - echo " $0 list" - echo "" - echo "Actions:" - echo " create Create a new virtual machine" - echo " delete Delete a virtual machine" - echo " list List all defined virtual machines" - echo " info Show information about a virtual machine" - echo " connect Connect to the console of a virtual machine" - echo "" - echo "Options for 'create':" - echo " -h Show this help message" - echo " -n NAME Host name (required)" - echo " -b BRIDGE Bridge interface name" - echo " -r RAM RAM in MB (default: ${VM_MEM_SIZE})" - echo " -c VCPUS Number of VCPUs (default: ${VM_VCPUS})" - echo " -s DISK Disk size in GB (default: ${VM_DISK_SIZE})" - echo " -v Verbose mode" + less << EOF +NAME + $0 + +USAGE + Usage: $0 create -n NAME [-b BRIDGE] [-r RAM] [-c VCPUS] [-s DISK] [-v] + $0 delete NAME + $0 info NAME + $0 connect NAME + $0 list + +ACTIONS + create Create a new virtual machine + delete Delete a virtual machine + list List all defined virtual machines + info Show information about a virtual machine + connect Connect to the console of a virtual machine + +OPTIONS + -h Show this help message + -n NAME Host name (required) + -b BRIDGE Bridge interface name + -r RAM RAM in MB (default: ${VM_MEM_SIZE}) + -c VCPUS Number of VCPUs (default: ${VM_VCPUS}) + -s DISK Disk size in GB (default: ${VM_DISK_SIZE}) + -v Verbose mode + +AUTHOR + Victor Gracia Enguita + +COPYRIGHT + This is free software; see the source for copying conditions. +EOF exit 1 } - # Check if at least one argument is provided if [ $# -eq 0 ]; then usage @@ -97,12 +108,16 @@ case "${ACTION}" in show_vm_menu #Set guest type based on check_host_os vm_set_guest_type - #Download cloud image - vm_download_base_image - #Compare hashes - compare_checksum - #Create guest image - vm_create_guest_image + if [[ "$VM_OS_TYPE" == "BSD" && "${VM_OS_VARIANT}" == *"openbsd"* ]]; then + generate_openbsd_image + else + #Download cloud image + vm_download_base_image + #Compare hashes + compare_checksum + #Create guest image + vm_create_guest_image + fi #Generate ssh key vm_generate_ssh_hey #Generate meta-data file for VM @@ -121,8 +136,6 @@ case "${ACTION}" in fi VM_HOSTNAME="$1" source env_scripts/common.sh - echo "Action: ${ACTION}" - echo "VM Name: ${VM_HOSTNAME}" if [[ "${ACTION}" == 'delete' ]]; then vm_delete ${VM_HOSTNAME} elif [[ "${ACTION}" == 'info' ]]; then @@ -133,7 +146,6 @@ case "${ACTION}" in ;; list) - #echo "Action: list" vm_list ;;