Некоторые проверки не удались
CI / test (push) Successful in 3m19s
Docker / Build and publish worker image (push) Successful in 17m20s
SSH Source-Install E2E / Alpine/Ubuntu/Arch source-install E2E (push) Failing after 2m48s
15 KiB
15 KiB
Changelog
2026-08-13
Source-install E2E in CI (work package 5)
- Added
.github/workflows/test-ssh.yml, a Gitea Actions workflow that runs the Alpine/Ubuntu/Arch Docker/OpenSSH source-install matrix (make test-ssh) on pushes tomasterand on manualworkflow_dispatchonly, separate from the Docker-free unit CI. It does not trigger onpull_request: the distro fixtures execute the checked-out code inside privileged Docker, so untrusted PR code must never run there automatically. It bounds the job withtimeout-minutes: 90(the go-test-timeout 60mstays in place), scopes concurrency per ref (test-ssh-${{ gitea.ref }}), declarespermissions: contents: read, uploads no artifacts, and cleans up on every path. - Pinned the two actions to immutable full commit SHAs (verified against the
GitHub API):
actions/checkout@v4->11d5960a326750d5838078e36cf38b85af677262andactions/setup-go@v5->40f1582b2485089dde7abd97c1529aa768e1baff. The repo-wide convention still leavesci.yml/docker.ymlon moving tags (accepted, documented risk); seedocs/source-installation.md. - Wired
RSMON_TEST_IMAGE_ALPINE/RSMON_TEST_IMAGE_UBUNTU/RSMON_TEST_IMAGE_ARCHandRSMON_TEST_DOCKER_DNSrepository variables (all empty by default) so CI can pin per-fixture mirror/snapshot images and a resolver for flaky CI DNS. - Added
scripts/ci/test-ssh.sh: preflights Docker and the harness's loopback port-publishing requirement with a tinydocker run -p 127.0.0.1::22probe (fails fast with an actionable message plus diagnostics/fix options instead of a 60m timeout on an unsupported runner), then runsmake test-sshand trapsEXITto remove every leftoverrsmon-worker-test-*container/network/image tag. Cleanup filters are anchored to the harness's own prefix/repository so they never touch a shared base image. The workflow adds anif: always()cleanup step as a belt-and-suspenders so a killed job never leaves test material on the runner. - External network is fetched live by design (go.dev toolchain, rocketgit.ru
source clone, distro repos); operators can pin a resolver via the
RSMON_TEST_DOCKER_DNSrepository variable (comma-separated nameservers, applied asdocker run --dns ...) and mirror/snapshot overrides viaRSMON_TEST_IMAGE_ALPINE/RSMON_TEST_IMAGE_UBUNTU/RSMON_TEST_IMAGE_ARCH. - Fixed a history-dependent test fragility:
TestSourceInstallDirtyCheckoutPreservesStagingand the rollback test's build-failure step dirty the tracked tree by appending a marker line toMakefileinstead ofgit checkout master~1 -- Makefile, which silently stopped dirtying the tree once the last commit did not touch that file.
2026-08-12
Source-install hardening review
- Fail-closed remote scripts: checkout, branch resolution, and build steps now
run under
set -eu(and package/record steps chain with&&), so a failed checkout or fetch can never be masked by a stalerev-parseor subsequent command. The checkout step additionally refuses (git diff --quiet/--cached --quiet) before the destructivecheckout -B, because-Bsilently discards local changes and would otherwise never fail on a dirty tree. A dirty-tree checkout failure surfaces as acheck out brancherror before the build runs; the newTestSourceInstallSSHCheckoutFailureNotMaskedunit test andTestSourceInstallDirtyCheckoutPreservesStagingDocker test prove the previous staging binary and commit record are preserved byte-for-byte. - Atomic toolchain replacement: the Go toolchain is downloaded, SHA-256
verified, extracted into a same-filesystem staging dir, verified to report
the target version, and only then swapped into
ToolchainDirwith the prior toolchain moved to a sibling.go-backupthat is restored on swap failure. A failed download/verify/extract/swap never destroys the prior Go. - Record-after-build pairing:
rsmon-worker.commitis written only after a successful build, so the record and the staged binary always correspond to the same commit. The build verifies<stage>.new --versionbefore an atomicmv -fover the previous staging binary;GOMODCACHEis now set alongsideGOCACHEinside the build dir so reruns reuse both caches. - Origin verification: an existing checkout's
remote.origin.urlmust exactly match the configured repository before anything is fetched or built. - Repository hardening: only
https://clone URLs without userinfo are accepted (ValidateRepoURL, enforced before dialing and again when planning). - Explicit charset validation for Go version and architecture overrides
(
sshinstall.ValidGoVersion/ValidGoArch) before any remote mutation. - Bounded remote execution: each remote command is capped by
--session-timeout(default 30m) and captured stdout is size-bounded alongside the existing stderr bound; deploy's streamingrunRemotekeeps its historical no-timeout behavior. - The source installer now defaults to the remote's default branch (the public
repo publishes
master) instead of the plan's stalemaindefault, while--branchstill pins an explicit branch that must exist remotely. The README quickstart no longer shows the incorrect--branch main. - CLI secret flags keep their compatibility, but docs now explicitly state that
file options (
-password-file, etc.) keep secrets out of argv and shell history while direct flags expose them through the process list. - The harness accepts
RSMON_TEST_DOCKER_DNS(comma-separated) to pindocker run --dnsfor fixture containers, so internet-facing installs are not at the mercy of a flaky local resolver.
Remote source-install execution (work package 3)
- Added
installer.SourceInstall(internal/installer/sourceinstall.go): executes the source-install flow through the existing SSH transport, reusing thedeploycommand'sSSHOptions(keys, passphrases, passwords, sudo passwords, known-hosts, pinned fingerprints) and its privilege path. Extracted the sharedSSHOptionsstruct and asudoWraphelper so deploy and source install cannot diverge. - Steps implemented: minimal package-prerequisite install per distro
(
apk/apt/pacman/dnf, never a compiler), SHA-256-verified Go 1.26 toolchain download/extraction with an idempotent version-skip and temp-dir cleanup, clone-or-update of the public repository (with a bounded 3-attempt retry for transient DNS/TLS/proxy failures), resolution of the remote default branch (a pinned branch must exist remotely), a resolved branch and commit record at<BuildDir>/rsmon-worker.commit, and a staging build (CGO_ENABLED=0,-trimpath, repository-ldflags) verified via--version. The running service, config, and data directory are untouched (work package 4 boundary). - Security: every interpolated remote value is single-quoted; branch and
commit values are strictly validated; no worker token or control-plane
credential is sent; sudo passwords travel only over session stdin; remote
errors are bounded (stderr truncated in
runRemoteOutput). - Added unit tests for the remote scripts, option validation, branch/commit parsing, sudo wrapping, the secrets-absent contract, and an in-process real-SSH orchestration flow (with missing-pinned-branch, build-failure, and detection-failure paths).
- Added
TestSourceInstallFixturesto the Docker/OpenSSH harness: each of Alpine, Ubuntu, and Arch installs from a clean state through the real harness transport (prerequisite install, verified Go 1.26, clone, resolved commit, staging build), then a rerun proves idempotency (same branch, toolchain reuse, no temp leaks). All three resolved the public repo'smasterat4651deb2...in the recorded run.make test-sshtimeout raised to 60m. - Documented the branch-resolution reality: the public repository currently
publishes
master, and the installer records whatever the remote default branch resolves to.
Source-install harness and planning foundations (work packages 1-2)
- Added
internal/installer/harness: a reusable Docker/OpenSSH test harness that builds real OpenSSH containers for Alpine, Ubuntu, and Arch, waits for real network SSH readiness, captures the server host key into a tempknown_hostsfile, and tears the container, network, per-instance fixture image tag, and temp dir down reliably. It uses thegolang.org/x/crypto/sshlibrary and known_hosts verification semantics the installer'sdeploypath relies on (the harness owns its connection code rather than reusing the installer functions) and never mocks SSH. A fresh known_hosts file trusts the first key (TOFU); the host-key mismatch test proves a different key is rejected before any command runs. - Added distro fixtures under
internal/installer/harness/testdata/fixtures. Alpine defaults to thereg.rsxx.ru/library/alpine:3mirror; Ubuntu and Arch fall back to Docker Hub refs overridable viaRSMON_TEST_IMAGE_<NAME>. Each fixture starts clean (no Go, no worker source) and authenticates with a bundled test key; password auth is disabled. The test key is strictly test-only - it grants root only to the disposable fixture containers - and must never be used outside the harness. - Added opt-in integration controls: the Docker tests run only with
RSMON_TEST_DOCKER=1(make test-ssh); defaultmake testandgo test ./...skip them and never pull or start containers.make testalso pinsRSMON_TEST_DOCKER=0so an exported opt-in flag cannot leak into the unit run. - Integration tests assert real SSH round trips, clean target state, distro / package-manager / init detection per fixture, a full source plan including the pinned Go toolchain, host-key mismatch rejection, host-key stability, failed-start cleanup, and complete teardown (container, network, fixture image tag, and temp dir gone).
- Added
internal/sshinstall: pure, unit-tested detection and planning for the source installer - os-release parsing, distro/package-manager/init resolution,uname -mto Go archive mapping, pinned Go 1.26 toolchain with published SHA-256, and a reviewable ordered plan. No remote execution yet. make testnow includes the new packages;make test-sshruns the live fixture matrix.
Public endpoint configuration (milestone 1 of public-endpoint-and-identity)
PUBLIC_URLis now the canonical advertised public origin; the legacyWORKER_URLis accepted only for the bounded migration and logs a startup deprecation warning.PUBLIC_URLwins whenever both are set, and the installer dropsWORKER_URLfrom freshly written env files whenPUBLIC_URLis present.- Startup and install validate the origin shape: absolute
http/httpsURL with scheme and authority only; userinfo, query, fragment, and any path other than/are rejected. - Plain-HTTP
PUBLIC_URLon a non-loopback host is rejected in an explicitly production environment (DEPLOY_ENV,RSMON_ENV, orGO_ENV=production); other environments keep the historical warning. internal/wireaddspublic_urltoWorkerInit(control plane to worker), keeping the legacyurlfield for old control planes; the worker preferspublic_urland rejects unusable values, keeping the previous accepted URL.RegisterRequest.public_urlis the registration contract for the pending RSMon counterpart (the worker does not currently transmit the URL during registration; it consumes the accepted endpoint fromWorkerInit).- The legacy
WORKER_URLis held only to the tolerant absolute-URL check (no newly rejected legacy shapes);PUBLIC_URLis held to the strict scheme-and-authority origin shape. Both reject a missing hostname, e.g.https://:27401.
2026-07-19
Standalone installation and deployment
- Added
rsmon-worker installfor installing the current binary, a mode-0600 environment file, and a root-owned systemd service. - Added
rsmon-worker deployfor installing workers over SSH with key or password authentication, optional secret files, and host-key verification throughknown_hostsor a pinned fingerprint. - Added
--token-file,--url,--api-key, and--no-startdeployment options. - Added optional
--dockerdeployment using a prebuilt image. Docker install and deploy now require an immutablerepository@sha256:...reference before any Docker or remote-host mutation; the former mutablelatestdefault is no longer accepted. - Simplified the default systemd service to
Type=simple,User=root, andRestart=on-failure. - Reworked the legacy
scripts/install-systemd.shscript as a compatibility wrapper around the built-in installer.
Standalone repository cleanup
- Removed the remaining certificate-bundle fallback under
/data/rsmonand updated its documentation. - Changed Docker build and runtime bases to public Go and Debian images.
- Documented binary, systemd, Docker, and SSH deployment workflows.
- Added installer and SSH host-verification tests.
Verification
- Passed
make test,make build,go vet, andgo mod verify. - Validated systemd units, Compose configuration, Docker image pull and execution, and production worker job reporting.
- Published the changes as commit
3256dcd(feat: add worker install and deploy) onmaster, triggering the Docker image workflow.
Task protocol and local audit hardening
- Required one task-envelope branch, matching outer/inner job IDs, and a non-empty lease token before local execution.
- Added structured terminal failures for unsupported check kinds and safely attributable malformed envelopes.
- Recorded delegated notification outcomes in the bounded
/notificationsview using only job ID, method, status, duration, and time. - Added static permanent handling for invalid deadlines and recovered notification executor panics without retaining secret-bearing text.
- Removed the critical-cluster test-config endpoint, CLI flag, environment switch, and production helper; hardcoded config application is test-only.
- Made runner token rotation connection-scoped and in-memory: it reconnects without stopping web, inventory, metrics, or cluster subsystems. Durable token storage, bootstrap exchange, rotation acknowledgement, and revocation remain unimplemented.
- Added bounded resend of dequeued check and notification result envelopes after websocket reconnect; control-plane application remains at-least-once and must deduplicate by leased job and lease token. Failed metric snapshots are dropped and replaced by the next periodic tick, not replayed.
- SIGTERM stops new dispatch and waits for active work, but stale-lease acknowledgement, bounded graceful final-result drain, and duplicate-frame coverage remain open.