From 372e56363338c0dd2f5bc5fa5f2e4d147685b23d Mon Sep 17 00:00:00 2001 From: paulober <44974737+paulober@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:28:59 +0100 Subject: [PATCH] Add cloud-init support Signed-off-by: paulober <44974737+paulober@users.noreply.github.com> --- README.md | 4 + build.sh | 2 + stage2/04-cloud-init/00-packages | 1 + stage2/04-cloud-init/01-run.sh | 28 +++++ stage2/04-cloud-init/README.txt | 14 +++ .../files/00-network-manager-all.yaml | 3 + .../04-cloud-init/files/99_raspberry-pi.cfg | 24 +++++ stage2/04-cloud-init/files/meta-data | 18 ++++ stage2/04-cloud-init/files/network-config | 50 +++++++++ stage2/04-cloud-init/files/user-data | 102 ++++++++++++++++++ 10 files changed, 246 insertions(+) create mode 100644 stage2/04-cloud-init/00-packages create mode 100755 stage2/04-cloud-init/01-run.sh create mode 100644 stage2/04-cloud-init/README.txt create mode 100644 stage2/04-cloud-init/files/00-network-manager-all.yaml create mode 100644 stage2/04-cloud-init/files/99_raspberry-pi.cfg create mode 100644 stage2/04-cloud-init/files/meta-data create mode 100644 stage2/04-cloud-init/files/network-config create mode 100644 stage2/04-cloud-init/files/user-data diff --git a/README.md b/README.md index 4f7002e..f2ec148 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,10 @@ The following environment variables are supported: If set, use this directory path as the location of scripts to run when generating images. An absolute or relative path can be given for a location outside the pi-gen directory. + * `ENABLE_CLOUD_INIT` (Default: `1`) + + If set to `1`, cloud-init and netplan will be installed and configured. This will allow you to configure your Raspberry Pi using cloud-init configuration files. The cloud-init configuration files should be placed in the bootfs or by editing the files in `stage2/04-cloud-init/files`. Cloud-init will be configured to read them on first boot. + A simple example for building Raspberry Pi OS: ```bash diff --git a/build.sh b/build.sh index 98cece8..8e7215a 100755 --- a/build.sh +++ b/build.sh @@ -244,6 +244,8 @@ export QUILT_NO_DIFF_INDEX=1 export QUILT_NO_DIFF_TIMESTAMPS=1 export QUILT_REFRESH_ARGS="-p ab" +export ENABLE_CLOUD_INIT=${ENABLE_CLOUD_INIT:-1} + # shellcheck source=scripts/common source "${SCRIPT_DIR}/common" # shellcheck source=scripts/dependencies_check diff --git a/stage2/04-cloud-init/00-packages b/stage2/04-cloud-init/00-packages new file mode 100644 index 0000000..db064e6 --- /dev/null +++ b/stage2/04-cloud-init/00-packages @@ -0,0 +1 @@ +cloud-init diff --git a/stage2/04-cloud-init/01-run.sh b/stage2/04-cloud-init/01-run.sh new file mode 100755 index 0000000..e4b8c64 --- /dev/null +++ b/stage2/04-cloud-init/01-run.sh @@ -0,0 +1,28 @@ +#!/bin/bash -e + +if [ "${ENABLE_CLOUD_INIT}" != "1" ]; then + log "Skipping cloud-init stage" + exit 0 +fi + +install -v -D -m 644 -t "${ROOTFS_DIR}/etc/cloud/cloud.cfg.d/" files/99_raspberry-pi.cfg + +# some preseeding without any runtime effect yet +# install meta-data file for NoCloud data-source to work +install -v -m 755 files/meta-data "${ROOTFS_DIR}/boot/firmware/meta-data" +install -v -m 755 files/user-data "${ROOTFS_DIR}/boot/firmware/user-data" +install -v -m 755 files/network-config "${ROOTFS_DIR}/boot/firmware/network-config" + +# setup default netplan config which will instruct netplan to pass control over to network-manager +# at boot time. This will make NetworkManager manage all devices and by default. +# Any Ethernet device will come up with DHCP, once carrier is detected +install -v -D -m 600 -t "${ROOTFS_DIR}/lib/netplan/" files/00-network-manager-all.yaml + +if [ -n "${FIRST_USER_NAME}" ]; then + # set the default user name to the one provided via FIRST_USER_NAME + # this will make cloud-init create the user with that name instead of 'pi' + sed -i "s/name: pi/name: ${FIRST_USER_NAME}/" "${ROOTFS_DIR}/etc/cloud/cloud.cfg" +else + # remove the users:\n - default section from cloud.cfg + sed -i "/^users:/,/^- default/d" "${ROOTFS_DIR}/etc/cloud/cloud.cfg" +fi diff --git a/stage2/04-cloud-init/README.txt b/stage2/04-cloud-init/README.txt new file mode 100644 index 0000000..cbe448d --- /dev/null +++ b/stage2/04-cloud-init/README.txt @@ -0,0 +1,14 @@ +Cloud-init support for Raspberry Pi OS + +Reference for Raspberry Pi custom cloud-init config module: https://cloudinit.readthedocs.io/en/latest/reference/modules.html#raspberry-pi-configuration + +- files/network-config is required because otherwise imager would fail to create the correct filesystem entry + +- files/user-data same reason and to include some example configurations + +- files/meta-data Cloud-init instance configuration + +- files/99_raspberry-pi.cfg Cloud-init datasource configuration + +- files/00-network-manager-all.yaml Example from netplan docs/ubuntu for handing over control from +netplan to NetworkManager by default. diff --git a/stage2/04-cloud-init/files/00-network-manager-all.yaml b/stage2/04-cloud-init/files/00-network-manager-all.yaml new file mode 100644 index 0000000..b654768 --- /dev/null +++ b/stage2/04-cloud-init/files/00-network-manager-all.yaml @@ -0,0 +1,3 @@ +network: + version: 2 + renderer: NetworkManager diff --git a/stage2/04-cloud-init/files/99_raspberry-pi.cfg b/stage2/04-cloud-init/files/99_raspberry-pi.cfg new file mode 100644 index 0000000..0b85080 --- /dev/null +++ b/stage2/04-cloud-init/files/99_raspberry-pi.cfg @@ -0,0 +1,24 @@ +# configure cloud-init with NoCloud + +datasource_list: [ NoCloud, None ] +datasource: + NoCloud: + seedfrom: file:///boot/firmware + +power_state: + delay: now + mode: reboot + message: Rebooting machine to enable usb gadget mode + condition: test -f /etc/modules-load.d/usb-gadget.conf + +# Disable SSH host key generation +# regenerate_ssh_host_keys.service will take care +# of it on first boot +ssh_deletekeys: false +# Disable generation as it could be that the new keys +# aren't available yet when the service runs. +# also they are really only needed for the ssh service +# which will only run after the keys are already present +# so we don't schedule cloud-init after key generation +# as that would delay first boot too much +ssh_genkeytypes: [] diff --git a/stage2/04-cloud-init/files/meta-data b/stage2/04-cloud-init/files/meta-data new file mode 100644 index 0000000..ac36efd --- /dev/null +++ b/stage2/04-cloud-init/files/meta-data @@ -0,0 +1,18 @@ +# This is the meta-data configuration file for cloud-init. Please refer to the +# cloud-init documentation for more information: +# +# https://cloudinit.readthedocs.io/ + +# Set the datasource mode to "local". This ensures that user-data is acted upon +# prior to bringing up the network (because everything about the datasource is +# assumed to be local). If you wish to use an HTTP datasource instead, you can +# change this to "net" or override it on the kernel cmdline (see README). +dsmode: local + +# Specifies the "unique" identifier of the instance. Typically in cloud-init +# this is generated by the owning cloud and is actually unique (to some +# degree). Here our data-source is local, so this is just a fixed string. +# Warning: changing this will cause cloud-init to assume it is running on a +# "new" instance, and to go through first time setup again (the value is +# compared to a cached copy). +instance_id: rpios-image diff --git a/stage2/04-cloud-init/files/network-config b/stage2/04-cloud-init/files/network-config new file mode 100644 index 0000000..2bfc7ad --- /dev/null +++ b/stage2/04-cloud-init/files/network-config @@ -0,0 +1,50 @@ +# This file contains a netplan-compatible configuration which cloud-init will +# apply on first-boot (note: it will *not* update the config after the first +# boot). Please refer to the cloud-init documentation and the netplan reference +# for full details: +# +# https://netplan.io/reference +# https://cloudinit.readthedocs.io/en/latest/topics/network-config.html +# https://cloudinit.readthedocs.io/en/latest/topics/network-config-format-v2.html +# +# Please note that the YAML format employed by this file is sensitive to +# differences in whitespace; if you are editing this file in an editor (like +# Notepad) which uses literal tabs, take care to only use spaces for +# indentation. See the following link for more details: +# +# https://en.wikipedia.org/wiki/YAML +# +# Additionally, please be aware that if your boot sequence depends on active +# networking (e.g. if your cloud-init configuration pulls packages or SSH +# keys from the network), you *must* mark at least one interface as required +# ("optional: false") below. Otherwise, particularly on faster boards, +# cloud-init will start attempting to use the network before it is ready + +# Some additional examples are commented out below + +#network: +# version: 2 +# +# ethernets: +# eth0: +# dhcp4: true +# optional: true + +# wifis: +# wlan0: +# dhcp4: true +# optional: true +# access-points: +# myhomewifi: +# password: "S3kr1t" +# myworkwifi: +# password: "correct battery horse staple" +# workssid: +# auth: +# key-management: eap +# method: peap +# identity: "me@example.com" +# password: "passw0rd" +# ca-certificate: /etc/my_ca.pem + +# regulatory-domain: GB diff --git a/stage2/04-cloud-init/files/user-data b/stage2/04-cloud-init/files/user-data new file mode 100644 index 0000000..ce34257 --- /dev/null +++ b/stage2/04-cloud-init/files/user-data @@ -0,0 +1,102 @@ +#cloud-config + +# This is the user-data configuration file for cloud-init. By default this sets +# up an initial user called "ubuntu" with password "ubuntu", which must be +# changed at first login. However, many additional actions can be initiated on +# first boot from this file. The cloud-init documentation has more details: +# +# https://cloudinit.readthedocs.io/ +# +# Please note that the YAML format employed by this file is sensitive to +# differences in whitespace; if you are editing this file in an editor (like +# Notepad) which uses literal tabs, take care to only use spaces for +# indentation. See the following link for more details: +# +# https://en.wikipedia.org/wiki/YAML +# +# Some additional examples are provided in comments below the default +# configuration. + +## Set the system's hostname. Please note that, unless you have a local DNS +## setup where the hostname is derived from DHCP requests (as with dnsmasq), +## setting the hostname here will not make the machine reachable by this name. +## You may also wish to install avahi-daemon (see the "packages:" key below) +## to make your machine reachable by the .local domain +#hostname: raspberrypi + +## Set up the keyboard layout. See localectl(1), in particular the various +## list-x11-* sub-commands, to determine the available models, layouts, +## variants, and options +#keyboard: +# model: pc105 +# layout: gb +# variant: +# options: ctrl:nocaps + +# Controls password authentication with the SSH daemon; the default here can +# prevent logging into SSH with a password. Changing this is a security risk +# and you should at the very least ensure a different default password is +# specified above +#ssh_pwauth: false + +## On first boot, use ssh-import-id to give the specific users SSH access to +## the default user +#ssh_import_id: +#- lp:my_launchpad_username +#- gh:my_github_username + +## Add users and groups to the system, and import keys with the ssh-import-id +## utility +#groups: +#- robot: [robot] +#- robotics: [robot] +#- pi +# +#users: +#- default +#- name: robot +# gecos: Mr. Robot +# primary_group: robot +# groups: users +# ssh_import_id: foobar +# lock_passwd: false +# passwd: $5$hkui88$nvZgIle31cNpryjRfO9uArF7DYiBcWEnjqq7L1AQNN3 + +## Update apt database and upgrade packages on first boot +#package_update: true +#package_upgrade: true + +## Install additional packages on first boot +#packages: +#- avahi-daemon +#- rng-tools +#- python3-gpiozero +#- [python3-serial, 3.5-1] + +## Write arbitrary files to the file-system (including binaries!) +#write_files: +#- path: /etc/default/console-setup +# content: | +# # Consult the console-setup(5) manual page. +# ACTIVE_CONSOLES="/dev/tty[1-6]" +# CHARMAP="UTF-8" +# VIDEOMODE= +# FONT="Lat15-Terminus18x10.psf.gz" +# FONTFACE= +# FONTSIZE= +# CODESET="Lat15" +# permissions: '0644' +# owner: root:root +#- encoding: gzip +# path: /root/Makefile +# content: !!binary | +# H4sICF2DTWIAA01ha2VmaWxlAFNWCM8syVBILMjPyU/PTC1WKMlXiPB2dlFQNjSx5MpNteLi +# dLDiSoRQxYl5KeWZyRkgXrSCkoqKRmaKgm6pppKCbmqhgoFCrIKamkK1QmpyRr6Ckn92YqWS +# NdC80uQMBZhOa4VahZoaqIrwjMQSewXfxOxUhcwShcr80qLi1Jw0RSUuAIYfEJmVAAAA +# owner: root:root +# permissions: '0644' + +## Run arbitrary commands at rc.local like time +#runcmd: +#- [ ls, -l, / ] +#- [ sh, -xc, "echo $(date) ': hello world!'" ]