Skip to content

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 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.

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.

/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.

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 status shows 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.

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.

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.

  • 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-inventory shows the bytes.
  • 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 beta channel first; a person promotes one to stable.
  • 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.

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.

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.