commit 78e049f6e0d5b6a8829aea943e91842419bc0f1f Author: Robert Kaussow Date: Thu Sep 8 15:01:32 2022 +0200 initial commit 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