<#1161 Make osctrl act as a Santa sync server> Pul...
# osctrl
g
#1161 Make osctrl act as a Santa sync server Pull request opened by javuto Make osctrl act as a Santa sync server Closes #1142 Problem osctrl has no way to manage Santa, the macOS binary-authorization agent. Teams running osquery through osctrl need a separate sync server (Moroz, Rudolph, Zentral, Workshop) to push allow/block rules and collect execution events. This PR makes osctrl that sync server and reuses its existing node enrollment and environments. What changes Everything is behind a new flag, off by default:
--santa-enabled
/
SERVICE_SANTA_ENABLED
/
service.santaEnabled
. Enable it on both
osctrl-tls
and
osctrl-api
. When the flag is off: • no tables are migrated • no routes are registered •
/api/v1/features
reports
santa: false
osctrl-tls: sync protocol endpoint •
POST /{env}/santa/sync/{stage}/{machineid}
implements the Santa sync v2 protocol:
preflight
,
eventupload
,
ruledownload
(paginated, 500 rules per page) and
postflight
. • Santa's
SyncBaseURL
is
https://<tls host>/<env uuid>/santa/sync/
. • Requests can be JSON or protobuf, optionally gzip/deflate compressed. Responses use the same encoding as the request. • Rule changes reach clients by digest: • At preflight, osctrl computes a SHA-256 of the machine's effective rules. • If the digest differs from the last applied one, osctrl answers `CLEAN_RULES`; otherwise
NORMAL
. • If rules change mid-sync,
ruledownload
returns
409
and the client resyncs next time. A client never ends up with a mixed rule set. • Machine config (MONITOR/LOCKDOWN, batch size, transitive rules, USB blocking) is pushed at preflight. osctrl-api: management API (
/api/v1/santa/{env}/...
)
•
GET/PUT config
• `POST token`: generate or rotate the token •
GET/POST rules
,
DELETE rules/{id}
•
GET machines
,
GET events
• All routes require environment admin and write audit log lines (
santa: <action>
). • Rules can be scoped to the environment, a tag, or a node. The most specific scope wins: node > tag > env. • Rule types: BINARY, CERTIFICATE, TEAMID, SIGNINGID, CDHASH. Policies: ALLOWLIST, ALLOWLIST_COMPILER, BLOCKLIST, SILENT_BLOCKLIST, CEL. • Rules are validated and normalized server-side: • hashes are lowercased, TEAMID and SIGNINGID team prefixes uppercased • fields that don't apply are cleared • length limits are enforced • Duplicate rules return
409
. • Events are pruned daily, with a fixed 30-day retention. •
osctrl-api.yaml
is regenerated. Frontend: new Santa page per environment Visible to environment admins when the feature is enabled. • Rules: list, add and delete rules. • Events: paged execution events. The Allow / Block buttons prefill a BINARY rule for the file's hash. • Machines: synced clients with mode, rule count, Santa version and last sync time. • Settings: • client config • token rotation (asks for confirmation when replacing an existing token; the token is shown once) • the
SyncBaseURL
and
SyncExtraHeaders
values for the MDM profile • Strings added to all 21 locales. Posture • New darwin check `santa_agent`: passes when the Santa system extension is
activated_enabled
. Seeded disabled. Persistence • New tables, created by AutoMigrate only when the feature is enabled:
santa_env_configs
,
santa_rules
(unique index on env, type, identifier and scope),
santa_machines
,
santa_events
. • Existing tables are unchanged. Dependencies •
buf.build/gen/go/northpolesec/protos/protocolbuffers/go
(Santa sync v2 protos) •
<http://google.golang.org/protobuf|google.golang.org/protobuf>
becomes a direct dependency Security and operational impact • Authentication • Each environment has one token, sent by Santa in the
X-Osctrl-Santa-Token
header. • Only its SHA-256 is stored, and it is compared in constant time. • The plaintext is returned once with
Cache-Control: no-store
and is never readable afterwards. • Node ownership • The machine ID must be the UUID of a node already enrolled in that same environment. • A malformed ID gets
400
. • An unknown environment, unknown machine, machine from another environment, or wrong token all get
403
, so responses don't reveal which environments or machines exist. • Limits • The raw body is capped at 10 MiB and the decompressed body at 20 MiB (
413
past either). • Unknown protobuf/JSON fields are discarded. • Audit flooding • Failed sync authentications are written to the audit log, capped at 60 per hour per environment. • Dropped writes are counted in
santa_auth_audit_dropped_total
. • No IP rate limit, so fleets behind one NAT aren't throttled. • Trust model limit (documented) • The token is shared by every Santa host in an environment, and hardware UUIDs aren't secret. • So any host holding the token can sync as another enrolled node in that environment: it can read that node's scoped rules and upload events as it. • Recommended mitigation: client mTLS at the reverse proxy, using Santa's
ClientAuthCertificate*
keys. • Untouched routes: the existing osctrld script route and all osquery TLS routes are unchanged. • Docs:
ARCHITECTURE.md
updated.
pkg/santa/README.md
documents the protocol, the auth model and the MDM profile.
deploy/config/{tls,api}.yml
gain
santaEnabled: false
. Not included (follow-ups) • Sync stages
commands
,
metrics
, `signalupload`; FCM/NATS push • File-access, network-flow and telemetry rules; bundle uploads • Forwarding Santa events to alert rules or log sinks; MCP tools • Editing rules in place; event search; per-machine mode overrides; bulk rule import • Sending
removable_media_policy
instead of the deprecated
block_usb_mount
• Configurable event retention Validation • `go test ./...`: pass (59 packages) • `make frontend-test`: pass (785 tests,
tsc
clean) • `make openapi-check`: clean • `golangci-lint`: no new issues (13 existing errcheck findings in
pkg/logging
and
carves.go
, which this PR doesn't touch) Not yet validated: • End-to-end against a real Santa client on macOS • Postgres and MySQL; only SQLite was exercised in tests • Native-speaker review of the non-English translations jmpsec/osctrl