ubuntu-cloud-init — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ubuntu-cloud-init (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
cloud-init runs early in boot, reads configuration from a datasource, and applies it through modules across four boot stages. This skill is authoring-led (produce correct cloud-config from a description) with a strong validation/debug path, aimed at on-prem / air-gapped Ubuntu Server LTS using the NoCloud datasource.
Mapping: 24.04 LTS ≈ cloud-init 24.x; 26.04 LTS ≈ 26.1.
cidata ISO/USB, HTTP seed, or SMBIOS serial). Pin datasource_list: [NoCloud] to stop cloud-init probing cloud metadata services and timing out.
#cloud-config. Use MIME multipartonly to combine cloud-config with scripts. (Formats table below.)
cloud-init schema -c user-data --annotate.instance-id in meta-data.cloud-init clean --logs to re-run as iffirst boot.
First line determines the format:
| Header (line 1) | Format |
|---|---|
#cloud-config | YAML config processed by modules (the usual choice). |
#!/bin/sh (shebang) | A script, run once per instance in the Final stage. |
#cloud-boothook | Runs very early, every boot (guard with cloud-init-per). |
#include | List of URLs, each fetched as user-data. |
Content-Type: multipart/mixed | MIME — combine cloud-config + scripts. Build with cloud-init devel make-mime. |
## template: jinja | Jinja template (line 1); real header on line 2. Variables = instance-data keys, e.g. {{ v1.instance_id }}. |
Any of these may be gzipped.
users / ssh (created in the Network stage):
#cloud-config
ssh_pwauth: false
users:
- default # KEEP this first to retain the distro user + cloud keys
- name: deploy
groups: [sudo]
sudo: "ALL=(ALL) NOPASSWD:ALL"
lock_passwd: true
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA... deploy@sitepasswd (a hash) applies only to new users; hashed_passwd applies even to existing ones. Generate a hash with mkpasswd --method=SHA-512 --rounds=4096.
write_files:
write_files:
- path: /etc/myapp/app.conf
owner: root:root
permissions: '0640'
content: |
backend = db.internal:5432
- encoding: b64 # also gz, gz+b64
content: aGVsbG8K
path: /etc/motd.d/banner
defer: true # write in Final stage (after users/packages)runcmd vs bootcmd: runcmd runs once per instance in the Config stage (later); bootcmd runs early on every boot (guard once-only work with cloud-init-per once <name> ...).
runcmd:
- [systemctl, enable, --now, myapp]apt — local mirror + internal repo (the air-gapped essential):
apt:
preserve_sources_list: false
primary:
- arches: [default]
uri: http://mirror.internal/ubuntu
security:
- arches: [default]
uri: http://mirror.internal/ubuntu
sources:
internal-app.list:
source: "deb [signed-by=$KEY_FILE] http://mirror.internal/app stable main"
key: |
-----BEGIN PGP PUBLIC KEY BLOCK-----
...embed the key inline; do NOT rely on a keyserver air-gapped...
-----END PGP PUBLIC KEY BLOCK-----
conf: |
Acquire::http::Proxy "http://apt-cache.internal:3142";ca_certs — trust an internal CA (runs in the Network stage, before apt, so HTTPS to the internal mirror works):
ca_certs:
trusted:
- |
-----BEGIN CERTIFICATE-----
<internal root CA>
-----END CERTIFICATE-----ntp — point at local time source:
ntp:
enabled: true
servers: [ntp.internal]
pools: []Other on-prem-relevant modules: packages/package_update/package_upgrade (avoid package_upgrade without a full mirror), disk_setup+fs_setup+mounts, growpart/resize_rootfs, timezone/locale/keyboard, set_passwords/chpasswd, seed_random, power_state, hostname (hostname/fqdn/manage_etc_hosts). Avoid `phone_home` air-gapped — it only calls out to a URL and retries on failure. Full per-module detail (keys, snippets, stage, frequency) in references/modules.md.
cloud-init finds NoCloud config from (in precedence order) SMBIOS serial → kernel cmdline → seed dirs → labeled block device. The common methods:
meta-data + user-data (and optionally vendor-data,network-config) into /var/lib/cloud/seed/nocloud/.
vfat/iso9660 filesystem labeled CIDATA (case-insensitive)containing those files in its root. genisoimage -output seed.iso -volid cidata -joliet -rock user-data meta-data network-config (or cloud-localds seed.iso user-data meta-data).
ds=nocloud;s=http://10.0.0.1:8000/ (the schemedecides local vs network; trailing slash required — files are appended as <uri>/user-data etc.).
-smbios type=1,serial=ds=nocloud;s=http://10.0.0.1:8000/.Required files: meta-data and user-data. Optional: vendor-data, network-config. Since cloud-init 24.3, an HTTP/seed source that omits network-config triggers a boot retry/timeout — ship an empty `network-config` for back-compat, or a real netplan-v2 one.
`instance-id` re-run rule: cloud-init only re-applies user-data when the instance-id in meta-data changes (or after cloud-init clean). Bump it whenever the config changes.
Full seeding detail (cmdline grammar + aliases, DMI variable expansion, FTP, GRUB escaping, seedfrom, dsname deprecations) is in references/nocloud-and-airgapped.md.
cloud-init network-config has v1 (its own schema) and v2 (which IS netplan format). On Ubuntu the renderer is netplan; cloud-init writes /etc/netplan/50-cloud-init.yaml. user-data cannot set network config — networking comes from the datasource, system config, or kernel cmdline.
To author the network block, or to hand network control back to netplan entirely (network: {config: disabled} in /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg), use the ubuntu-netplan skill.
cloud-init schema -c user-data --annotate # validate a file, errors inline
sudo cloud-init schema --system --annotate # validate the live system's user-data
cloud-init status --long --wait # 0=ok, 1=error, 2=recoverable error
cloud-init query --all # inspect instance-data
sudo cloud-init clean --logs # wipe state → next boot is "first boot"Logs: /var/log/cloud-init.log, /var/log/cloud-init-output.log, /run/cloud-init/ (incl. ds-identify.log, result.json, instance-data.json). Config: /etc/cloud/cloud.cfg + /etc/cloud/cloud.cfg.d/*.cfg. Full CLI, the boot stages, and re-run/golden-image notes in references/cli-and-debugging.md.
datasource_list: [NoCloud] (or [NoCloud, None]) in a cloud.cfg.d drop-in— stops cloud probing/timeouts.
apt module; embed repo signing keys inline.ca_certs.trusted.ntp.servers.phone_home; don't package_upgrade without a full mirror; avoid snapunless a local store / pre-acked assertions are available.
vendor-data for site-wide defaults (mirror, CA, NTP, base users); per-hostuser-data overrides it.
is itself driven by a NoCloud user-data carrying a top-level autoinstall: key; cloud-init ignores autoinstall and passes it to Subiquity. Inside that document, the nested user-data: is cloud-config for the installed system (first boot).
references/nocloud-and-airgapped.md — every NoCloud seeding method, cmdlinegrammar, SMBIOS/DMI, instance-id, datasource_list pinning, DataSourceNone, vendor-data, deprecations.
references/modules.md — full cloud-config module reference (on-prem modules indepth; the rest listed by stage). Has a TOC.
references/cli-and-debugging.md — CLI, boot stages & module ordering, re-run /golden-image, version notes (24.x vs 26.1).
references/examples.md — minimal and realistic air-gapped configs with matchingmeta-data.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.