Security
A patching service is, by construction, a way to change what runs on your servers. This page is how Updawg is built so that it can do the one thing it exists for — install the updates you approved — and so that a compromise of Updawg, or of anyone with access to it, cannot turn into much more than that.
It says what is true today. Where something is planned rather than built, it says so.
The agent only connects out
Section titled “The agent only connects out”The agent opens one kind of connection: HTTPS to agents.updawg.net on port 443. It
listens on nothing. There is no SSH, no inbound port and no remote shell, and it
works from behind NAT and outbound-only firewalls. Work reaches it as the answer to
its own check-in.
There is no job that runs a command
Section titled “There is no job that runs a command”Everything the agent can be asked to do is one of a fixed set of typed jobs:
| Job | What it does |
|---|---|
| Refresh inventory | Collect and upload what is installed |
| Preflight | Check whether a change would succeed, and change nothing |
| Apply updates | Install exact package versions |
| Release upgrade | Upgrade to one named release (Debian, Ubuntu, RHEL family) |
| Reboot | Reboot, with a warning to logged-in users |
| Snapshot, rollback | Take a snapshot before a change; return to one |
| Self-update | Update the agent |
None of them takes a script, a command or a shell string, and there is no way to add
one from the server: a job kind is code in the agent. An update job names exact
versions — openssl 3.0.13-0ubuntu3.4, not “the latest” — and the agent refuses it
if the version the host would install has moved since it was approved, rather than
install something nobody reviewed.
This is the property that bounds everything below. Whatever else goes wrong, the worst an attacker can make an agent do is install a package version that exists in the host’s own configured repositories, reboot it, or take it to a newer release.
The host decides what it will do
Section titled “The host decides what it will do”/etc/updawg/agent.toml on each host says which kinds of job it accepts at all.
Local configuration always wins: the service can narrow what a host does and
nothing it sends can widen it. A host with mode = "observe" never changes anything,
whatever is approved. Take reboot out of the list and that host is never rebooted
by Updawg. The agent reports this to the service on every check-in, so the portal
shows what each host will actually accept.
Every job is signed, and the agent checks
Section titled “Every job is signed, and the agent checks”Each organization has its own Ed25519 signing key. Private keys are generated inside Google Cloud KMS and never exist outside it — not in our database, not in a backup, and not in the memory of any Updawg service. The signing service asks the key service for a signature; it cannot obtain the key itself. The portal API has no access of any kind.
An attacker who achieved code execution on the signing host could ask for signatures for as long as its credential remained valid, and every such request would appear in an audit log they could not alter. They could not extract a signing key, and they could not revoke or destroy one: the signing service is not granted that permission. Revoking its credential stops all of it in a single operation.
Two further things bound what such an attacker could do: jobs are typed operations with exact package versions and there is no job kind that runs arbitrary commands, and an agent’s local configuration always overrides the server.
What else stands between an approval and a signature:
- The signing service runs on its own host, apart from the API that serves the portal. Before it signs, it checks for itself that the job belongs to a proposal that was approved by people entitled to approve it — it does not take the API’s word for it.
- Agents pin the key. An agent learns its organization’s public key when it
enrols, and accepts a job only if that key signed it.
sudo updawgctl statusshows the fingerprint, to compare with the portal. - Rotation is endorsed. A new organization key is accepted by agents because the old key signed a statement introducing it — so rotating is something only the holder of the current key can do, and an agent never has to be told to trust a key out of band.
Agent identity
Section titled “Agent identity”At enrolment the agent generates an Ed25519 key pair on the host. The private key never leaves it. The service issues a client certificate for the public key, valid for about a month and renewed automatically, and every request the agent makes is authenticated by it over mutual TLS. An enrollment token is single-purpose: it can enrol hosts into one organization, until it expires, runs out of uses or is revoked.
Decommissioning a host in the portal means the service refuses its certificate from then on, whoever presents it.
Tenant isolation
Section titled “Tenant isolation”Every organization’s data is separated twice: by the application’s own authorisation checks, and by row-level security in the database, forced on every tenant table, so a query that forgot to filter by organization returns nothing rather than someone else’s hosts. Every API route is tested against a second organization’s data.
What is kept, and how
Section titled “What is kept, and how”- Sessions, API tokens, enrollment tokens and sign-in links are stored only as hashes. A copy of the database does not contain a usable one.
- Notification channel secrets (webhook URLs, signing secrets) are encrypted under a key held in Cloud KMS.
- The audit log is append-only for the application: it can add entries and cannot change or delete them. It is kept for as long as your plan says, and exportable as CSV or JSON.
- What the agent collects is listed exactly, including
what it never collects.
sudo updawgctl print-inventoryshows the bytes.
Getting the agent
Section titled “Getting the agent”- The install script is short enough to read, and says to. It checks every file against published SHA-256 sums before installing any of them.
- The apt and dnf repositories are signed with a key held in a hardware security
module in Cloud KMS. Our release pipeline can ask it for signatures and cannot read
it; only the pipeline on our main branch can ask at all; and a package, once
published, can never be replaced — a version always names the same file.
Fingerprint:
4FFF 29A6 2F24 ACF3 E043 76EE DFD5 B8C2 CBCF 67C5. - Stable is a person’s decision. Every build goes to the
betachannel first; a person promotes one tostable. - Agents update themselves only when you let them: an organization setting, or a proposal to update the agent that somebody approves. The agent checks the release manifest against the release key built into it, installs the exact build it names by checksum, and puts the previous version back if the new one does not check in.
Reporting a vulnerability
Section titled “Reporting a vulnerability”Our disclosure policy, with response times and safe harbour for good-faith
research, is at updawg.net/security. Write to
security@updawg.net; /.well-known/security.txt
says the same.
Not yet
Section titled “Not yet”Said plainly, so nobody has to find out:
- Customer-held signing keys — where your organization signs jobs with a key we never have, so Updawg cannot create a valid job at all — are planned for Enterprise and not built.
- Reproducible builds and SLSA provenance for the agent are planned and not built.