diff --git a/AzureMarketplace/marketplace-image.pkr.hcl b/AzureMarketplace/marketplace-image.pkr.hcl index 73950c1..6bda5d7 100644 --- a/AzureMarketplace/marketplace-image.pkr.hcl +++ b/AzureMarketplace/marketplace-image.pkr.hcl @@ -27,6 +27,15 @@ variable "docker_packages" { default = "docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin" } +# Pinned to a version above what apt noble-updates ships (currently 2.11.x). +# Azure Marketplace cert check 200.3.3.4 fails for unspecified reasons against +# the apt build; switch to a tarball install from upstream to remove the +# Canonical packaging as a variable. +variable "waagent_version" { + type = string + default = "2.15.0.1" +} + variable "subscription_id" { type = string default = "${env("AZURE_SUBSCRIPTION_ID")}" @@ -137,6 +146,11 @@ build { destination = "/tmp/bitwarden-first-login.sh" } + provisioner "file" { + source = "../CommonMarketplace/files/etc/systemd/system/disable-swap.service" + destination = "/tmp/disable-swap.service" + } + # Move staged files to their final system locations provisioner "shell" { inline = [ @@ -148,8 +162,10 @@ build { "sudo mv /tmp/install-lite.sh /opt/bitwarden/install-lite.sh", "sudo mv /tmp/001_onboot /var/lib/cloud/scripts/per-instance/001_onboot", "sudo mv /tmp/bitwarden-first-login.sh /etc/profile.d/bitwarden-first-login.sh", - "sudo chown root:root /etc/update-motd.d/99-bitwarden-welcome /etc/ufw/applications.d/bitwarden /opt/bitwarden/setup-wizard.sh /opt/bitwarden/install-standard.sh /opt/bitwarden/install-lite.sh /var/lib/cloud/scripts/per-instance/001_onboot /etc/profile.d/bitwarden-first-login.sh", - "sudo chmod 644 /etc/ufw/applications.d/bitwarden /etc/profile.d/bitwarden-first-login.sh" + "sudo mv /tmp/disable-swap.service /etc/systemd/system/disable-swap.service", + "sudo chown root:root /etc/update-motd.d/99-bitwarden-welcome /etc/ufw/applications.d/bitwarden /opt/bitwarden/setup-wizard.sh /opt/bitwarden/install-standard.sh /opt/bitwarden/install-lite.sh /var/lib/cloud/scripts/per-instance/001_onboot /etc/profile.d/bitwarden-first-login.sh /etc/systemd/system/disable-swap.service", + "sudo chmod 644 /etc/ufw/applications.d/bitwarden /etc/profile.d/bitwarden-first-login.sh /etc/systemd/system/disable-swap.service", + "sudo systemctl enable disable-swap.service" ] } @@ -177,7 +193,19 @@ build { provisioner "shell" { environment_vars = ["DEBIAN_FRONTEND=noninteractive"] inline = [ - "sudo apt-get -qqy -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install walinuxagent", + # Remove any walinuxagent shipped by Canonical so apt's version cannot + # override the upstream-source install on later upgrade. + "sudo apt-get -qqy purge walinuxagent || true", + "sudo apt-get -qqy -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install python3 python3-setuptools", + # Fetch the pinned upstream release and install via setup.py. The + # --register-service flag drops a systemd unit and a /etc/waagent.conf + # if missing. var.waagent_version is interpolated at template-compile + # time, not at shell-runtime. + "curl -fsSL -o /tmp/walinuxagent.tar.gz https://github.com/Azure/WALinuxAgent/archive/refs/tags/v${var.waagent_version}.tar.gz", + "sudo mkdir -p /opt/walinuxagent-src", + "sudo tar -xzf /tmp/walinuxagent.tar.gz -C /opt/walinuxagent-src --strip-components=1", + "cd /opt/walinuxagent-src && sudo python3 setup.py install --register-service", + "sudo rm -f /tmp/walinuxagent.tar.gz", "sudo systemctl enable walinuxagent", "waagent --version", ] @@ -202,7 +230,9 @@ build { ] } - # Azure-specific cleanup + # Azure-specific cleanup. First pass at history deletion runs here so the + # image is in a clean state before deprovision; a second pass runs after + # deprovision below to catch anything deprovision recreates. provisioner "shell" { execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E bash '{{ .Path }}'" environment_vars = [ @@ -211,15 +241,23 @@ build { ] inline = [ "truncate -s 0 /var/log/waagent.log 2>/dev/null || true", - "find / -xdev -name '.bash_history' -type f -delete 2>/dev/null || true", + "find / -name '.bash_history' -type f -delete 2>/dev/null || true", ] } - # Azure generalization - must be the last provisioner + # Azure generalization - must be the last provisioner. + # Runs `sh` (dash) which does not write bash history, but waagent itself + # may shell out via bash during deprovision and recreate /root/.bash_history. + # After deprovision finishes, sweep again with HISTFILE=/dev/null so the + # captured disk has no .bash_history regardless of who recreated it. provisioner "shell" { execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'" + environment_vars = [ + "HISTFILE=/dev/null", + "HISTSIZE=0", + ] inline = [ - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" + "/usr/sbin/waagent -force -deprovision+user && find / -name '.bash_history' -type f -delete 2>/dev/null; export HISTSIZE=0 && sync" ] } diff --git a/AzureMarketplace/scripts/99-img-check.sh b/AzureMarketplace/scripts/99-img-check.sh index 8a1dba7..ea89386 100644 --- a/AzureMarketplace/scripts/99-img-check.sh +++ b/AzureMarketplace/scripts/99-img-check.sh @@ -57,16 +57,18 @@ else STATUS=2 fi -# Check Azure Linux Agent version (minimum 2.7.x required) +# Check Azure Linux Agent version (minimum 2.13.x required by Azure Marketplace +# cert 200.3.3.4; the apt-installed 2.11 from noble-updates is below this +# floor and the packer build installs walinuxagent from upstream source). if hash waagent 2>/dev/null; then WAAGENT_VERSION=$(waagent --version 2>&1 | head -1 | grep -oP '(?<=WALinuxAgent-)\d+\.\d+' | head -1) WAAGENT_MAJOR=$(echo "${WAAGENT_VERSION}" | cut -d. -f1) WAAGENT_MINOR=$(echo "${WAAGENT_VERSION}" | cut -d. -f2) - if [[ "${WAAGENT_MAJOR}" -gt 2 ]] || ([[ "${WAAGENT_MAJOR}" -eq 2 ]] && [[ "${WAAGENT_MINOR}" -ge 7 ]]); then + if [[ "${WAAGENT_MAJOR}" -gt 2 ]] || ([[ "${WAAGENT_MAJOR}" -eq 2 ]] && [[ "${WAAGENT_MINOR}" -ge 13 ]]); then echo -en "\e[32m[PASS]\e[0m Azure Linux Agent version ${WAAGENT_VERSION} meets minimum requirement.\n" ((PASS++)) else - echo -en "\e[41m[FAIL]\e[0m Azure Linux Agent version ${WAAGENT_VERSION} is below minimum supported version (2.7.x).\n" + echo -en "\e[41m[FAIL]\e[0m Azure Linux Agent version ${WAAGENT_VERSION} is below minimum supported version (2.13.x).\n" ((FAIL++)) STATUS=2 fi diff --git a/CommonMarketplace/files/etc/systemd/system/disable-swap.service b/CommonMarketplace/files/etc/systemd/system/disable-swap.service new file mode 100644 index 0000000..a1ccc6b --- /dev/null +++ b/CommonMarketplace/files/etc/systemd/system/disable-swap.service @@ -0,0 +1,15 @@ +[Unit] +Description=Disable swap (Azure Marketplace 200.3.3.3 requirement) +# Run after both the Azure linux agent and cloud-init's final stage so we +# kill any swap they created on first boot before MPC's probe samples. +After=walinuxagent.service cloud-final.service +Wants=cloud-final.service + +[Service] +Type=oneshot +ExecStart=/usr/sbin/swapoff -a +ExecStartPost=-/bin/rm -f /swap.img /swapfile +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/CommonMarketplace/scripts/90-cleanup.sh b/CommonMarketplace/scripts/90-cleanup.sh index 66f93a7..2343c27 100644 --- a/CommonMarketplace/scripts/90-cleanup.sh +++ b/CommonMarketplace/scripts/90-cleanup.sh @@ -44,8 +44,21 @@ EOF chmod 644 /etc/cloud/cloud.cfg.d/99-disable-swap.cfg # Configure SSH client alive interval (Azure requirement: 30-235 seconds). -# Use a drop-in that sorts before /etc/ssh/sshd_config.d/50-cloud-init.conf so -# this setting wins — sshd uses the first occurrence of each directive. +# Write to BOTH the main sshd_config and a drop-in: +# - Main file: satisfies Azure's certification probe, which appears to do a +# literal grep of /etc/ssh/sshd_config and does not honor Include'd drop-ins. +# - Drop-in: wins at sshd runtime against any later cloud-init drop-in +# because /etc/ssh/sshd_config.d/*.conf is sourced in lexical order and +# sshd uses the first occurrence of each directive. +for directive in "ClientAliveInterval 120" "ClientAliveCountMax 3"; do + key="${directive%% *}" + if grep -qE "^[#[:space:]]*${key}\b" /etc/ssh/sshd_config; then + sed -i "s|^[#[:space:]]*${key}\b.*|${directive}|" /etc/ssh/sshd_config + else + echo "${directive}" >> /etc/ssh/sshd_config + fi +done + cat > /etc/ssh/sshd_config.d/10-bitwarden-marketplace.conf <<'EOF' ClientAliveInterval 120 ClientAliveCountMax 3