Repository navigation
TLS was documented, not implemented - #42
Merged
Merged
Conversation
TLS_ENABLED was documented in three places and read by nothing; the ingest protocol had no reply frame; and the agent's sent flag was the only record of what the server held, so a server-side reset stranded every quiet table for ever.
| context = _ssl.SSLContext(_ssl.PROTOCOL_TLS_CLIENT) | ||
| context.check_hostname = False | ||
| context.verify_mode = _ssl.CERT_NONE | ||
| with context.wrap_socket(raw, server_hostname=host): |
d3vhex
added a commit
that referenced
this pull request
Sep 8, 2026
…ntion TLS was documented, not implemented
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLS_ENABLED,TLS_CERTandTLS_KEYappeared in.env.example, in theproduction guide, and in the closing output of
certs/generate_certs.py— andwere read by no line of code. Setting
TLS_ENABLED=1produced a console thatcame up cleanly on plain HTTP while its operator had every reason to believe
it was HTTPS. Assume any console that ran with that setting was never
encrypted, and rotate credentials typed into it.
core/tls.pymakes it real on one rule: asking for TLS and not getting itis a startup failure, never a fallback. Telemetry moves to port 5011 inside
TLS, the control channel to
wss://, andSESSION_COOKIE_SECUREfollowsTLS_ENABLEDwhen unset — the two had to agree and nothing made them.Six things only turning it on could have found
Every one of these passed the unit tests and needed a real container, a real
certificate and a real handshake to surface:
certs/ships in the image ownedby root; the server runs unprivileged. Generation died with
EACCESand,correctly refusing to downgrade, restart-looped. Material now lives in the
data volume beside
data/fernet.key./api/agent/caread a path of its own while the generator wrote toTLS_DIR, so the endpoint installers depend on for trust answered 404 onevery containerised deployment.
and the channel could not open — reporting, and unreachable.
requestsclient. Three fixes for one defect, so trustis now installed once at startup as the system store plus this CA
(
REQUESTS_CA_BUNDLE,CURL_CA_BUNDLE,SSL_CERT_FILE). Trusting theprivate CA instead of the system roots would have been the easier change
and would have made the agent distrust every other endpoint on the machine.
registration and the binary download, so on a self-signed server every
earlier call failed and the fix arrived two steps too late. Trust is now
established before the first network call, and the Windows installer adds
the CA to the machine store.
ServerCertificateValidationCallback = {$true}does not work onPowerShell 5.1. .NET invokes the script block on an I/O thread with no
runspace; the callback throws and surfaces as "an unexpected error occurred
on a send", which reads as a TLS fault. An hour went into protocol
versions, cipher suites, ALPN and IPv6 before the inner exception was
unwrapped. Both the console snippet and the installer use
curl.exenow.Three of the six were the same defect wearing different clothes: trust wired
per connection rather than per host.
The ingest protocol had no reply
The agent marked rows sent when
sendallreturned — which means the bytesreached a socket buffer and nothing more. There is a receipt frame now, and
three cases stay distinguishable: rows stored, an error (keep them, retry, say
why), and no receipt at all from a server older than the frame. Once a server
has acknowledged once, later silence is treated as a fault — there is a real
configuration that accepts a batch and discards it.
Four live data-loss bugs, found by making failure visible
Moving the insertion failure out of
if debugturned up four paths droppingrows while every layer reported success:
docker_containers— one duplicate row rolled back the whole batch.Rows insert individually now; snapshots take the last write.
registry_logs,process_events,security_audit— accepted byALLOWED_TABLESwith no table indb/init.sql. 7,788 rows held on oneendpoint against a table that did not exist.
siem_events— the console's Clear button truncated rows and left everyingest fingerprint, so each future row was skipped as a duplicate of a row
that no longer existed. One click permanently blinded the table.
critical_files— a delta stream classified as a server snapshot, soevery batch deleted the 1,200 rows held and kept the handful that changed.
A rejected row now releases its dedup fingerprint; without that it could never
be stored again even after the cause was fixed.
A server reset stranded every quiet table
sentlives on the agent and is its only record of what the server holds.Nothing told it when the server's database was recreated, and a sent row is
never re-offered — so
fim_datarefilled within minutes andportscan_resultsat at 93 rows locally against 0 on the server, for ever.The server now stamps each database incarnation with an identity and returns
it on every receipt. An agent that sees it change re-offers its rows, bounded
to 1,000 per table and deduplicated server-side. It re-offers on first sight
too — the agents that most need this are the ones running since before the
epoch existed.
The telemetry health view can finally tell the two apart, and says server
reset rather than "accepted and discarded" when the rows predate the
database.
The agent's local database grew for ever
Nothing pruned it: 530,428 rows of
hardware_inventoryon one host, allshipped, never read again. Hourly retention keeps the newest 5,000 per table —
above the recovery limit, so pruning cannot quietly cap what a reset can
rescue — and only ever deletes rows the server has confirmed.
security_auditwas shipped by the agent with no collector anywhere. Removedfrom the shipping list; the server keeps the schema.
The channel could starve itself
GET /config/rulestimed out at 5s against a healthy agent. Stream payloadsand control frames shared one send lock, an orphaned screen pump never
stopped, and
PING_INTERVAL_Swas doubling as the socket timeout, so one slowsend could hold the lock for 30s. Stream frames are droppable and yield to a
queued reply; the socket deadline is its own constant; the server tells the
agent when a stream is gone.
Charts across the console
Eleven pages now carry the dashboard's visual language, each derived from rows
the page already holds — a chart with its own request can disagree with the
table beside it. Two places deliberately have none and say why in the code:
OSV returns no CVSS score, and
resource_usageis a snapshot table.Tests
2,317 passing. New structural invariants: a table the agent deduplicates
locally can never be a server snapshot; every
ALLOWED_TABLESentry must havesomewhere to land; every table the agent ships must have a writer; clearing a
deduplicated table must clear its fingerprints; no certificate callback may be
a PowerShell script block; and every setting the TLS guide documents must be
one the code reads.
The nine skips in
test_encrypted_column_widthsare gone — they were skippingbecause three tables had no schema at all, so the suite reported "nothing to
check here" about tables the agent was filling.