Skip to content

Observer 0.24.5

Choose a tag to compare

@github-actions github-actions released this 01 Oct 06:18
· 21 commits to main since this release
06e9ca7

Observer 0.24.5.

Linux: root no longer follows or waits on what the service account puts in /etc/observer

Mostly Linux (.deb). On Windows, the package changes in its version number and in a few small things, listed at the end.

What was wrong. sudo observer share, rotate-key and doctor run as root inside /etc/observer, which belongs to the observer account the service runs as. They read credentials.json and certificate.pfx with the ordinary file calls. An attacker who had already taken over that account could put something else where those files belong. Measured with 0.24.4 run as root: a FIFO made the command wait for ever, a link to /dev/zero made it run out of memory, a socket ended in an unhandled exception, and a link to a file only root can read was read as root. When that file happened to be shaped like the store, share printed its secret, and a plain rotate-key (not --now) copied it into previous in the new store, which the observer account can read. Nothing in the package made the account easier to take over; this closes what root did once it had been.

What changes. Root opens each file once, never through a link at the file's own name, asks the open file what it is, and only then reads it, up to 64 KiB. A link, a FIFO, a device, a directory, a socket or a file over 64 KiB is refused with a sentence that says which, and nothing of it is read. So is a file with a second name (a hard link) that belongs to another account than the folder; the service's own file with a backup name next to it is read as before. rotate-key writes nothing when it refuses, and says that nothing was rotated. doctor says that what is where the store belongs is not a regular file, is over 64 KiB, or has a second name of another account, and offers no chown for it, because a chown changes the file behind the other name too, which may be root's. It also waits at most its own time limit for the local channel, and reports a socket whose queue of connections is full as STUCK. The answer of the local channel is read up to 64 KiB, and control characters in what it says are shown as ?. share and rotate-key refuse a token that holds a character a terminal acts on, and print nothing of it; the text of a damaged store's error, which may quote a key of the file, is shown without such characters too.

rotate-key --now does not need what the old store holds. It writes a store with nothing of the old one in it, so a link or a FIFO at that name, a store that cannot be parsed and one that holds something that is not a token no longer stop it: it says that the old store was not read, and replaces the name, never what a link pointed to. An access denied still stops it, because the write would fail for the same reason. A plain rotate-key carries the old key over and still refuses all of these.

If a command now refuses. If you made the link or the extra name yourself, the refusal is deliberate: read what it points at with sudo cat <that file>. If you did not, stop the service (sudo systemctl stop observer), look with sudo ls -l /etc/observer, remove what does not belong and start it again. Removing credentials.json makes the service create a new token, and every dashboard that holds the old one stops connecting until it is given the new one. Removing certificate.pfx makes it create a new certificate: the fingerprint changes, and observer share shows the new one. If the sentence says that nothing is known to be wrong with the file, this machine could not check it: do not delete it, look at it with the two commands the sentence gives, and report what uname -m and uname -r print.

Unchanged. The packaged service is not root and reads its own folder as before, and so does root in a folder that root owns. A service started by hand as root in a folder another account owns now refuses to start, with one sentence, when the store or the certificate is a file root will not read or could not check. A file that is not there is reported as before.

On Windows, rotate-key --now no longer stops on a store it cannot parse; share and rotate-key refuse a token that holds a control character; rotate-key says that nothing was rotated when it does not rotate; and the answer of the local channel is read up to 64 KiB. Nothing else changes.

Not fixed here. rotate-key --now and doctor talk to the running service through a socket in /run/observer, a folder the same account controls, so a taken-over account can answer for the service, and its answer still reads as a verdict. A FUSE file system mounted by that account (off by default on most systems) can lie about who owns a folder. With fs.protected_hardlinks=0 (not the default on Debian and Ubuntu) a link count is only a snapshot. Only x86-64 was measured: on other architectures a wrong value of O_NOFOLLOW makes root refuse to read, and does not make it follow. A directory planted at credentials.json.new stops a rotation with the error the write gives. Until these have their own changes, treat "the old key is refused" as true only if you trust the service account.

The packages are NOT signed

Windows will say so twice, in two different ways, and the two are not fixed
by the same thing:

  • User Account Control will show "Unknown publisher". A signature removes
    this immediately, from the first download;
  • SmartScreen will warn on first run of the installer. A signature does
    NOT remove this: since March 2024 not even an EV certificate bypasses it.
    It depends on how many clean downloads the file has accumulated, not on the
    certificate it carries.

To go ahead: More info, then Run anyway.

Where this package comes from

Every attached file carries a provenance attestation signed by GitHub, which
ties it to the commit and the workflow that produced it:

gh attestation verify Observer.msi --repo Akr0n/observer

It is not an Authenticode signature and Windows does not look at it. It answers
a different and equally useful question: does this file really come from that
source code
, rather than does Windows trust whoever made it.

Windows

Installs the service, the dashboard and the command line. Needs an elevated terminal:

msiexec /i Observer.msi

Nothing else needs installing: the package bundles the .NET runtime, which is
why it weighs about fifty megabytes. Up to 0.2.0 it required ASP.NET Core 10
without checking that it was there, and on a machine without it the service
died at startup while the installer talked about insufficient privileges - a
cause that had nothing to do with the real one.

Note that this means the runtime's security fixes no longer come from
Windows Update. They come with a new Observer release.

If that machine already has an Observer service registered by hand, remove it
first, and empty the installation folder.

Linux

sudo apt install ./observer_0.24.5_amd64.deb

Requires Ubuntu 24.04 or later: aspnetcore-runtime-10.0 comes from the official
archive, so the runtime is not bundled and is updated with the system.

After installing

To watch the machine you are sitting at there is nothing to configure, and no token
is needed: the dashboard uses the local channel. The token and the certificate
fingerprint are needed only so that ANOTHER computer can query this one, and you get
them with observer share.