From 5c5ddc8f51c8efd79f0e2d5028cbaf9706d70f26 Mon Sep 17 00:00:00 2001 From: honsma235 <114189750+honsma235@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:23:57 +0200 Subject: [PATCH] make rauc updates possible fix self-signing and upload rauc bundle --- .github/workflows/build.yaml | 39 +++++++- README.md | 50 +++++++++- buildroot-external/ota/rauc-hook | 19 ++++ buildroot-external/ota/rockpi-ca.pem | 95 +++++++++++++++++++ buildroot-external/ota/system.conf.gtpl | 1 + .../system/rauc.service.d/haos-rockpi.conf | 7 ++ buildroot-external/scripts/rauc.sh | 7 +- 7 files changed, 206 insertions(+), 12 deletions(-) create mode 100644 buildroot-external/ota/rockpi-ca.pem create mode 100644 buildroot-external/rootfs-overlay/usr/lib/systemd/system/rauc.service.d/haos-rockpi.conf diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d18bee351ed..5c2dd9bbb25 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -202,6 +202,21 @@ jobs: run: | sed -i -E "s/(^VERSION_SUFFIX=\").*(\"$)/\1${VERSION_DEV}\2/" buildroot-external/meta + - name: 'Add release PKI certs' + if: ${{ needs.prepare.outputs.self_signed_cert != 'true' }} + env: + RAUC_CERTIFICATE: ${{ secrets.RAUC_CERTIFICATE }} + RAUC_PRIVATE_KEY: ${{ secrets.RAUC_PRIVATE_KEY }} + run: | + echo -e "-----BEGIN CERTIFICATE-----\n${RAUC_CERTIFICATE}\n-----END CERTIFICATE-----" > cert.pem + echo -e "-----BEGIN PRIVATE KEY-----\n${RAUC_PRIVATE_KEY}\n-----END PRIVATE KEY-----" > key.pem + + - name: Get self-signed certificate from the prepare job + if: ${{ needs.prepare.outputs.self_signed_cert == 'true' }} + uses: actions/download-artifact@v4 + with: + name: signing-key + - name: Build run: | BUILDER_UID="$(id -u)" @@ -224,8 +239,26 @@ jobs: BR2_CHECK_DOTCONFIG_OPTS="--github-format --strip-path-prefix=/build/" linux-check-dotconfig - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: haos-image - path: output/images/haos_*.img.xz + name: haos-image-${{ matrix.board.id }} + path: | + output/images/haos_*.img.xz + output/images/haos_*.raucb if-no-files-found: error + + - name: Generate build summary + run: | + echo "# ${{ matrix.board.id }} build summary" >> $GITHUB_STEP_SUMMARY + echo "## Artifacts" >> $GITHUB_STEP_SUMMARY + echo "| File | Size (bytes) | Size (formatted) |" >> $GITHUB_STEP_SUMMARY + echo "|:-|:-|:-|" >> $GITHUB_STEP_SUMMARY + for f in output/images/haos_*; do + echo "| $(basename $f) | $(du -b $f | cut -f1) | $(du -bh $f | cut -f1) |" >> $GITHUB_STEP_SUMMARY + done + echo "## Partitions" >> $GITHUB_STEP_SUMMARY + echo "| File | Size (bytes) | Size (formatted) |" >> $GITHUB_STEP_SUMMARY + echo "|:-|:-|:-|" >> $GITHUB_STEP_SUMMARY + for f in boot.vfat kernel.img rootfs.erofs overlay.ext4 data.ext4; do + echo "| ${f} | $(du -b output/images/$f | cut -f1) | $(du -bh output/images/$f | cut -f1) |" >> $GITHUB_STEP_SUMMARY + done diff --git a/README.md b/README.md index 5503ae8511f..82bb3073538 100644 --- a/README.md +++ b/README.md @@ -76,11 +76,55 @@ With this setup you always need to have an SD card inserted from which the board 1. Insert SD card into board and boot 1. There might be some error messages like `find_valid_gpt: *** ERROR: Invalid GPT ***`. These are expected because U-Boot will attempt to find the boot partition on SD card first (it's not there). It should then recognize the NVMe SSD and boot from it. -## Automatic updates +## Upgrading -Since this is an unofficial fork of Homeassistant OS, the OS image cannot be updated over the air (supervisor, core and all other components can be updated just fine). You will need to reflash for each release. +Since this is an unofficial fork of Homeassistant OS, the OS image cannot be updated from the UI (supervisor, core and all other components can be updated just fine). Instead, the update process is triggered via SSH from the command line. + +**Always create a full backup before upgrading to avoid data loss.** + +Use the [HassOS SSH port Configurator](https://community.home-assistant.io/t/add-on-hassos-ssh-port-22222-configurator/264109) to enable ssh root access on port 22222 (the "Terminal & SSH" Add-on will not work as it only exposes a limited shell in a container). + +Connect to the device and run the following command. Replace the URL with the appropriate link from the Releases page for your device. Make sure to copy the `.raucb` URL, not the `.img.xz`. + +``` +rauc install https://github.com/citruz/haos-rockpi/releases/download//haos_-.raucb +``` + +The file will be downloaded to a temporary location in `/mnt/data/tmp/` so make sure that there is enough space on the data partition left. + +``` +# rauc install https://github.com/citruz/haos-rockpi/releases/download/ota-test/haos_rockpi-4b-plus-12.2.dev20240502.raucb +installing + 0% Installing + 0% Determining slot states + 10% Determining slot states done. + 10% Checking bundle + 10% Verifying signature + 20% Verifying signature done. + 20% Checking bundle done. + 20% Checking manifest contents + 30% Checking manifest contents done. + 30% Determining target install group + 40% Determining target install group done. + 40% Updating slots + 40% Checking slot boot.0 + 41% Checking slot boot.0 done. + 41% Copying image to boot.0 + ... + 99% Copying image to spl.0 done. + 99% Updating slots done. +100% Installing done. +Installing `https://github.com/citruz/haos-rockpi/releases/download/ota-test/haos_rockpi-4b-plus-12.2.dev20240502.raucb` succeeded +``` + +Now reboot the device to switch to the new OS version. + +This process also updates the bootloader. If it detects that the system is booted from an SSD it will update the bootloader on the eMMC or SD card. + +If there is an error during installation, run `journalctl -u rauc.service` to get an extended output log. + +**Note:** The OTA update functionality was added in 12.3. To upgrade from an earlier version to 12.3 or later a few extra steps are required. Please refer to the the 12.3 release notes for detailed instructions. -However, HA's built-in backup and restore functionality works great so there should be no dataloss. Make sure to create a "Full Backup" and download it before flashing a new image. ## Hardware support diff --git a/buildroot-external/ota/rauc-hook b/buildroot-external/ota/rauc-hook index f366d5c7e1e..fe9d1602ac1 100755 --- a/buildroot-external/ota/rauc-hook +++ b/buildroot-external/ota/rauc-hook @@ -79,6 +79,25 @@ install_spl() { dd if="${RAUC_IMAGE_NAME}" of="${DEVICE_ROOT}boot0" conv=notrunc ${FLAGS} bs=512 skip=1 count=2047 echo 1 > /sys/block/"$(basename "${DEVICE_ROOT}boot0")"/force_ro fi + + # Rock Pi: Check if we are booting from SSD and flash bootloader to eMMC/SD + if case "${DEVICE_ROOT}" in "/dev/nvme"*) true;; *) false;; esac; then + # make sure there are no partitions for the device we're writing to. + # if there are any, this is not the expected configuration so rather + # error out than overwrite something unrelated. + if [ -b "/dev/mmcblk0" ] && ! [ -b "/dev/mmcblk0p1" ]; then + echo "Updating bootloader in eMMC/SD mmcblk0" + dd if="${RAUC_IMAGE_NAME}" of="/dev/mmcblk0" conv=notrunc ${FLAGS} bs=512 seek=64 skip=64 + elif [ -b "/dev/mmcblk1" ] && ! [ -b "/dev/mmcblk1p1" ]; then + echo "Updating bootloader in eMMC/SD mmcblk1" + dd if="${RAUC_IMAGE_NAME}" of="/dev/mmcblk1" conv=notrunc ${FLAGS} bs=512 seek=64 skip=64 + else + echo "Boot from SSD detected but got unexpected device configuration." + echo "Please open an issue on Github with the following output:" + lsblk + exit 1 + fi + fi } check_grubenv() { diff --git a/buildroot-external/ota/rockpi-ca.pem b/buildroot-external/ota/rockpi-ca.pem new file mode 100644 index 00000000000..fa31b262321 --- /dev/null +++ b/buildroot-external/ota/rockpi-ca.pem @@ -0,0 +1,95 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 37:51:55:9e:0a:d3:6b:d7:5e:c0:5a:13:2c:58:8b:ba:a5:18:34:24 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN = HassOS RockPi CA Root + Validity + Not Before: Apr 28 17:20:10 2024 GMT + Not After : Apr 26 17:20:10 2034 GMT + Subject: CN = HassOS RockPi CA Root + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b2:20:99:2c:65:eb:40:0c:8c:bd:2b:4b:ca:9e: + 6e:b9:b4:ef:84:83:a4:d3:f1:e6:d0:fd:70:d0:ce: + 5b:a2:96:e6:58:06:4c:bf:7b:17:ad:fb:5d:f3:45: + 60:04:58:40:5d:11:72:f1:9e:34:dc:ac:03:4b:d2: + dc:be:64:b6:05:4d:dc:33:dc:28:e4:2f:3f:75:ce: + d9:c3:da:bd:34:69:06:44:4a:b2:f1:7c:e2:7e:e3: + bf:71:9a:d1:23:94:8f:39:96:e7:7e:9b:30:68:11: + 88:fe:27:07:f7:81:73:65:a5:ab:82:07:bc:98:d3: + 36:b7:cf:a5:82:e5:91:4f:9d:57:bc:fc:68:fe:5b: + a8:b9:8b:0a:00:7f:da:3f:30:83:e9:01:70:50:66: + 1f:32:8d:32:92:6e:e5:1a:81:42:56:75:b6:e5:87: + 32:45:53:52:a7:25:c2:9c:59:29:dd:80:b7:0e:fd: + eb:01:dd:b1:d9:25:a8:2f:09:13:41:30:8b:d2:b9: + cc:07:9f:82:bd:8d:d8:d9:ce:45:dd:38:05:4b:c5: + 79:35:ce:c0:4a:27:97:c9:b4:87:7a:4a:be:42:ab: + 07:09:c1:27:cd:2d:51:c3:24:ac:50:be:3e:da:3a: + 4b:7b:ba:67:77:dc:79:e5:4c:70:0c:02:b1:bd:de: + a5:89 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + E1:19:4C:24:5A:ED:0A:BB:FE:9D:B3:88:A7:11:A0:92:47:AD:8B:FD + X509v3 Authority Key Identifier: + keyid:E1:19:4C:24:5A:ED:0A:BB:FE:9D:B3:88:A7:11:A0:92:47:AD:8B:FD + DirName:/CN=HassOS RockPi CA Root + serial:37:51:55:9E:0A:D3:6B:D7:5E:C0:5A:13:2C:58:8B:BA:A5:18:34:24 + X509v3 Basic Constraints: + CA:TRUE + X509v3 Key Usage: + Certificate Sign, CRL Sign + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 7e:94:20:ab:02:8f:32:90:af:57:63:1f:e3:ef:25:28:88:73: + 67:f6:bb:a9:95:56:78:30:40:51:77:c1:35:ec:11:a0:f1:51: + e1:2b:66:02:80:3e:73:20:3a:bc:17:fe:cc:a2:a0:33:0f:9c: + 5f:90:29:fc:43:ff:c6:ff:29:3c:35:6a:19:9d:cc:10:40:f0: + b1:db:08:3f:54:c6:ec:71:d2:2c:95:88:a4:76:b3:df:87:0d: + 27:cb:51:39:7d:a9:69:17:75:8f:96:db:0f:dc:b4:27:1a:0b: + 24:5b:c0:7a:98:88:c5:e5:ac:21:a8:9a:f5:f5:f5:b2:e7:6d: + 17:fa:cf:1f:6f:c3:c9:2f:76:4e:55:07:96:03:34:6e:68:ec: + 7f:e0:71:3b:e7:a3:a7:f7:94:e7:49:8f:51:9a:3b:54:ef:d0: + dd:29:2d:40:ce:4d:45:04:f7:83:1b:ce:22:ea:e7:dc:de:36: + 39:7c:4f:1f:69:a6:63:3d:87:a6:85:f0:e9:e7:32:ae:e1:0e: + 28:23:57:f3:3d:5a:51:8c:d4:6c:64:6c:14:18:04:ef:24:5b: + df:4b:1c:40:39:75:c2:a6:5a:d1:2f:0b:c5:40:1b:7a:1a:14: + db:1d:ad:8b:67:14:cc:cb:b3:5f:4d:85:5c:f6:e1:60:57:50: + e7:de:7b:14 +-----BEGIN CERTIFICATE----- +MIIDaTCCAlGgAwIBAgIUN1FVngrTa9dewFoTLFiLuqUYNCQwDQYJKoZIhvcNAQEL +BQAwIDEeMBwGA1UEAwwVSGFzc09TIFJvY2tQaSBDQSBSb290MB4XDTI0MDQyODE3 +MjAxMFoXDTM0MDQyNjE3MjAxMFowIDEeMBwGA1UEAwwVSGFzc09TIFJvY2tQaSBD +QSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsiCZLGXrQAyM +vStLyp5uubTvhIOk0/Hm0P1w0M5bopbmWAZMv3sXrftd80VgBFhAXRFy8Z403KwD +S9LcvmS2BU3cM9wo5C8/dc7Zw9q9NGkGREqy8XzifuO/cZrRI5SPOZbnfpswaBGI +/icH94FzZaWrgge8mNM2t8+lguWRT51XvPxo/luouYsKAH/aPzCD6QFwUGYfMo0y +km7lGoFCVnW25YcyRVNSpyXCnFkp3YC3Dv3rAd2x2SWoLwkTQTCL0rnMB5+CvY3Y +2c5F3TgFS8V5Nc7ASieXybSHekq+QqsHCcEnzS1RwySsUL4+2jpLe7pnd9x55Uxw +DAKxvd6liQIDAQABo4GaMIGXMB0GA1UdDgQWBBThGUwkWu0Ku/6ds4inEaCSR62L +/TBbBgNVHSMEVDBSgBThGUwkWu0Ku/6ds4inEaCSR62L/aEkpCIwIDEeMBwGA1UE +AwwVSGFzc09TIFJvY2tQaSBDQSBSb290ghQ3UVWeCtNr117AWhMsWIu6pRg0JDAM +BgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAfpQg +qwKPMpCvV2Mf4+8lKIhzZ/a7qZVWeDBAUXfBNewRoPFR4StmAoA+cyA6vBf+zKKg +Mw+cX5Ap/EP/xv8pPDVqGZ3MEEDwsdsIP1TG7HHSLJWIpHaz34cNJ8tROX2paRd1 +j5bbD9y0JxoLJFvAepiIxeWsIaia9fX1sudtF/rPH2/DyS92TlUHlgM0bmjsf+Bx +O+ejp/eU50mPUZo7VO/Q3SktQM5NRQT3gxvOIurn3N42OXxPH2mmYz2HpoXw6ecy +ruEOKCNX8z1aUYzUbGRsFBgE7yRb30scQDl1wqZa0S8LxUAbehoU2x2ti2cUzMuz +X02FXPbhYFdQ5957FA== +-----END CERTIFICATE----- +-----BEGIN X509 CRL----- +MIIByjCBswIBATANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVIYXNzT1MgUm9j +a1BpIENBIFJvb3QXDTI0MDQyODE3NTE1MloXDTI0MTAyNTE3NTE1MlqgXzBdMFsG +A1UdIwRUMFKAFOEZTCRa7Qq7/p2ziKcRoJJHrYv9oSSkIjAgMR4wHAYDVQQDDBVI +YXNzT1MgUm9ja1BpIENBIFJvb3SCFDdRVZ4K02vXXsBaEyxYi7qlGDQkMA0GCSqG +SIb3DQEBCwUAA4IBAQABV99wPJlXWfK6VQ+sN1701FJZLcr9HPrM3CO1jqtF1e+i +iBDwhOjl2HATDbUJkmCz7647HHNQ0NTRZkI69LEyzjhURsyvG8Kn1VBzFV21Jca2 +ni/IWqS6UgvwLNqMWob13aHwF4OZezPVafq1/oDhlCy2dCkmqN0brIzknc4Zh0M7 +WaEOLKQtxJRPZl0B6n+2pLXaRO7ZvwPgwcel9aFjAvN8LuZsxQ/fZ2NV7lXPkb6v +7LzYtpY51mhHGj5YOHuHeHF1iYFUnPgECyEsGgvuHXLtKF31GUdSiRv4AwG8tLj8 +5VUEKzrdKNhgL48xIWXjiwWGv925czhvH5ZnkaDb +-----END X509 CRL----- diff --git a/buildroot-external/ota/system.conf.gtpl b/buildroot-external/ota/system.conf.gtpl index 4d15e371c44..52422c52e9f 100644 --- a/buildroot-external/ota/system.conf.gtpl +++ b/buildroot-external/ota/system.conf.gtpl @@ -2,6 +2,7 @@ compatible={{ env "ota_compatible" }} mountprefix=/run/rauc statusfile=/mnt/data/rauc.db +max-bundle-download-size=314572800 {{- if eq (env "BOOTLOADER") "tryboot" }} bootloader=custom {{- else }} diff --git a/buildroot-external/rootfs-overlay/usr/lib/systemd/system/rauc.service.d/haos-rockpi.conf b/buildroot-external/rootfs-overlay/usr/lib/systemd/system/rauc.service.d/haos-rockpi.conf new file mode 100644 index 00000000000..77d6c4a8530 --- /dev/null +++ b/buildroot-external/rootfs-overlay/usr/lib/systemd/system/rauc.service.d/haos-rockpi.conf @@ -0,0 +1,7 @@ +# change tmp dir to allow download of large bundles +[Unit] +RequiresMountsFor=/mnt/data + +[Service] +Environment=TMPDIR=/mnt/data/tmp +ExecStartPre=/usr/bin/mkdir -p /mnt/data/tmp diff --git a/buildroot-external/scripts/rauc.sh b/buildroot-external/scripts/rauc.sh index a1e5fa594e8..85c65b5e892 100755 --- a/buildroot-external/scripts/rauc.sh +++ b/buildroot-external/scripts/rauc.sh @@ -32,12 +32,7 @@ function write_rauc_config() { function install_rauc_certs() { local cert="/build/cert.pem" - if [ "${DEPLOYMENT}" == "development" ]; then - # Contains development and release certificate - cp "${BR2_EXTERNAL_HASSOS_PATH}/ota/dev-ca.pem" "${TARGET_DIR}/etc/rauc/keyring.pem" - else - cp "${BR2_EXTERNAL_HASSOS_PATH}/ota/rel-ca.pem" "${TARGET_DIR}/etc/rauc/keyring.pem" - fi + cp "${BR2_EXTERNAL_HASSOS_PATH}/ota/rockpi-ca.pem" "${TARGET_DIR}/etc/rauc/keyring.pem" # Add local self-signed certificate (if not trusted by the dev or release # certificate it is a self-signed certificate, dev-ca.pem contains both)