mirror of
https://github.com/jetkvm/kvm.git
synced 2026-06-20 05:24:32 +00:00
11b99d59fd
* Implement GPG signature verification for OTA updates - Added GPG signature verification to the OTA update process, ensuring that updates requiring signatures cannot be applied without them. - Introduced a new GPGVerifier struct to handle fetching and verifying signatures. - Updated the updateApp and updateSystem methods to check for signature URLs and download signatures as needed. - Enhanced error handling for missing signatures and verification failures. - Removed the old release.sh script as its functionality has been integrated into the Makefile for better release management. * Add tests for GPG signature verification in OTA updates * Refactor error message for missing GPG signature URL in OTA updates * Refactor OTA update process to improve signature handling - Introduced a new method for downloading component signatures, ensuring that updates requiring signatures cannot proceed without them. - Updated the Makefile to allow E2E tests to optionally include OTA tests based on the SKIP_OTA_E2E variable. - Enhanced the test_local_update.sh script to support signature file verification and inclusion during tests. - Improved error handling for missing signature URLs and added context cancellation checks in GPG key fetching. * Refactor GPG key caching to validate keyring before storing * Update Makefile to enhance E2E test process with optional OTA signature verification * Comment out non-working keyservers and update root key fingerprint * Add Ubunutu keyserver * Update root key fingerprint for GPG signature verification in OTA updates * Add signed OTA E2E test and full E2E test suite to Makefile - Introduced `test_e2e_signed` target for testing signed OTA updates with GPG signature verification. - Added `test_e2e_full` target to run both regular and signed OTA tests, requiring a signing key fingerprint. - Enhanced error handling for missing parameters in both test targets. * Update IP address extraction in test_local_update.sh to exclude all localhost addresses * Add GPG public key fetching tests with caching and error handling * Enhance build and testing scripts for signed OTA updates * Add fingerprint extraction and validation for GPG keys * Simplify bypass mechanism of OTA signature checks * Refactor E2E testing and release workflows * Enhance OTA testing framework and scripts * Improve local network IP detection in OTA helpers by implementing route-based detection as a primary method, falling back to interface scanning if necessary. * Add support for unsigned OTA version testing - Introduced a new script to test unsigned OTA updates with specific version checks. - Updated Makefile to include the new test script for unsigned OTA. - Enhanced existing E2E tests to validate version differences and ensure proper OTA behavior. - Improved error handling for required environment variables in the testing framework. * Update Makefile to include core E2E tests and enhance dev release validation - Added `test_core_e2e.sh` script execution to both production and development release workflows. - Improved user confirmation prompt before proceeding with the dev release. - Added completion messages to indicate successful test execution and readiness for release. * Enhance Makefile and testing scripts for improved OTA validation - Added a new script execution for testing unsigned OTA updates in the Makefile. - Updated E2E test configurations to exclude specific OTA tests and improve retry logic for video stream dimension retrieval. - Refactored mouse round-trip tests to remove unnecessary settle time parameters. * Final release confirmation of prod releases * Cleanup OTA code: eliminate redundant parsing, TOCTOU, and duplication - Remove double parseAndValidateKeyring call by threading validated keyring through fetchFromSingleKeyserver → fetchFromKeyservers → updateMemoryCache - Extract getKeyring() helper to deduplicate VerifySignature and VerifySignatureFromFile preamble - Replace os.Stat+os.Remove TOCTOU pattern with direct os.Remove ignoring os.ErrNotExist in downloadFile - Remove unnecessary fs.existsSync in mock server handler; check signaturePath variable directly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * E2E: fix flaky tests, add unsigned OTA to dev test lane - Fix mouse roundtrip flakiness by increasing MOUSE_SETTLE_MS (50→150ms) - Export sshExec from helpers for ota-helpers.ts - Reduce overly conservative delays (polling, animations, reconnects) - Add waitForVideoDimensions helper with proper polling - Improve ensureLocalAuthMode to try known passwords before SSH reset - Add unsigned specific-version OTA test to `make test_e2e` target - Build baseline + dev binary with pinned VERSION_DEV to avoid timestamp drift Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove stale dev_release checklist item from PR templates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package ota
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os/exec"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
systemUpdatePath = "/userdata/jetkvm/update_system.tar"
|
|
)
|
|
|
|
// DO NOT call it directly, it's not thread safe
|
|
// Mutex is currently held by the caller, e.g. doUpdate
|
|
func (s *State) updateSystem(ctx context.Context, systemUpdate *componentUpdateStatus, bypassSignatureCheck bool) error {
|
|
l := s.l.With().Str("path", systemUpdatePath).Logger()
|
|
|
|
// Validate signature requirement and download if available
|
|
signature, err := s.downloadComponentSignature(ctx, systemUpdate, "system", &l, bypassSignatureCheck)
|
|
if err != nil {
|
|
return s.componentUpdateError("Error with system signature", err, &l)
|
|
}
|
|
|
|
if err := s.downloadFile(ctx, systemUpdatePath, systemUpdate.url, "system"); err != nil {
|
|
return s.componentUpdateError("Error downloading system update", err, &l)
|
|
}
|
|
|
|
downloadFinished := time.Now()
|
|
systemUpdate.downloadFinishedAt = downloadFinished
|
|
systemUpdate.downloadProgress = 1
|
|
s.triggerComponentUpdateState("system", systemUpdate)
|
|
|
|
if err := s.verifyFile(
|
|
ctx,
|
|
systemUpdatePath,
|
|
systemUpdate.hash,
|
|
signature,
|
|
&systemUpdate.verificationProgress,
|
|
); err != nil {
|
|
return s.componentUpdateError("Error verifying system update", err, &l)
|
|
}
|
|
verifyFinished := time.Now()
|
|
systemUpdate.verifiedAt = verifyFinished
|
|
systemUpdate.verificationProgress = 1
|
|
systemUpdate.updatedAt = verifyFinished
|
|
systemUpdate.updateProgress = 1
|
|
s.triggerComponentUpdateState("system", systemUpdate)
|
|
|
|
l.Info().Msg("System update downloaded")
|
|
|
|
l.Info().Msg("Starting rk_ota command")
|
|
|
|
cmd := exec.Command("rk_ota", "--misc=update", "--tar_path=/userdata/jetkvm/update_system.tar", "--save_dir=/userdata/jetkvm/ota_save", "--partition=all")
|
|
var b bytes.Buffer
|
|
cmd.Stdout = &b
|
|
cmd.Stderr = &b
|
|
if err := cmd.Start(); err != nil {
|
|
return s.componentUpdateError("Error starting rk_ota command", err, &l)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
go func() {
|
|
ticker := time.NewTicker(1800 * time.Millisecond)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
if systemUpdate.updateProgress >= 0.99 {
|
|
return
|
|
}
|
|
systemUpdate.updateProgress += 0.01
|
|
if systemUpdate.updateProgress > 0.99 {
|
|
systemUpdate.updateProgress = 0.99
|
|
}
|
|
s.triggerComponentUpdateState("system", systemUpdate)
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
err = cmd.Wait()
|
|
cancel()
|
|
rkLogger := s.l.With().
|
|
Str("output", b.String()).
|
|
Int("exitCode", cmd.ProcessState.ExitCode()).Logger()
|
|
if err != nil {
|
|
return s.componentUpdateError("Error executing rk_ota command", err, &rkLogger)
|
|
}
|
|
rkLogger.Info().Msg("rk_ota success")
|
|
|
|
s.rebootNeeded = true
|
|
systemUpdate.updateProgress = 1
|
|
systemUpdate.updatedAt = verifyFinished
|
|
s.triggerComponentUpdateState("system", systemUpdate)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *State) confirmCurrentSystem() {
|
|
output, err := exec.Command("rk_ota", "--misc=now").CombinedOutput()
|
|
if err != nil {
|
|
s.l.Warn().Str("output", string(output)).Msg("failed to set current partition in A/B setup")
|
|
}
|
|
s.l.Trace().Str("output", string(output)).Msg("current partition in A/B setup set")
|
|
}
|