From eef59510ca01b9d0f5e8115f54bb62b838131122 Mon Sep 17 00:00:00 2001 From: Eugene Zamriy Date: Thu, 6 Jul 2023 11:42:17 +0300 Subject: [PATCH] Adds VMWare Vagrant box build configuration --- .gitignore | 2 + README.md | 68 +++++++++ ansible/requirements.yml | 3 + ansible/roles/cleanup_vm/README.md | 3 + ansible/roles/cleanup_vm/meta/main.yml | 14 ++ ansible/roles/cleanup_vm/tasks/main.yml | 148 ++++++++++++++++++++ ansible/roles/nfs_client/README.md | 3 + ansible/roles/nfs_client/meta/main.yml | 15 ++ ansible/roles/nfs_client/tasks/main.yml | 5 + ansible/roles/vagrant_pubkey/README.md | 4 + ansible/roles/vagrant_pubkey/meta/main.yml | 15 ++ ansible/roles/vagrant_pubkey/tasks/main.yml | 16 +++ ansible/roles/vmware_guest/README.md | 4 + ansible/roles/vmware_guest/meta/main.yml | 17 +++ ansible/roles/vmware_guest/tasks/main.yml | 5 + ansible/vagrant.yml | 11 ++ http/msvsphere-9-vagrant.x86_64.ks | 69 +++++++++ msvsphere-9-vagrant.pkr.hcl | 58 ++++++++ variables.pkr.hcl | 107 ++++++++++++++ 19 files changed, 567 insertions(+) create mode 100644 .gitignore create mode 100644 ansible/requirements.yml create mode 100644 ansible/roles/cleanup_vm/README.md create mode 100644 ansible/roles/cleanup_vm/meta/main.yml create mode 100644 ansible/roles/cleanup_vm/tasks/main.yml create mode 100644 ansible/roles/nfs_client/README.md create mode 100644 ansible/roles/nfs_client/meta/main.yml create mode 100644 ansible/roles/nfs_client/tasks/main.yml create mode 100644 ansible/roles/vagrant_pubkey/README.md create mode 100644 ansible/roles/vagrant_pubkey/meta/main.yml create mode 100644 ansible/roles/vagrant_pubkey/tasks/main.yml create mode 100644 ansible/roles/vmware_guest/README.md create mode 100644 ansible/roles/vmware_guest/meta/main.yml create mode 100644 ansible/roles/vmware_guest/tasks/main.yml create mode 100644 ansible/vagrant.yml create mode 100644 http/msvsphere-9-vagrant.x86_64.ks create mode 100644 msvsphere-9-vagrant.pkr.hcl create mode 100644 variables.pkr.hcl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c1f236d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +*.box diff --git a/README.md b/README.md index fe3a614..610ca9e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,74 @@ building MSVSphere images for various cloud platforms. +## Build environment configuration + +Supported operating systems: + + * MSVSphere 9 and other EL9-compatible distributions + * Fedora + +Follow the Packer installation [instructions](https://developer.hashicorp.com/packer/downloads?product_intent=packer). + +Alternatively, you can install a Packer binary from a Yandex +[mirror](https://hashicorp-releases.yandexcloud.net/packer/): just download a +latest version archive and unzip it somewhere in PATH (e.g. `~/.local/bin`). + +Verify that Packer works: + +```shell +$ packer version +Packer v1.9.1 +``` + +Install required Packer plugins: + +```shell +$ packer init -upgrade . +``` + +Dependently on your network configuration, you may also need to open the +8000-9000 TCP port range so that Packer can serve kickstart files to VMs: + +```shell +$ firewall-cmd --zone=public --add-port=8000-9000/tcp --permanent +$ firewall-cmd --reload +``` + + +## Building images + +In order to build an image use the following command syntax: + +```shell +$ packer build -only=${BUILDER}.${CONFIGURATION} . +``` + +where `${BUILDER}` is a Packer builder (e.g. `virtualbox-iso`) and +`${CONFIGURATION}` is an image configuration name (e.g. +`msvsphere-9-vagrant-x86_64`). + +A graphical VM console is disabled by default, but you can enable it for +debugging purposes by setting the `headless` variable to `false`: + +```shell +$ packer build -only=vmware-iso.msvsphere-9-vagrant-x86_64 \ + -var headless=false . +``` + +See the [variables.pkr.hcl](variables.pkr.hcl) file for other supported +variables. + + +### Building Vagrant boxes + +VMWare Vagrant box build command: + +```shell +$ packer build -only=vmware-iso.msvsphere-9-vagrant-x86_64 . +``` + + ## License Licensed under the MIT license, see the [LICENSE](LICENSE) file for details. diff --git a/ansible/requirements.yml b/ansible/requirements.yml new file mode 100644 index 0000000..7d35793 --- /dev/null +++ b/ansible/requirements.yml @@ -0,0 +1,3 @@ +--- +collections: [] +roles: [] diff --git a/ansible/roles/cleanup_vm/README.md b/ansible/roles/cleanup_vm/README.md new file mode 100644 index 0000000..555be3e --- /dev/null +++ b/ansible/roles/cleanup_vm/README.md @@ -0,0 +1,3 @@ +# cleanup_vm + +An Ansible role that cleans-up and deprovisions a virtual machine. diff --git a/ansible/roles/cleanup_vm/meta/main.yml b/ansible/roles/cleanup_vm/meta/main.yml new file mode 100644 index 0000000..60c21b5 --- /dev/null +++ b/ansible/roles/cleanup_vm/meta/main.yml @@ -0,0 +1,14 @@ +galaxy_info: + role_name: cleanup_vm + author: Eugene Zamriy + description: Cleans-up and deprovisions a VM + license: MIT + min_ansible_version: '2.5' + platforms: + - name: EL + versions: + - '8' + - '9' + galaxy_tags: [] + +dependencies: [] diff --git a/ansible/roles/cleanup_vm/tasks/main.yml b/ansible/roles/cleanup_vm/tasks/main.yml new file mode 100644 index 0000000..d07ef99 --- /dev/null +++ b/ansible/roles/cleanup_vm/tasks/main.yml @@ -0,0 +1,148 @@ +--- +- name: Remove old kernels + ansible.builtin.shell: dnf remove -y $(dnf repoquery --installonly --latest-limit=-1 -q) + +- name: Delete DNF cache + ansible.builtin.command: dnf clean all + +- name: Find DNF history files + ansible.builtin.find: + paths: /var/lib/dnf + patterns: "history*" + register: dnf_history + +- name: Reset DNF history + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ dnf_history.files }}" + +- name: Find temporary files + ansible.builtin.find: + file_type: any + paths: + - /tmp + - /var/tmp + patterns: '*' + register: tmp_files + +- name: Remove temporary files + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ tmp_files.files }}" + +- name: Find SSH host keys + ansible.builtin.find: + paths: /etc/ssh + patterns: '*host*key*' + register: host_keys + +- name: Remove SSH host keys + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ host_keys.files }}" + +- name: Remove kickstart files + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /root/anaconda-ks.cfg + - /root/original-ks.cfg + +- name: Truncate files + ansible.builtin.command: "truncate -s 0 {{ item }}" + loop: + - /etc/machine-id + - /etc/resolv.conf + - /var/log/audit/audit.log + - /var/log/wtmp + - /var/log/lastlog + - /var/log/btmp + - /var/log/cron + - /var/log/maillog + - /var/log/messages + - /var/log/secure + - /var/log/spooler + +- name: Remove log folders. + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /var/log/anaconda + - /var/log/qemu-ga + - /var/log/tuned + - /var/lib/cloud + - /etc/hostname + - /etc/machine-info + - /var/lib/systemd/credential.secret + +- name: Find log files. + ansible.builtin.find: + paths: + - /var/log + - /var/log/sssd + patterns: '*log,*.old,*.log.gz,*.[0-9],*.gz,*-????????' + register: log_files + +- name: Remove log files + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ log_files.files }}" + +- name: Remove random-seed + ansible.builtin.file: + path: /var/lib/systemd/random-seed + state: absent + +- name: Disable root SSH login via password + ansible.builtin.file: + path: /etc/ssh/sshd_config.d/01-permitrootlogin.conf + state: absent + when: ansible_facts['distribution_major_version'] == '9' + +- name: Fill free space with zeroes + ansible.builtin.shell: dd if=/dev/zero of=/zeroed_file bs=1M oflag=direct || rm -f /zeroed_file + +- name: Detect swap partition + ansible.builtin.command: grep -oP '^/dev/[\w-]+' /proc/swaps + register: swaps + ignore_errors: true + +- name: Wipe out swap data + block: + - name: Get swap partition UUID + ansible.builtin.command: "blkid {{ swaps.stdout }} -s UUID -o value" + register: swap_blkid + + - name: Unmount swap partition + ansible.builtin.command: "swapoff {{ swaps.stdout }}" + + - name: Fill swap partition with zeroes + ansible.builtin.shell: "dd if=/dev/zero of={{ swaps.stdout }} bs=1M oflag=direct || /bin/true" + + - name: Format swap partition + ansible.builtin.command: "mkswap -U {{ swap_blkid.stdout }} -f {{ swaps.stdout }}" + + - name: Mount swap partition + ansible.builtin.command: "swapon {{ swaps.stdout }}" + when: swaps.rc == 0 + +- name: Sync disc + ansible.builtin.command: sync + +- name: Clear shell history + ansible.builtin.shell: history -c + +- name: Check if WALinuxAgent is installed + ansible.builtin.stat: + path: /usr/sbin/waagent + register: cleanup_vm_waagent + +- name: Deprovision WALinuxAgent + ansible.builtin.command: waagent -deprovision+user -force + when: cleanup_vm_waagent.stat.exists diff --git a/ansible/roles/nfs_client/README.md b/ansible/roles/nfs_client/README.md new file mode 100644 index 0000000..5cd9a14 --- /dev/null +++ b/ansible/roles/nfs_client/README.md @@ -0,0 +1,3 @@ +# nfs_client + +An Ansible role that installs the `nfs-utils` package. diff --git a/ansible/roles/nfs_client/meta/main.yml b/ansible/roles/nfs_client/meta/main.yml new file mode 100644 index 0000000..66b2409 --- /dev/null +++ b/ansible/roles/nfs_client/meta/main.yml @@ -0,0 +1,15 @@ +galaxy_info: + role_name: nfs_client + author: Eugene Zamriy + description: Installs nfs-utils package + license: MIT + min_ansible_version: '2.5' + platforms: + - name: EL + versions: + - '8' + - '9' + galaxy_tags: + - nfs + +dependencies: [] diff --git a/ansible/roles/nfs_client/tasks/main.yml b/ansible/roles/nfs_client/tasks/main.yml new file mode 100644 index 0000000..d78d09b --- /dev/null +++ b/ansible/roles/nfs_client/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- name: Install nfs-utils + ansible.builtin.dnf: + name: nfs-utils + state: present diff --git a/ansible/roles/vagrant_pubkey/README.md b/ansible/roles/vagrant_pubkey/README.md new file mode 100644 index 0000000..895ac85 --- /dev/null +++ b/ansible/roles/vagrant_pubkey/README.md @@ -0,0 +1,4 @@ +# vagrant_pubkey + +An Ansible role that adds a Vagrant public SSH key to the `vagrant` user's +`~/.ssh/authorized_keys` file. diff --git a/ansible/roles/vagrant_pubkey/meta/main.yml b/ansible/roles/vagrant_pubkey/meta/main.yml new file mode 100644 index 0000000..d643392 --- /dev/null +++ b/ansible/roles/vagrant_pubkey/meta/main.yml @@ -0,0 +1,15 @@ +galaxy_info: + role_name: vagrant_pubkey + author: Eugene Zamriy + description: Adds Vagrant public SSH key + license: MIT + min_ansible_version: '2.5' + platforms: + - name: EL + versions: + - '8' + - '9' + galaxy_tags: + - vagrant + +dependencies: [] diff --git a/ansible/roles/vagrant_pubkey/tasks/main.yml b/ansible/roles/vagrant_pubkey/tasks/main.yml new file mode 100644 index 0000000..cc96b04 --- /dev/null +++ b/ansible/roles/vagrant_pubkey/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: Create /home/vagrant/.ssh directory + ansible.builtin.file: + path: /home/vagrant/.ssh + state: directory + owner: vagrant + group: vagrant + mode: 0o700 + +- name: Download Vagrant public SSH key + ansible.builtin.get_url: + url: https://raw.githubusercontent.com/hashicorp/vagrant/main/keys/vagrant.pub + dest: /home/vagrant/.ssh/authorized_keys + owner: vagrant + group: vagrant + mode: 0o600 diff --git a/ansible/roles/vmware_guest/README.md b/ansible/roles/vmware_guest/README.md new file mode 100644 index 0000000..aa2bf1e --- /dev/null +++ b/ansible/roles/vmware_guest/README.md @@ -0,0 +1,4 @@ +# vmware_guest + +An Ansible role that installs the `open-vm-tools` package on a virtual +machine. diff --git a/ansible/roles/vmware_guest/meta/main.yml b/ansible/roles/vmware_guest/meta/main.yml new file mode 100644 index 0000000..e2c7408 --- /dev/null +++ b/ansible/roles/vmware_guest/meta/main.yml @@ -0,0 +1,17 @@ +galaxy_info: + role_name: vmware_guest + author: Eugene Zamriy + description: Installs open-vm-tools + license: MIT + min_ansible_version: '2.5' + platforms: + - name: EL + versions: + - '8' + - '9' + galaxy_tags: + - guest + - system + - vmware + +dependencies: [] diff --git a/ansible/roles/vmware_guest/tasks/main.yml b/ansible/roles/vmware_guest/tasks/main.yml new file mode 100644 index 0000000..4da7833 --- /dev/null +++ b/ansible/roles/vmware_guest/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- name: Install open-vm-tools + ansible.builtin.dnf: + name: open-vm-tools + state: present diff --git a/ansible/vagrant.yml b/ansible/vagrant.yml new file mode 100644 index 0000000..b83423b --- /dev/null +++ b/ansible/vagrant.yml @@ -0,0 +1,11 @@ +--- +- name: MSVSphere Vagrant box + hosts: default + become: true + + roles: + - role: vmware_guest + when: packer_provider == 'vmware-iso' + - nfs_client + - vagrant_pubkey + - cleanup_vm diff --git a/http/msvsphere-9-vagrant.x86_64.ks b/http/msvsphere-9-vagrant.x86_64.ks new file mode 100644 index 0000000..c003b45 --- /dev/null +++ b/http/msvsphere-9-vagrant.x86_64.ks @@ -0,0 +1,69 @@ +# MSVSphere 9 Vagrant boxes kickstart file + +# TODO: change url to the kickstart one when we have it +url --url https://rsync.inferitos.ru/msvsphere/9.2/BaseOS/x86_64/os/ +repo --name=BaseOS --baseurl=https://rsync.inferitos.ru/msvsphere/9.2/BaseOS/x86_64/os/ +repo --name=AppStream --baseurl=https://rsync.inferitos.ru/msvsphere/9.2/AppStream/x86_64/os/ + +text +skipx +eula --agreed +firstboot --disabled + +lang C.UTF-8 +keyboard us +timezone UTC --utc + +network --bootproto=dhcp +firewall --disabled +services --enabled=sshd +selinux --enforcing + +bootloader --location=mbr +zerombr +clearpart --all --initlabel +autopart --type=plain --nohome --noboot --noswap + +rootpw vagrant +user --name=vagrant --plaintext --password vagrant + +reboot --eject + + +%packages --inst-langs=en +@core +bzip2 +dracut-config-generic +tar +usermode +-biosdevname +-dnf-plugin-spacewalk +-dracut-config-rescue +-iprutils +-iwl*-firmware +-langpacks-* +-mdadm +-open-vm-tools +-plymouth +-rhn* +%end + + +# disable kdump service +%addon com_redhat_kdump --disable +%end + + +%post +# allow passwordless sudo for the vagrant user +echo "vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vagrant + +# see Vagrant documentation (https://docs.vagrantup.com/v2/boxes/base.html) +# for details about the requiretty. +sed -i "s/^.*requiretty/# Defaults requiretty/" /etc/sudoers +yum clean all + +# permit root login via SSH with password authentication +echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/01-permitrootlogin.conf + +%end diff --git a/msvsphere-9-vagrant.pkr.hcl b/msvsphere-9-vagrant.pkr.hcl new file mode 100644 index 0000000..86089cb --- /dev/null +++ b/msvsphere-9-vagrant.pkr.hcl @@ -0,0 +1,58 @@ +/** + * Packer template for building MSVSphere 9 Vagrant boxes. + */ + +source "vmware-iso" "msvsphere-9-vagrant-x86_64" { + iso_url = var.iso_url_9_x86_64 + iso_checksum = var.iso_checksum_9_x86_64 + boot_command = var.vagrant_boot_cmd_9_x86_64_bios + boot_wait = var.boot_wait + cpus = var.cpus + memory = var.memory + disk_size = var.vagrant_disk_size + headless = var.headless + http_directory = var.http_directory + guest_os_type = "centos-64" + shutdown_command = var.vagrant_shutdown_command + ssh_username = var.vagrant_ssh_username + ssh_password = var.vagrant_ssh_password + ssh_timeout = var.ssh_timeout + vmx_data = { + "cpuid.coresPerSocket" : "1" + } + vmx_data_post = { + "memsize" : var.post_memory + "numvcpus" : var.post_cpus + } + vmx_remove_ethernet_interfaces = true +} + + +build { + sources = [ + "sources.vmware-iso.msvsphere-9-vagrant-x86_64" + ] + + provisioner "ansible" { + playbook_file = "ansible/vagrant.yml" + galaxy_file = "ansible/requirements.yml" + roles_path = "ansible/roles" + collections_path = "ansible/collections" + ansible_env_vars = [ + "ANSIBLE_PIPELINING=True", + "ANSIBLE_REMOTE_TEMP=/tmp", + "ANSIBLE_SSH_ARGS='-o ControlMaster=no -o ControlPersist=180s -o ServerAliveInterval=120s -o TCPKeepAlive=yes -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa'" + ] + extra_arguments = [ + "--extra-vars", + "packer_provider=${source.type}" + ] + } + + post-processors { + post-processor "vagrant" { + compression_level = "9" + output = "MSVSphere-${var.os_version_9}-${formatdate("YYYYMMDD", timestamp())}.{{.Provider}}.x86_64.box" + } + } +} diff --git a/variables.pkr.hcl b/variables.pkr.hcl new file mode 100644 index 0000000..103b55c --- /dev/null +++ b/variables.pkr.hcl @@ -0,0 +1,107 @@ +/** + * MSVSphere Packer template variables. + */ + +variable "os_version_9" { + description = "The target MSVSphere 9 version" + type = string + default = "9.2" +} + +// TODO: switch to the boot ISO on production +variable "iso_url_9_x86_64" { + description = "MSVSphere 9 x86_64 installation ISO URL" + type = string + default = "https://rsync.inferitos.ru/msvsphere/9.2/isos/x86_64/MSVSphere-9.2-alpha-x86_64-minimal.iso" +} + +variable "iso_checksum_9_x86_64" { + description = "MSVSphere 9 x86_64 installation ISO checksum" + type = string + default = "file:https://rsync.inferitos.ru/msvsphere/9.2/isos/x86_64/CHECKSUM" +} + +variable "headless" { + description = "Start the VM without a GUI console if true" + type = bool + default = true +} + +variable "cpus" { + description = "The number of CPUs for a VM" + type = number + default = 2 +} + +variable "memory" { + description = "The amount of RAM in megabytes" + type = number + default = 2048 +} + +variable "boot_wait" { + description = "Time to wait before interacting with an OS bootloader menu" + type = string + default = "10s" +} + +variable "ssh_timeout" { + description = "The SSH connection timeout" + type = string + default = "1800s" +} + +variable "post_cpus" { + description = "The number of CPUs to set for the VM after build" + type = number + default = 1 +} + +variable "post_memory" { + description = "The amount of RAM to set for the VM after build" + type = number + default = 1024 +} + +variable "http_directory" { + description = "The kickstart files directory path" + type = string + default = "http" +} + +/** + * Vagrant-specific settings. + */ +variable "vagrant_disk_size" { + description = "The VM disk size in megabytes" + type = number + default = 20000 +} + +variable "vagrant_boot_cmd_9_x86_64_bios" { + description = "The boot command for x86_64 VMs in a BIOS mode" + type = list(string) + default = [ + " inst.text inst.gpt ", + "inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/msvsphere-9-vagrant.x86_64.ks", + "" + ] +} + +variable "vagrant_ssh_username" { + description = "A login to use for SSH authentication" + type = string + default = "vagrant" +} + +variable "vagrant_ssh_password" { + description = "A password to use for SSH authentication" + type = string + default = "vagrant" +} + +variable "vagrant_shutdown_command" { + description = "The VM shutdown command" + type = string + default = "echo vagrant | sudo -S /sbin/shutdown -hP now" +}