updater

package
v1.15.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2025 License: MIT Imports: 31 Imported by: 0

README

Release signing and key rollover documentation

Audience: core maintainers

This document captures the necessary steps to perform regular (usually every 2nd year) release signing key rollover.

Updating the gopass binary

The gopass self-updater is invoked when calling gopass update. It works only if the binary is writable by the user running the command. It is specifically not designed to update any gopass packages installed by a package manager.

The updater first tries to ensure that it is supposed to update the binary (usually because it can write to the binary location) and then fetches the latest release from GitHub. If this ever causes trouble we could cache this info and proxy requests through gopass.pw.

If there is a new release it will fetch both SHA256SUMS and SHA256SUMS.sig assets from the latest release and verify the signature matches one of the built-in updater keys.

If the checksum file is verified we continue to fetch the actual binary archive and compare that against the (verified) checksum file and replace the binary.

All of this is implemented by the files in this directory.

Publishing assets for the updater during releases

When a new release is cut we rely on GoReleaser and GitHub Action workflows to update all necessary assets. The configuration for those is spread across the repository and the GitHub Action configuration.

A new release is published by pushing a version tag (v*) to the repository. Once that happens the GHA workflow autorelease.yml is kicked off. It is configured in .github/workflows/autorelease.yml and through a number of injected environment variables from the GHA settings. Most importantly GPG_PRIVATE_KEY which contains the armored GPG private part of the current release signing key and the respective passphrase in PASSPHRASE.

GoReleaser is controlled by .goreleaser.yml in the root of this repository. The relevant sections there are checksum to ensure a checksum file is generated and the signs section to sign the checksum file using the provided GPG_FINGERPRINT in the workflow.

The relase signing key is set to expire every other year, so we need to follow a certain key rotation protocol to allow for a seamless key rotation.

  • At T-6 Month we should notice that TestGPGVerifyIn6Months starts to fail.
    • There is likely a loss obstrusive way to achieve that, but I'll leave it at that for now.
  • We should then create an issue to track the key rollover (this should never happen in secret). The entire security posture isn't perfect but that's the best I can do with my resources. Help always appreciated.
  • For the actual rollout we first need to generate a new key. That needs to be done by exactly one core maintainer with write access to the repo and the GHA secrets since only they can inject the new key, fingerprint and passphrase.
  • To generate the key run: gpg --expert --full-generate-key and select RSA and RSA, 3072 (bits) and a validity of 2y. Use Gopass Release Signing Key YYYY as the name, GitHub Actions only as the comment and [email protected] as the email.
    • Note: If you're correctly following the 6 Months advance notice process, use the next year for the name.
    • RSA isn't perfect but we used to have some compatability issues with non-RSA keys. Feel free to revisit this in the future. Keep the keep size to a reasonable value. Last time I checked 4096 did seem a bit excessive and with different algorithms these numbers will need to change as well. In doubt the BSI TR-02102-1 should have a reasonable recommendation.
    • Use a strong, random passphrase. Since you should never have to type it anywhere make it long, cryptic and stop worrying about it, e.g. gopass pwgen 32.
  • If you are me, you should probably also save a copy of both parts of new key into your gopass maintainer password store. For convenience add the Key ID / Fingerprint into the secret so you don't have to import the key just to get the Key ID.
    • Hint: gpg --output 0xKEYID.pub --armor --export 0xKEYID and gpg --output 0xKEYID.private --armor --export-secret-key 0xKEYID
  • You should also sign the new key with the old key and possibly your personal key and push it to some keyservers.
    • Use gpg --yes -u 0xOLDKEYID --sign-key 0xKEYID to sign.
  • Now export the public key and inject it into the pubkeys slice in verify.go. Add a comment with the year and the key id.
  • As usual send a PR and get this merged. Consider kicking off a new release, if that makes sense.
  • STOP HERE. For a seamless key rollover we need to wait until most users had a chance to update to a version that has both the old and the new keys. So if possible wait a few months at least. Keep the GH issue open and assigned to track that process.
  • After ~5 Months continue here.
  • Regenerate the test signature for verify_test.go
    • Create a file that contains gopass-sign-test\n and run gpg -u 0xKEYID --armor --output /tmp/testdata.sig --detatch-sign testdata. Use the correct KEYID (the one of the NEW key).
    • Hint: Make sure the input only contains one line break, not two.
    • Paste the content of /tmp/testdata.sig into the testSignature in verify_test.go. Make sure all tests pass.
  • Navigate to https://github.com/gopasspw/gopass/settings/secrets/actions
    • Paste the armored private part of the new key into the existing GPG_PRIVATE_KEY secret.
    • Paste the corresponding passphrase into PASSPHRASE.
  • At this point you should be able to safely delete the old public key from verify.go and kick off a new release.
  • At the very end upload the new key to some keyservers: gpg --send-keys 0xKEYID and possibly gpg --keyserver pgp.mit.edu --send-keys 0xKEYID.
    • In case you mess up during key generation you might need to start over and you don't want to have conflicting keys on a keyserver where you can't delete them.

Documentation

Overview

Package updater provides a simple update mechanism for gopass. It will check for updates, download the latest release and verify the GPG signature of the release.

Index

Constants

This section is empty.

Variables

View Source
var (
	// APITimeout is how long we wait for the GitHub API.
	APITimeout = 30 * time.Second

	// BaseURL is exported for tests.
	BaseURL = "https://api.github.com/repos/%s/%s/releases/latest"
)
View Source
var (
	// DownloadTimeout is the overall timeout for the download, including all retries.
	DownloadTimeout = time.Minute * 5
)
View Source
var UpdateMoveAfterQuit = true

UpdateMoveAfterQuit is exported for testing.

Functions

func IsUpdateable

func IsUpdateable(ctx context.Context) error

IsUpdateable returns an error if this binary is not updateable.

func Update

func Update(ctx context.Context, currentVersion semver.Version) error

Update will start the interactive update assistant.

Types

type Asset

type Asset struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	URL  string `json:"browser_download_url"`
}

Asset is a GitHub release asset.

type Release

type Release struct {
	ID          int            `json:"id"`
	Name        string         `json:"name"`
	TagName     string         `json:"tag_name"`
	Draft       bool           `json:"draft"`
	Prerelease  bool           `json:"prerelease"`
	PublishedAt time.Time      `json:"published_at"`
	Assets      []Asset        `json:"assets"`
	Version     semver.Version `json:"-"`
}

Release is a GitHub release.

func FetchLatestRelease

func FetchLatestRelease(ctx context.Context) (Release, error)

FetchLatestRelease fetches meta-data about the latest Gopass release from GitHub.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳