{
  "id": "CB-UBU-SVC-028",
  "standard": "ubuntu",
  "version": "0.1.2",
  "url": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-SVC-028",
  "json": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-SVC-028.json",
  "yaml": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-SVC-028.yml",
  "profiles_effective": [
    "server-hardened"
  ],
  "license": "Text CC BY 4.0, code Apache-2.0",
  "attribution": "CIRSEC Baselines, https://baselines.cirsec.org",
  "control": {
    "id": "CB-UBU-SVC-028",
    "title": "Disable cloud-init once the server is provisioned",
    "finding": {
      "title": "cloud-init still runs at every boot on a provisioned server",
      "description": "Whoever can change the instance metadata or attach a configuration disk can run commands as root at the next boot."
    },
    "status": "active",
    "profiles": [
      "server-hardened"
    ],
    "risk": {
      "severity": "medium"
    },
    "assessment": "automated",
    "tags": [
      "services",
      "cloud-init",
      "provisioning",
      "attack-surface"
    ],
    "applicability": {
      "facts": [
        "cloud-init"
      ]
    },
    "description": "cloud-init configures a new instance from its datasource: the metadata service of the cloud\nprovider (IMDS), or a configuration disk such as a NoCloud `cidata` volume or an OpenStack\nconfig drive. It is installed on every Ubuntu cloud image and on servers installed with\nSubiquity, and it starts at every boot: it detects the datasource again, runs its per-boot\nmodules (`bootcmd`, `scripts-per-boot`, hostname and `/etc/hosts` updates), and when the\ninstance ID changes it runs every first-boot module again (users, SSH keys, packages, user-data\nscripts).\n\nOnce the server is provisioned and managed by the organization's own tools, the file\n`/etc/cloud/cloud-init.disabled` (or `cloud-init=disabled` on the kernel command line) stops\ncloud-init from running at boot. `cloud-init status` then prints `disabled`.\n",
    "rationale": "The datasource is a root-level configuration channel that is not covered by the server's own\nchange management. A person who can edit the user-data in the cloud console, answer as the\nmetadata service on a network without IMDS protection, or attach a disk labelled `cidata` to the\nmachine can add an SSH key, create an account or run a script as root at the next boot. Stopping\ncloud-init after provisioning closes that channel and keeps the network, users and keys under\nconfiguration management only.\n",
    "impact": "From the next boot, cloud-init no longer applies changes coming from the platform:\n\n- **network**: `/etc/netplan/50-cloud-init.yaml` stays as written at provisioning and is no\n  longer regenerated. When it matches the interface by MAC address, a new or replaced network\n  card (another MAC address, a VM moved to a new network) comes up without network;\n- **disk**: the root partition and file system are no longer grown after the disk is enlarged\n  (`growpart`, `resizefs`): run `growpart` and `resize2fs` by hand;\n- **identity**: hostname, SSH keys and users from the metadata are no longer updated, and a\n  cloned VM keeps the SSH host keys of its source.\n\nRe-enable cloud-init (remove the file and reboot) before resizing, moving or cloning the server\nthrough the cloud platform. Do not apply this control to images or templates, and keep it off\nfor autoscaled instances that the platform must be able to rebuild.\n",
    "notes": "The fix only writes the file when `cloud-init status` reports `done`, so that it never stops a\nprovisioning that is still running or failed. The fix is disabled by default (`lockout_risk`):\na server whose network depends on cloud-init can become unreachable after a hardware change.\nRemoving the `cloud-init` package also meets the goal, but it is harder to undo and some\nplatforms (Azure, Proxmox templates) expect it to be present. The QEMU guest agent and the SSH\nserver keep working: they do not depend on cloud-init once the system is provisioned.\n",
    "setting": {
      "location": "/etc/cloud/cloud-init.disabled",
      "default_value": "Absent (cloud-init runs at every boot)",
      "recommended_value": "Present"
    },
    "audit": {
      "manual": "1. Check that `/etc/cloud/cloud-init.disabled` exists, or that `/proc/cmdline` contains\n   `cloud-init=disabled`.\n2. After a reboot, `cloud-init status` must print `status: disabled`.\n",
      "checks": [
        {
          "id": "disabled",
          "engine": "shell",
          "description": "cloud-init is disabled by /etc/cloud/cloud-init.disabled or the kernel command line.",
          "spec": {
            "script": "if [ -e /etc/cloud/cloud-init.disabled ]; then\n  echo \"/etc/cloud/cloud-init.disabled present\"\n  exit 0\nfi\nif grep -qw 'cloud-init=disabled' /proc/cmdline; then\n  echo \"cloud-init=disabled on the kernel command line\"\n  exit 0\nfi\ns=$(cloud-init status 2>/dev/null | sed -n 's/^status: //p')\necho \"cloud-init enabled (status: ${s:-unknown})\"\nexit 1\n"
          }
        }
      ]
    },
    "remediation": {
      "manual": "1. Check that provisioning is over: `cloud-init status` must print `status: done`.\n2. Check that `/etc/netplan/` holds the network configuration the server needs without\n   cloud-init (and adapt any `match: macaddress` if the hardware may change).\n3. Run `touch /etc/cloud/cloud-init.disabled`, reboot, and check that `cloud-init status`\n   prints `status: disabled` and that the network and SSH access still work.\n",
      "effects": {
        "restart": "reboot",
        "disruption": "low",
        "lockout_risk": true
      },
      "rollback": "Delete `/etc/cloud/cloud-init.disabled` and reboot. If the instance ID has not changed,\ncloud-init only runs its per-boot modules again.\n",
      "scripts": [
        {
          "id": "disable",
          "engine": "shell",
          "content": "f=/etc/cloud/cloud-init.disabled\n[ -e \"$f\" ] && exit 0\ns=$(cloud-init status 2>/dev/null | sed -n 's/^status: //p')\nif [ \"$s\" != done ]; then\n  echo \"cloud-init provisioning is not done (status: ${s:-unknown}), not disabling it\" >&2\n  exit 1\nfi\nprintf '# Written by CIRSEC Baselines CB-UBU-SVC-028: cloud-init does not run at boot.\\n' >\"$f\"\n"
        }
      ]
    },
    "references": [
      {
        "title": "cloud-init documentation - How to disable cloud-init",
        "url": "https://docs.cloud-init.io/en/latest/howto/disable_cloud_init.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "cloud-init documentation - Boot stages",
        "url": "https://docs.cloud-init.io/en/latest/explanation/boot.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "cloud-init documentation - NoCloud datasource",
        "url": "https://docs.cloud-init.io/en/latest/reference/datasources/nocloud.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "cloud-init(1), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man1/cloud-init.1.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "ANSSI-BP-028 v2.0 - Recommandations de configuration d'un système GNU/Linux (R62)",
        "url": "https://messervices.cyber.gouv.fr/documents-guides/fr_np_linux_configuration-v2.0.pdf",
        "publisher": "ANSSI",
        "kind": "guide",
        "accessed": "2026-10-19"
      }
    ],
    "mappings": [
      {
        "framework": "anssi-linux",
        "framework_version": "2.0",
        "id": "R62",
        "relation": "partial",
        "note": "R62 asks to disable every unneeded service; this control covers cloud-init after provisioning."
      },
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "CM-7",
        "relation": "partial"
      },
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "CM-5",
        "relation": "related"
      }
    ],
    "threats": [
      {
        "framework": "mitre-attack",
        "id": "T1037"
      },
      {
        "framework": "mitre-attack",
        "id": "T1098.004"
      },
      {
        "framework": "mitre-attack",
        "id": "T1552.005"
      }
    ],
    "authors": [
      {
        "name": "Alice",
        "github": "Sn0wAlice"
      }
    ],
    "verified": [
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "20.04"
        ],
        "environment": "Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic",
        "notes": "Stock image fails (status: done); fix only with CB_ENABLE_CB_UBU_SVC_028=1 (lockout_risk), writes cloud-init.disabled; after reboot status: disabled, network (DHCP from the cloud-init netplan file), SSH and guest agent work, also after installing and removing about 30 packages."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "22.04"
        ],
        "environment": "Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic",
        "notes": "Stock image fails (status: done); fix only with CB_ENABLE_CB_UBU_SVC_028=1 (lockout_risk), writes cloud-init.disabled; after reboot status: disabled, network (DHCP from the cloud-init netplan file), SSH and guest agent work, also after installing and removing about 30 packages."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "24.04"
        ],
        "environment": "Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic",
        "notes": "Stock image fails (status: done); fix only with CB_ENABLE_CB_UBU_SVC_028=1 (lockout_risk), writes cloud-init.disabled; after reboot status: disabled, network (DHCP from the cloud-init netplan file), SSH and guest agent work, also after installing and removing about 30 packages."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "26.04"
        ],
        "environment": "Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic",
        "notes": "Stock image fails (status: done); fix only with CB_ENABLE_CB_UBU_SVC_028=1 (lockout_risk), writes cloud-init.disabled; after reboot status: disabled, network (DHCP from the cloud-init netplan file), SSH and guest agent work, also after installing and removing about 30 packages."
      }
    ],
    "introduced_in": "0.1.0",
    "created": "2026-10-19",
    "updated": "2026-10-19"
  }
}