From 352601029ba29407aa15db3011cf6bc63bd51764 Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Sat, 6 Aug 2022 22:17:14 +0200 Subject: [PATCH] initial commit --- .dictionary | 4 + .drone.yml | 40 +++++++ .gitignore | 1 + LICENSE | 21 ++++ README.md | 48 ++++++++ renovate.json | 33 ++++++ rocky-9.0/data/init.ks.pkrtpl.hcl | 146 ++++++++++++++++++++++++ rocky-9.0/server.auto.pkrvars.hcl | 19 ++++ rocky-9.0/server.pkr.hcl | 86 ++++++++++++++ rocky-9.0/variables.pkr.hcl | 181 ++++++++++++++++++++++++++++++ scripts/rocky-9.x.sh | 60 ++++++++++ 11 files changed, 639 insertions(+) create mode 100644 .dictionary create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 renovate.json create mode 100644 rocky-9.0/data/init.ks.pkrtpl.hcl create mode 100644 rocky-9.0/server.auto.pkrvars.hcl create mode 100644 rocky-9.0/server.pkr.hcl create mode 100644 rocky-9.0/variables.pkr.hcl create mode 100644 scripts/rocky-9.x.sh diff --git a/.dictionary b/.dictionary new file mode 100644 index 0000000..3d35e1b --- /dev/null +++ b/.dictionary @@ -0,0 +1,4 @@ +packer-proxmox +(P|p)roxmox +HashiCorp +ISOs diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..037510f --- /dev/null +++ b/.drone.yml @@ -0,0 +1,40 @@ +--- +kind: pipeline +name: check + +platform: + os: linux + arch: amd64 + +steps: + - name: whitespace + image: thegeeklab/alpine-tools + commands: + - git diff-tree --check $(git hash-object -t tree /dev/null) HEAD + + - name: packer + image: hashicorp/packer + commands: + - packer fmt -recursive -check -diff . + + - name: markdownlint + image: thegeeklab/markdownlint-cli + commands: + - markdownlint 'README.md' + + - name: spellcheck + image: thegeeklab/alpine-tools + commands: + - spellchecker --files 'README.md' -d .dictionary -p spell indefinite-article syntax-urls --no-suggestions + environment: + FORCE_COLOR: true + +trigger: + ref: + - refs/heads/main + - refs/tags/** + - refs/pull/** + +--- +kind: signature +hmac: 198cfa135666f3da049d67fb619d2438a9770b2ecad822bb040b47ac228c518b diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a6353d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.envrc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3812eb4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Robert Kaussow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..344e622 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# packer-proxmox + +[![Build Status](https://img.shields.io/drone/build/ansible/packer-proxmox?logo=drone&server=https%3A%2F%2Fdrone.rknet.org)](https://drone.rknet.org/ansible/packer-proxmox) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?label=license)](LICENSE) + +This repository provides infrastructure-as-code examples to automate the creation of virtual machine images on Proxmox using [HashiCorp Packer](https://www.packer.io) and the [Packer Plugin for Proxmox](https://www.packer.io/plugins/builders/proxmox/iso). All examples are written in the HashiCorp Configuration Language `HCL2`. + +## Requirements + +**Packer**: + +- HashiCorp [Packer](packer-install) v1.7.7 or higher. +- HashiCorp [Packer Plugin for Proxmox](https://www.packer.io/plugins/builders/proxmox/iso) (`proxmox-iso`) v1.0.8 or higher. + +Required plugins are automatically downloaded during the `packer init` phase. These plugins are placed in the same directory as your Packer executable `/usr/local/bin` or `$HOME/.packer.d/plugins`. + +## Configuration + +### Step 1 - Download the release + +Download the **latest** release. + +### Step 2 - Download the Guest Operating Systems ISOs + +1. Download the required guest operating system ISO images (e.g. `Rocky-9.0-x86_64-minimal.iso`). +2. Obtain the checksum type (e.g. `sha256`, `md5`, etc.) and checksum value for each guest operating system `.iso` image. This will be used in the build input variables. +3. Upload the guest operating system `.iso` images to your Proxmox server. + +### Step 3 - Configure the Variables + +All available [variables](https://www.packer.io/docs/templates/hcl_templates/variables) are defined in the `variables.pkr.hcl` files. They can be overwritten using environment variables or in the `server.auto.pkrvars.hcl` file. + +### Step 4 - Modify the Configurations and Scripts (Optional) + +If required, modify the configuration and scripts files. + +## Build + +Initialize packer and start a build. + +```Shell +packer init rocky-9.0/ +packer build rocky-9.0/ +``` + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..e24516d --- /dev/null +++ b/renovate.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["github>thegeeklab/renovate-presets:ansible"], + "extractVersion": "^v?(?.*)$", + "packageRules": [ + { + "groupName": "vaultwarden stack", + "matchPackagePatterns": ["^thegeeklab/vaultwarden"] + }, + { + "groupName": "drone stack", + "matchPackagePatterns": ["^drone"] + }, + { + "matchPackagePatterns": ["^minio"], + "versioning": "regex:^(RELEASE\\.)?(?\\d{4})-(?\\d{2})-(?\\d{2})T\\S*?Z$", + "extractVersion": "^RELEASE\\.(?.*)$" + }, + { + "matchPackagePatterns": ["rpmbuild/cups"], + "versioning": "regex:^(?\\d+)\\.(?\\d+)\\.(?\\d+)(-(?\\d+))?$" + }, + { + "matchPackagePatterns": ["renovate/renovate"], + "schedule": ["before 3am on Monday"] + }, + { + "matchPackagePatterns": ["renovate/renovate"], + "matchUpdateTypes": ["minor", "patch"], + "automerge": true + } + ] +} diff --git a/rocky-9.0/data/init.ks.pkrtpl.hcl b/rocky-9.0/data/init.ks.pkrtpl.hcl new file mode 100644 index 0000000..548822e --- /dev/null +++ b/rocky-9.0/data/init.ks.pkrtpl.hcl @@ -0,0 +1,146 @@ +### Installs from the first attached CD-ROM/DVD on the system. +cdrom + +### Performs the kickstart installation in text mode. +### By default, kickstart installations are performed in graphical mode. +text + +### Accepts the End User License Agreement. +eula --agreed + +### Sets the language to use during installation and the default language to use on the installed system. +lang ${vm_guest_os_language} + +### Sets the default keyboard type for the system. +keyboard ${vm_guest_os_keyboard} + +### Configure network information for target system and activate network devices in the installer environment (optional) +### --onboot enable device at a boot time +### --device device to be activated and / or configured with the network command +### --bootproto method to obtain networking configuration for device (default dhcp) +### --noipv6 disable IPv6 on this device +### +### network --bootproto=static --ip=172.16.11.200 --netmask=255.255.255.0 --gateway=172.16.11.200 --nameserver=172.16.11.4 --hostname centos-linux-8 +network --bootproto=dhcp + +### The selected profile will restrict root login. +### Add a user that can login and escalate privileges. +user --name=${build_username} --iscrypted --password=${build_password_encrypted} --groups=wheel + +### Configure firewall settings for the system. +### --enabled reject incoming connections that are not in response to outbound requests +### --ssh allow sshd service through the firewall +firewall --enabled --ssh + +### Sets up the authentication options for the system. +### The SSDD profile sets sha512 to hash passwords. Passwords are shadowed by default +### See the manual page for authselect-profile for a complete list of possible options. +authselect select sssd + +### Sets the state of SELinux on the installed system. +### Defaults to enforcing. +selinux --enforcing + +### Sets the system time zone. +timezone ${vm_guest_os_timezone} --utc + +### Sets how the boot loader should be installed. +bootloader --location=mbr + +### Initialize any invalid partition tables found on disks. +zerombr + +### Removes partitions from the system, prior to creation of new partitions. +### By default, no partitions are removed. +### --linux erases all Linux partitions. +### --initlabel Initializes a disk (or disks) by creating a default disk label for all disks in their respective architecture. +clearpart --all --initlabel + +### Modify partition sizes for the virtual machine hardware. +### Create primary system partitions. +part /boot --fstype xfs --size=1024 --label=BOOTFS +part /boot/efi --fstype vfat --size=1024 --label=EFIFS +part pv.01 --size=25 --grow + +### Create a logical volume management (LVM) group. +volgroup vg00 --pesize=4096 pv.01 + +### Modify logical volume sizes for the virtual machine hardware. +### Create logical volumes. +logvol swap --fstype swap --name=lv_swap --vgname=vg00 --size=2048 --label=SWAPFS +logvol / --fstype xfs --name=lv_root --vgname=vg00 --size=8000 --label=ROOTFS +logvol /home --fstype xfs --name=lv_home --vgname=vg00 --size=4000 --label=HOMEFS +logvol /opt --fstype xfs --name=lv_opt --vgname=vg00 --size=1000 --label=OPTFS +logvol /tmp --fstype xfs --name=lv_tmp --vgname=vg00 --size=1000 --label=TMPFS --fsoptions="nosuid,noexec,nodev" +logvol /var --fstype xfs --name=lv_var --vgname=vg00 --size=2000 --label=VARFS --fsoptions="nosuid" +logvol /var/tmp --fstype xfs --name=lv_vartmp --vgname=vg00 --size=1000 --label=LOGFS --fsoptions="nosuid,noexec,nodev" +logvol /var/www --fstype xfs --name=lv_www --vgname=vg00 --size=2000 --label=LOGFS --fsoptions="nosuid,noexec,nodev" +logvol /var/log --fstype xfs --name=lv_log --vgname=vg00 --size=1000 --label=LOGFS --fsoptions="nosuid,noexec,nodev" +logvol /var/log/audit --fstype xfs --name=lv_audit --vgname=vg00 --size=1024 --label=AUDITFS --fsoptions="nosuid,noexec,nodev" + +### Modifies the default set of services that will run under the default runlevel. +services --enabled=NetworkManager,sshd + +### Do not configure X on the installed system. +skipx + +### Disable firstboot. +firstboot --disable + +### Packages selection. +%packages +@^minimal-environment +kexec-tools +openssh-server +openssh-clients +sudo +curl +python3 +python3-libselinux +qemu-guest-agent +-aic94xx-firmware +-atmel-firmware +-b43-openfwwf +-bfa-firmware +-ipw2100-firmware +-ipw2200-firmware +-ivtv-firmware +-iwl100-firmware +-iwl1000-firmware +-iwl3945-firmware +-iwl4965-firmware +-iwl5000-firmware +-iwl5150-firmware +-iwl6000-firmware +-iwl6000g2a-firmware +-iwl6050-firmware +-libertas-usb8388-firmware +-ql2100-firmware +-ql2200-firmware +-ql23xx-firmware +-ql2400-firmware +-ql2500-firmware +-rt61pci-firmware +-rt73usb-firmware +-xorg-x11-drv-ati-firmware +-zd1211-firmware +%end + +### Post-installation commands. +%post + +dnf makecache +dnf install -y epel-release +dnf makecache +dnf install -y cloud-init +dnf clean all + +touch /etc/cloud/cloud-init.disabled +echo "Completed cloud-init step!" +echo "${build_username} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/${build_username} +sed -i "s/^.*requiretty/#Defaults requiretty/" /etc/sudoers +%end + +### Reboot after the installation is complete. +### --eject attempt to eject the media before rebooting. +reboot --eject diff --git a/rocky-9.0/server.auto.pkrvars.hcl b/rocky-9.0/server.auto.pkrvars.hcl new file mode 100644 index 0000000..dc869fe --- /dev/null +++ b/rocky-9.0/server.auto.pkrvars.hcl @@ -0,0 +1,19 @@ +// Virtual Machine Settings +vm_id = 910 +vm_guest_os_keyboard = de(nodeadkeys) +vm_guest_os_timezone = "Europe/Berlin" + +// Proxmox Settings +proxmox_iso_pool = "local:iso" +proxmox_iso_file = "Rocky-9.0-x86_64-minimal.iso" + +template_description = "Rocky Linux 9.0 Template" +template_name = "rocky-90-cloud" + + +// Communicator Settings +communicator_port = 22 +communicator_timeout = "30m" + +// Provisioner Settings +scripts = ["scripts/rocky-9.x.sh"] diff --git a/rocky-9.0/server.pkr.hcl b/rocky-9.0/server.pkr.hcl new file mode 100644 index 0000000..b48482d --- /dev/null +++ b/rocky-9.0/server.pkr.hcl @@ -0,0 +1,86 @@ +packer { + required_version = ">= 1.8.3" + required_plugins { + proxmox = { + version = ">= 1.0.8" + source = "github.com/hashicorp/proxmox" + } + } +} + +locals { + buildtime = formatdate("YYYY-MM-DD hh:mm ZZZ", timestamp()) + data_source_content = { + "/ks.cfg" = templatefile("${abspath(path.root)}/data/init.ks.pkrtpl.hcl", { + build_username = var.build_username + build_password_encrypted = var.build_password_encrypted + vm_guest_os_language = var.vm_guest_os_language + vm_guest_os_keyboard = var.vm_guest_os_keyboard + vm_guest_os_timezone = var.vm_guest_os_timezone + }) + } + data_source_command = "inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg" +} + +source "proxmox-iso" "rocky-linux-90" { + // Proxmox Settings + proxmox_url = "${var.proxmox_url}" + node = "${var.proxmox_node}" + username = "${var.proxmox_username}" + password = "${var.proxmox_password}" + + // Virtual Machine Settings + vm_id = "${var.vm_id}" + cores = "${var.vm_cpu_cores}" + sockets = "${var.vm_cpu_sockets}" + cpu_type = "${var.vm_cpu_type}" + memory = "${var.vm_mem_size}" + os = "${var.vm_os_type}" + network_adapters { + bridge = "${var.vm_network_adapter_bridge}" + model = "${var.vm_network_adapter_model}" + vlan_tag = "${var.vm_network_adapter_vlan_tag}" + } + disks { + disk_size = "${var.vm_disk_size}" + format = "${var.proxmox_storage_format}" + storage_pool = "${var.proxmox_storage_pool}" + storage_pool_type = "${var.proxmox_storage_pool_type}" + type = "scsi" + } + scsi_controller = "virtio-scsi-pci" + + // Removable Media Settings + iso_file = "${var.proxmox_iso_pool}/${var.proxmox_iso_file}" + http_content = "${local.data_source_content}" + + // Boot and Provisioning Settings + boot_wait = "10s" + boot_command = [ + "up", + "", + " ${local.data_source_command}" + ] + + // Communicator Settings and Credentials + communicator = "ssh" + ssh_username = "${var.build_username}" + ssh_password = "${var.build_password}" + ssh_port = "${var.communicator_port}" + ssh_timeout = "${var.communicator_timeout}" + + template_description = "${var.template_description} on ${local.buildtime}" + template_name = "${var.template_name}" + unmount_iso = true +} + +build { + sources = ["source.proxmox-iso.rocky-linux-90"] + + provisioner "shell" { + execute_command = "echo '${var.build_password}' | {{.Vars}} sudo -E -S sh -eux '{{.Path}}'" + scripts = formatlist("${path.cwd}/%s", var.scripts) + remote_folder = "/home/${var.build_username}" + } + +} diff --git a/rocky-9.0/variables.pkr.hcl b/rocky-9.0/variables.pkr.hcl new file mode 100644 index 0000000..3522e80 --- /dev/null +++ b/rocky-9.0/variables.pkr.hcl @@ -0,0 +1,181 @@ +// Proxmox Settings +variable "proxmox_iso_file" { + type = string +} + +variable "proxmox_iso_pool" { + type = string + default = "local:iso" +} + +variable "proxmox_url" { + type = string + default = "" +} + +variable "proxmox_node" { + type = string + default = "" +} + +variable "proxmox_username" { + type = string + default = "" +} + +variable "proxmox_password" { + type = string + default = "" +} + +variable "proxmox_storage_format" { + type = string + default = "raw" +} + +variable "proxmox_storage_pool" { + type = string + default = "local-lvm" +} + +variable "proxmox_storage_pool_type" { + type = string + default = "lvm-thin" +} + +variable "template_description" { + type = string +} + +variable "template_name" { + type = string +} + +// Virtual Machine Settings +variable "vm_id" { + type = number + description = "The ID used to reference the virtual machine." + default = 0 +} + +variable "vm_guest_os_language" { + type = string + description = "The guest operating system lanugage." + default = "en_US" +} + +variable "vm_guest_os_keyboard" { + type = string + description = "The guest operating system keyboard input." + default = "us" +} + +variable "vm_guest_os_timezone" { + type = string + description = "The guest operating system timezone." + default = "America/New_York" +} + +variable "vm_os_type" { + type = string + description = "The operating system." + default = "l26" +} + +variable "vm_cpu_sockets" { + type = number + description = "The number of virtual CPUs sockets. (e.g. '2')" + default = 1 +} + +variable "vm_cpu_cores" { + type = number + description = "The number of virtual CPUs cores per socket. (e.g. '1')" + default = 1 +} + +variable "vm_cpu_type" { + type = string + description = "The CPU type to emulate." + default = "host" +} + +variable "vm_mem_size" { + type = number + description = "The size for the virtual memory in MB. (e.g. '2048')" + default = 1024 +} + +variable "vm_disk_size" { + type = string + description = "The size for the virtual disk in MB. (e.g. '8G')" + default = "32G" +} + +variable "vm_disk_controller_type" { + type = list(string) + description = "The virtual disk controller types in sequence. (e.g. 'pvscsi')" + default = ["pvscsi"] +} + +variable "vm_disk_thin_provisioned" { + type = bool + description = "Thin provision the virtual disk." + default = true +} + +variable "vm_network_adapter_model" { + type = string + description = "Model of the virtual network adapter. (e.g. 'vmxnet3' or 'e1000e')" + default = "e1000" +} + +variable "vm_network_adapter_bridge" { + type = string + description = "Which Proxmox bridge to attach the adapter to." + default = "vmbr0" +} + +variable "vm_network_adapter_vlan_tag" { + type = string + description = "If the adapter should tag packets." + default = "" +} + +// Communicator Settings and Credentials +variable "build_username" { + type = string + description = "The username to login to the guest operating system. (e.g. rainpole)" + sensitive = true +} + +variable "build_password" { + type = string + description = "The password to login to the guest operating system." + sensitive = true +} + +variable "build_password_encrypted" { + type = string + description = "The encrypted password to login the guest operating system." + sensitive = true +} + +variable "communicator_port" { + type = number + description = "The port for the communicator protocol." + default = 22 +} + +variable "communicator_timeout" { + type = string + description = "The timeout for the communicator protocol." + default = "30m" +} + +// Provisioner Settings +variable "scripts" { + type = list(string) + description = "A list of scripts and their relative paths to transfer and execute." + default = [] +} diff --git a/scripts/rocky-9.x.sh b/scripts/rocky-9.x.sh new file mode 100644 index 0000000..368b14e --- /dev/null +++ b/scripts/rocky-9.x.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -eo pipefail + +#### Update system +echo '> Update packages ...' +dnf update -y +dnf clean all + +### Cleans all audit logs. ### +echo '> Cleaning all audit logs ...' + +if [ -f /var/log/audit/audit.log ]; then + cat /dev/null >/var/log/audit/audit.log +fi + +if [ -f /var/log/wtmp ]; then + cat /dev/null >/var/log/wtmp +fi + +if [ -f /var/log/lastlog ]; then + cat /dev/null >/var/log/lastlog +fi + +### Cleans persistent udev rules. ### +echo '> Cleaning persistent udev rules ...' +if [ -f /etc/udev/rules.d/70-persistent-net.rules ]; then + rm /etc/udev/rules.d/70-persistent-net.rules +fi + +### Clean the /tmp directories. ### +echo '> Cleaning /tmp directories ...' +rm -rf /tmp/* +rm -rf /var/tmp/* +rm -rf /var/cache/dnf/* + +### Clean the SSH keys. ### +echo '> Cleaning the SSH keys ...' +shred -u /etc/ssh/*_key /etc/ssh/*_key.pub +rm -f /etc/ssh/ssh_config.d/allow-root-ssh.conf + +### Clean the machine-id. ### +echo '> Cleaning the machine-id ...' +truncate -s 0 /etc/machine-id +rm -f /var/lib/dbus/machine-id +mkdir -p /var/lib/dbus +ln -s /etc/machine-id /var/lib/dbus/machine-id + +### Clean the shell history. ### +echo '> Cleaning the shell history ...' +unset HISTFILE +history -cw +echo >~/.bash_history +rm -f /root/.bash_history + +### Prepare cloud-init ### +echo '> Preparing cloud-init ...' +rm -f /etc/cloud/cloud-init.disabled + +### Done. ### +echo '> Done.'