update

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Apr 6, 2024 License: MIT, Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package update provides functionality to implement secure, self-updating Go programs (or other single-file targets).

Basic Example

This example shows how to update a program remotely from a URL.

import (
	"fmt"
	"net/http"

	"github.com/creativeprojects/go-selfupdate/update"
)

func doUpdate(url string) error {
	// request the new file
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	err := update.Apply(resp.Body, update.Options{})
	if err != nil {
		if rerr := update.RollbackError(err); rerr != nil {
			fmt.Println("Failed to rollback from bad update: %v", rerr)
		}
	}
	return err
}

Checksum Verification

Updating executable code on a computer can be a dangerous operation unless you take the appropriate steps to guarantee the authenticity of the new code. While checksum verification is important, it should always be combined with signature verification (next section) to guarantee that the code came from a trusted party.

go-update validates SHA256 checksums by default, but this is pluggable via the Hash property on the Options struct.

This example shows how to guarantee that the newly-updated binary is verified to have an appropriate checksum (that was otherwise retrieved via a secure channel) specified as a hex string.

import (
	"crypto"
	_ "crypto/sha256"
	"encoding/hex"
	"io"

	"github.com/creativeprojects/go-selfupdate/update"
)

func updateWithChecksum(binary io.Reader, hexChecksum string) error {
	checksum, err := hex.DecodeString(hexChecksum)
	if err != nil {
		return err
	}
	err = update.Apply(binary, update.Options{
		Hash: crypto.SHA256, 	// this is the default, you don't need to specify it
		Checksum: checksum,
	})
	if err != nil {
		// error handling
	}
	return err
}

Cryptographic Signature Verification

Cryptographic verification of new code from an update is an extremely important way to guarantee the security and integrity of your updates.

Verification is performed by validating the signature of a hash of the new file.

This example shows how to add signature verification to your updates. To make all of this work an application distributor must first create a public/private key pair and embed the public key into their application. When they issue a new release, the issuer must sign the new executable file with the private key and distribute the signature along with the update.

import (
	"crypto"
	_ "crypto/sha256"
	"encoding/hex"
	"io"

	"github.com/creativeprojects/go-selfupdate/update"
)

var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtrVmBxQvheRArXjg2vG1xIprWGuCyESx
MMY8pjmjepSy2kuz+nl9aFLqmr+rDNdYvEBqQaZrYMc6k29gjvoQnQ==
-----END PUBLIC KEY-----
`)

func verifiedUpdate(binary io.Reader, hexChecksum, hexSignature string) {
	checksum, err := hex.DecodeString(hexChecksum)
	if err != nil {
		return err
	}
	signature, err := hex.DecodeString(hexSignature)
	if err != nil {
		return err
	}
	opts := update.Options{
		Checksum: checksum,
		Signature: signature,
		Hash: crypto.SHA256, 	               // this is the default, you don't need to specify it
		Verifier: update.NewECDSAVerifier(),   // this is the default, you don't need to specify it
	}
	err = opts.SetPublicKeyPEM(publicKey)
	if err != nil {
		return err
	}
	err = update.Apply(binary, opts)
	if err != nil {
		// error handling
	}
	return err
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Apply

func Apply(update io.Reader, opts Options) error

Apply performs an update of the current executable (or opts.TargetFile, if set) with the contents of the given io.Reader.

Apply performs the following actions to ensure a safe cross-platform update:

1. If configured, computes the checksum of the new executable and verifies it matches.

2. If configured, verifies the signature with a public key.

3. Creates a new file, /path/to/.target.new with the TargetMode with the contents of the updated file

4. Renames /path/to/target to /path/to/.target.old

5. Renames /path/to/.target.new to /path/to/target

6. If the final rename is successful, deletes /path/to/.target.old, returns no error. On Windows, the removal of /path/to/target.old always fails, so instead Apply hides the old file instead.

7. If the final rename fails, attempts to roll back by renaming /path/to/.target.old back to /path/to/target.

If the roll back operation fails, the file system is left in an inconsistent state (between steps 5 and 6) where there is no new executable file and the old executable file could not be be moved to its original location. In this case you should notify the user of the bad news and ask them to recover manually. Applications can determine whether the rollback failed by calling RollbackError, see the documentation on that function for additional detail.

func RollbackError

func RollbackError(err error) error

RollbackError takes an error value returned by Apply and returns the error, if any, that occurred when attempting to roll back from a failed update. Applications should always call this function on any non-nil errors returned by Apply.

If no rollback was needed or if the rollback was successful, RollbackError returns nil, otherwise it returns the error encountered when trying to roll back.

Types

type Options

type Options struct {
	// TargetPath defines the path to the file to update.
	// The emptry string means 'the executable file of the running program'.
	TargetPath string

	// Create TargetPath replacement with this file mode. If zero, defaults to 0755.
	TargetMode os.FileMode

	// Checksum of the new binary to verify against. If nil, no checksum or signature verification is done.
	Checksum []byte

	// Public key to use for signature verification. If nil, no signature verification is done.
	PublicKey crypto.PublicKey

	// Signature to verify the updated file. If nil, no signature verification is done.
	Signature []byte

	// Pluggable signature verification algorithm. If nil, ECDSA is used.
	Verifier Verifier

	// Use this hash function to generate the checksum. If not set, SHA256 is used.
	Hash crypto.Hash

	// Store the old executable file at this path after a successful update.
	// The empty string means the old executable file will be removed after the update.
	OldSavePath string
}

Options for Apply update

func (*Options) SetPublicKeyPEM

func (o *Options) SetPublicKeyPEM(pembytes []byte) error

SetPublicKeyPEM is a convenience method to set the PublicKey property used for checking a completed update's signature by parsing a Public Key formatted as PEM data.

type Verifier

type Verifier interface {
	VerifySignature(checksum, signature []byte, h crypto.Hash, publicKey crypto.PublicKey) error
}

Verifier defines an interface for verifying an update's signature with a public key.

func NewECDSAVerifier

func NewECDSAVerifier() Verifier

NewECDSAVerifier returns a Verifier that uses the ECDSA algorithm to verify updates.

func NewRSAVerifier

func NewRSAVerifier() Verifier

NewRSAVerifier returns a Verifier that uses the RSA algorithm to verify updates.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL