From 78e049f6e0d5b6a8829aea943e91842419bc0f1f Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Thu, 8 Sep 2022 15:01:32 +0200 Subject: [PATCH] initial commit --- .dictionary | 6 + .drone.yml | 42 ++++++ .gitignore | 1 + .markdownlint.yml | 6 + LICENSE | 21 +++ README.md | 44 ++++++ renovate.json | 4 + rocky-9/data/files/90-hetznercloud.cfg | 69 +++++++++ rocky-9/data/files/92-hetznercloud-ds.cfg | 1 + rocky-9/data/files/93-hetznercloud.cfg | 9 ++ rocky-9/data/files/hcloud-metadata | 14 ++ rocky-9/data/files/hcloud-ssh-keys.service | 12 ++ rocky-9/data/init.ks.pkrtpl.hcl | 167 +++++++++++++++++++++ rocky-9/server.auto.pkrvars.hcl | 11 ++ rocky-9/server.pkr.hcl | 74 +++++++++ rocky-9/variables.pkr.hcl | 68 +++++++++ scripts/10-prepare-kickstart.sh | 35 +++++ scripts/20-rocky-9.sh | 61 ++++++++ test.sh | 14 ++ 19 files changed, 659 insertions(+) create mode 100644 .dictionary create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 .markdownlint.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 renovate.json create mode 100644 rocky-9/data/files/90-hetznercloud.cfg create mode 100644 rocky-9/data/files/92-hetznercloud-ds.cfg create mode 100644 rocky-9/data/files/93-hetznercloud.cfg create mode 100644 rocky-9/data/files/hcloud-metadata create mode 100644 rocky-9/data/files/hcloud-ssh-keys.service create mode 100644 rocky-9/data/init.ks.pkrtpl.hcl create mode 100644 rocky-9/server.auto.pkrvars.hcl create mode 100644 rocky-9/server.pkr.hcl create mode 100644 rocky-9/variables.pkr.hcl create mode 100644 scripts/10-prepare-kickstart.sh create mode 100644 scripts/20-rocky-9.sh create mode 100644 test.sh diff --git a/.dictionary b/.dictionary new file mode 100644 index 0000000..fe7f5cb --- /dev/null +++ b/.dictionary @@ -0,0 +1,6 @@ +packer-hcloud +(P|p)roxmox +HashiCorp +ISOs +jktr +hcloud-packer-templates diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..9ba228b --- /dev/null +++ b/.drone.yml @@ -0,0 +1,42 @@ +--- +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: c0676bb49df70f3e5aaa41597110a6124f0093a21e70c8bcb5e6a537a8295982 + +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a6353d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.envrc diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000..b59a114 --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,6 @@ +--- +default: True +MD013: False +MD041: False +MD004: + style: dash 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..ab7af0c --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# packer-hcloud + +[![Build Status](https://img.shields.io/drone/build/infra/packer-hcloud?logo=drone&server=https%3A%2F%2Fdrone.rknet.org)](https://drone.rknet.org/infra/packer-hcloud) +[![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 Hetzner Cloud using [HashiCorp Packer](https://www.packer.io) and the [Packer Plugin for Hetzner Cloud](https://www.packer.io/plugins/builders/hetzner-cloud). All examples are written in the HashiCorp Configuration Language `HCL2`. + +This project is inspired by [jktr/hcloud-packer-templates](https://github.com/jktr/hcloud-packer-templates). + +## Requirements + +**Packer**: + +- HashiCorp [Packer](https://www.packer.io/intro/getting-started/install.html) v1.7.7 or higher. +- HashiCorp [Packer Plugin for Hetzner Cloud](https://www.packer.io/plugins/builders/hetzner-cloud) (`hcloud`) v1.0.5 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 + +### Download the release + +Download the **latest** release. + +### 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. + +### 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/ +packer build rocky-9/ +``` + +## 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..0c407a6 --- /dev/null +++ b/renovate.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["github>thegeeklab/renovate-presets:default"] +} diff --git a/rocky-9/data/files/90-hetznercloud.cfg b/rocky-9/data/files/90-hetznercloud.cfg new file mode 100644 index 0000000..82829a4 --- /dev/null +++ b/rocky-9/data/files/90-hetznercloud.cfg @@ -0,0 +1,69 @@ +users: + - default + +disable_root: 0 +ssh_pwauth: 0 + +mount_default_fields: [~, ~, 'auto', 'defaults,nofail,x-systemd.requires=cloud-init.service', '0', '2'] +resize_rootfs_tmp: /dev +ssh_deletekeys: 1 +ssh_genkeytypes: ['rsa', 'ecdsa', 'ed25519'] +syslog_fix_perms: ~ +disable_vmware_customization: false + +cloud_init_modules: + - disk_setup + - migrator + - bootcmd + - write-files + - [ growpart, always ] + - [ resizefs, always ] + - set_hostname + - update_hostname + - [ update_etc_hosts, once-per-instance ] + - rsyslog + - users-groups + - ssh + +cloud_config_modules: + - mounts + - locale + - set-passwords + - rh_subscription + - yum-add-repo + - package-update-upgrade-install + - timezone + - puppet + - chef + - salt-minion + - mcollective + - disable-ec2-metadata + - runcmd + +cloud_final_modules: + - rightscale_userdata + - scripts-per-once + - scripts-per-boot + - scripts-per-instance + - scripts-user + - ssh-authkey-fingerprints + - keys-to-console + - phone-home + - final-message + - power-state-change + +system_info: + default_user: + name: cloud-user + lock_passwd: true + gecos: Cloud User + groups: [adm, systemd-journal] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + distro: rhel + paths: + cloud_dir: /var/lib/cloud + templates_dir: /etc/cloud/templates + ssh_svcname: sshd + +# vim:syntax=yaml diff --git a/rocky-9/data/files/92-hetznercloud-ds.cfg b/rocky-9/data/files/92-hetznercloud-ds.cfg new file mode 100644 index 0000000..b99bf58 --- /dev/null +++ b/rocky-9/data/files/92-hetznercloud-ds.cfg @@ -0,0 +1 @@ +datasource_list: [ Hetzner, None ] diff --git a/rocky-9/data/files/93-hetznercloud.cfg b/rocky-9/data/files/93-hetznercloud.cfg new file mode 100644 index 0000000..ec5740a --- /dev/null +++ b/rocky-9/data/files/93-hetznercloud.cfg @@ -0,0 +1,9 @@ +disable_root: 0 +ssh_pwauth: 1 + +# Set the distro defaults +system_info: + default_user: + name: root + shell: /bin/bash + lock_passwd: false diff --git a/rocky-9/data/files/hcloud-metadata b/rocky-9/data/files/hcloud-metadata new file mode 100644 index 0000000..59ad9e5 --- /dev/null +++ b/rocky-9/data/files/hcloud-metadata @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -eo pipefail + +case "$1" in +keys) + if [ ! -f /root/.ssh/authorized_keys ]; then + curl -SsfL http://169.254.169.254/hetzner/v1/metadata/public-keys | /usr/bin/jq -r '.[]' >/root/.ssh/authorized_keys + fi + ;; +*) + echo "Invalid command" + exit 3 + ;; +esac diff --git a/rocky-9/data/files/hcloud-ssh-keys.service b/rocky-9/data/files/hcloud-ssh-keys.service new file mode 100644 index 0000000..2944bb9 --- /dev/null +++ b/rocky-9/data/files/hcloud-ssh-keys.service @@ -0,0 +1,12 @@ +[Unit] +Description=Import hcloud root ssh keys +Requires=network-online.target +After=network-online.target +ConditionPathExists=!/root/.ssh/authorized_keys + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/hcloud-metadata keys + +[Install] +WantedBy=multi-user.target diff --git a/rocky-9/data/init.ks.pkrtpl.hcl b/rocky-9/data/init.ks.pkrtpl.hcl new file mode 100644 index 0000000..9655596 --- /dev/null +++ b/rocky-9/data/init.ks.pkrtpl.hcl @@ -0,0 +1,167 @@ +### Install from network +### 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 --device=link --activate --onboot=on + +### Use the Hetzner mirror for fast installations +url --url="${hcloud_mirror}/BaseOS/x86_64/os/" +repo --name="AppStream" --baseurl="${hcloud_mirror}/AppStream/x86_64/os/" + +### 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} + +### Set initial root password +rootpw --iscrypted ${build_password_encrypted} + +### 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=512 --label=BOOTFS +part /boot/efi --fstype vfat --size=512 --label=EFIFS +part pv.01 --size=19 --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=6000 --label=ROOTFS +logvol /home --fstype xfs --name=lv_home --vgname=vg00 --size=3000 --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=512 --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=512 --label=LOGFS --fsoptions="nosuid,noexec,nodev" +logvol /var/www --fstype xfs --name=lv_www --vgname=vg00 --size=1000 --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=512 --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 +jq +-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 +cat >/etc/cloud/cloud.cfg.d/90-hetznercloud.cfg </etc/cloud/cloud.cfg.d/92-hetznercloud-ds.cfg </etc/cloud/cloud.cfg.d/93-hetznercloud.cfg </usr/local/bin/hcloud-metadata <<\EOF +${files_hcloud_metadata} +EOF +chmod 700 /usr/local/bin/hcloud-metadata + +mkdir /root/.ssh/ +chmod 700 /root/.ssh/ +cat >/etc/systemd/system/hcloud-ssh-keys.service </boot/grub/grub.cfg < Update packages ...' +dnf -yq update +dnf -q 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 +rm -rf /root/.ssh/authorized_keys + +### 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 + +### Prepare cloud-init +echo '> Preparing cloud-init ...' +rm -f /etc/cloud/cloud-init.disabled + +### Clean the shell history +echo '> Cleaning the shell history ...' +unset HISTFILE +history -cw +echo >~/.bash_history +rm -f /root/.bash_history + +### Done +echo '> Done.' diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..59ad9e5 --- /dev/null +++ b/test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -eo pipefail + +case "$1" in +keys) + if [ ! -f /root/.ssh/authorized_keys ]; then + curl -SsfL http://169.254.169.254/hetzner/v1/metadata/public-keys | /usr/bin/jq -r '.[]' >/root/.ssh/authorized_keys + fi + ;; +*) + echo "Invalid command" + exit 3 + ;; +esac