feat(agent): install signed releases by itself, one time each day - #40
Merged
Merged
Conversation
nabil1440
added this pull request to stack #38
September 23, 2026 05:42
Add the signature file checksums.txt.sig: the key id, the tag, the time of the signature and an ed25519 signature over these lines and the bytes of checksums.txt. SignedChecksum checks it and takes the sum from the same bytes. The tool tools/releasesign makes the key and signs a release; make release-key and make sign-release run it. keys.go holds the trusted public keys. Refs #39
Each day, at a time from the server id, the agent gets the latest release. It installs the release only when it is newer, its checksums.txt has a valid signature, and the signature is more than 24 hours old. It then exits, and systemd starts the new binary. The check runs on each tick, not only on a report tick, and it saves its time before it starts. FLY_AGENT_AUTO_UPDATE=off stops the check on one server. A build without a release version, or without a trusted key, does not check. agent.update works as before. The state file now keeps the whole state: a new report interval no longer removes the time of the last check. Closes #39
make sign-release signed whatever checksums.txt GitHub served. Someone who controls GitHub could swap an archive and its sum before the signature. tools/sign-release.sh now shows the commit of the local tag and asks to type the tag. It checks the archives against checksums.txt, builds the release again from the local tag with the Go version of the CI binaries, and compares the binaries byte for byte. The binary holds its commit, so this also proves that CI built the local tag. checksums.txt must name exactly the built archives. The script signs and verifies with the tool and the keys of the tag. The build date is now the date of the commit, so the same commit and Go version give the same binary on any computer. Refs #39
- A make build after a tag (v0.2.0-3-gabcdef1, -dirty) sorts before the tag, so the release would replace newer code. release.IsLocalBuild turns auto-update off for these builds. - Read the release JSON through a 1 MiB limit: the agent reads it each day without a person. - Test that the tag and the key lines are part of the signed bytes. Refs #39
After the wall clock stepped back by more than a minute, the loop waited for the old tick time: the agent sent nothing for the size of the step. For example, a VM that booted with its clock one hour ahead went silent for one hour after NTP corrected it. The loop now skips a repeated minute only after a step back of less than 2 minutes. After a larger step it follows the new clock. A minute that runs two times is harmless: the control plane keeps one sample for each minute.
make release-key takes COMMENT (default: "server-cli release key for flywp"). The comment goes in the private key file as a PEM header and in the output, and keys.go names each key with it. A comment must be one line, so that it cannot add an other PEM header. Refs #39
In "make release-key KEY=~/key" the shell does not expand the "~": it is not at the start of a word. The tool and the sign script now replace a leading "~/" with the home directory. Refs #39
Add the public key 21ec14c790e96d62 ("server-cli release key for flywp").
The private key is kept outside GitHub. A test now fails when the list of
trusted keys is empty, so that no release ships without a key.
Refs #39
… counts Contract v0.3.0 records the update by release, and permits null for the update counts. The agent now sends updates_total and updates_security as null until apt-check gives a count. Before, it sent 0: a false "no updates". A later failure keeps the last counts, as before. The README pin is v0.3.0. Refs #39
The contract now states that a failed update count keeps the last counts, and that null means no count since the agent started. The agent already does this; only the pin changes.
nabil1440
force-pushed
the
agent/39-auto-update
branch
from
September 24, 2026 08:09
a1f2068 to
0041f2c
Compare
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.
Closes #39.
Part of the monitoring agent stack. This PR is on top of #37.
What changes
checksums.txt.sig. The file names the key, the tag and the time of the signature, and it has an ed25519 signature. The signature covers these lines and the bytes ofchecksums.txt.tools/releasesignmakes a key pair, signs and verifies.make release-keymakes the key. The maintainer signs on their own computer. The private key is never in GitHub.make sign-releaserunstools/sign-release.sh. It shows the commit of the local tag and asks to type the tag. It builds the release again from the local tag, with the Go version of the CI binaries, and compares the binaries byte for byte. If GitHub serves a different archive, or the tag on GitHub moved, the script stops before the signature. For this, the build date is now the date of the commit.internal/release/keys.goholds the public keys. A build without a key does not update itself.FLY_AGENT_AUTO_UPDATE=offin/etc/fly/agent.envstops the check on one server. An unknown value gives a warning, and the agent continues.make buildafter a tag (v0.2.0-3-gabcdef1,-dirty) does not update itself: the release would replace newer code.v0.3.1. The agent sendsupdates_totalandupdates_securityasnulluntilapt-checkgives a count. Before, it sent 0: a false "no updates". A later failed count sends the last counts again (v0.3.1 states this).agent.updatedoes not change. It stays the fast path, and the recovery path if the key is lost or stolen.Before release
internal/release/keys.gotrusts the key21ec14c790e96d62("server-cli release key for flywp"). A test fails if the list is empty.make sign-release VERSION=<tag> KEY=<file or ->.Tests
fly agent runinstalls a signed release from a test GitHub, then exits.checksums.txt, and a missing local tag stop the script, and no signature is written.make checkpasses.