<#1055 Serve the osquery package digest and honor ...
# osctrl
g
#1055 Serve the osquery package digest and honor the configured version in verify Pull request opened by javuto Serve the osquery package digest and honor the configured version in verify Two changes to
POST /{env}/verify
, the endpoint osctrld polls for what it should install.
osquery_sha256
in the verify response
osctrld#22 refuses to install an osquery package it cannot verify, and reads the digest from
VerifyResponse.OsquerySHA256
(
json:"osquery_sha256"
) before falling back to its own
--osquery-sha256
. osctrl did not send that field. It does now, sourced from a new
osquery.sha256
setting, so nodes on that branch start verifying downloads with no client change. Set it only on a uniform fleet. osquery ships a different artifact per format and architecture — deb amd64/arm64, rpm x86_64/aarch64, the macOS pkg, the Windows msi — and osctrld builds its download URL from its own `runtime.GOOS`/`GOARCH` without telling the server which one it will fetch. One scalar digest can therefore only be right for a deployment that installs a single package. Empty is the default and is safe, not a downgrade: osctrld then uses its own flag, or refuses. Returning a digest would make five of six platforms fail closed on a mismatch. The complete fix for mixed fleets is small and spans both repos: osctrld sends its package format and arch on verify, osctrl keys digests by
deb-amd64
,
rpm-x86_64
,
pkg
,
msi
. Worth a follow-up issue against osctrld#22 — the server half is then a map lookup in the helper this PR adds.
--osquery-version
had no reader
The flag is documented as "Version of osquery to be used" and
params.Osquery.Version
was populated at boot and consumed by nothing. Pinning a version in config or env changed nothing: verify handed every node the compile-time
version.OsqueryVersion
, and since that value is what osctrld installs and upgrades to, the whole fleet quietly tracked whatever osctrl was built against.
osqueryVersion()
now resolves configured → trimmed → build-time fallback. The fallback matters: an empty string reaches osctrld's
osqueryInstallDecision
, which treats an unparseable side as "unknown" and reinstalls, so an operator with no pin would get reinstall churn rather than today's behavior. The five package-filename call sites keep using the build constant (the MSI one moved onto it, so it stops being the odd one out). Those label a `.deb`/`.msi` osctrl didn't build — naming them after a config pin would make the label wrong in a new way. Files | File | Change | | -------------------------------------- | --------------------------------------------------------------- | | pkg/types/types.go | OsquerySHA256 on VerifyResponse, tag matching osctrld verbatim | | pkg/config/types.go | osquery.sha256 setting | | cmd/tls/handlers/handlers.go | osquerySHA256() and osqueryVersion(), both nil-safe and trimmed | | cmd/tls/handlers/post.go | verify returns both; MSI filename consistency | | deploy/config/tls.yml | documents the digest setting and when not to set it | | cmd/tls/handlers/verify_sha256_test.go | 7 tests | Not added to `api.yml`: osctrl-api never serves
/verify
, so the knob would be dead config there. Tests Config path, whitespace trimming (a digest pasted into YAML picks up newlines and neither side trims before comparing), both empty cases including a nil config, the version fallback chain, and a wire-format guard — renaming
osquery_sha256
silently disables verification on every node, so that one is pinned explicitly.
go build ./...
clean;
cmd/...
,
pkg/types
,
pkg/config
green. No OpenAPI regen:
VerifyResponse
is a TLS endpoint and doesn't appear in the API spec. jmpsec/osctrl