update

package
v0.0.0-...-64f3ffe Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2018 License: Apache-2.0, Apache-2.0 Imports: 15 Imported by: 0

README

go-update: Automatically update Go programs from the internet

go-update allows a program to update itself by replacing its executable file with a new version. It provides the flexibility to implement different updating user experiences like auto-updating, or manual user-initiated updates. It also boasts advanced features like binary patching and code signing verification.

Updating your program to a new version is as easy as:

err, errRecover := update.New().FromUrl("http://release.example.com/2.0/myprogram")
if err != nil {
	fmt.Printf("Update failed: %v\n", err)
}

Documentation and API Reference

Comprehensive API documentation and code examples are available in the code documentation available on godoc.org:

GoDoc

Features

  • Cross platform support (Windows too!)
  • Binary patch application
  • Checksum verification
  • Code signing verification
  • Support for updating arbitrary files

equinox.io

go-update provides the primitives for building self-updating applications, but there a number of other challenges involved in a complete updating solution such as hosting, code signing, update channels, gradual rollout, dynamically computing binary patches, tracking update metrics like versions and failures, plus more.

I provide this service, a complete solution, free for open source projects, at equinox.io.

License

Apache

Documentation

Overview

go-update allows a program to update itself by replacing its executable file with a new version. It provides the flexibility to implement different updating user experiences like auto-updating, or manual user-initiated updates. It also boasts advanced features like binary patching and code signing verification.

Updating your program to a new version is as easy as:

err, errRecover := update.New().FromUrl("http://release.example.com/2.0/myprogram")
if err != nil {
	fmt.Printf("Update failed: %v\n", err)
}

You may also choose to update from other data sources such as a file or an io.Reader:

err, errRecover := update.New().FromFile("/path/to/update")

Binary Diff Patching

Binary diff updates are supported and easy to use:

up := update.New().ApplyPatch(update.PATCHTYPE_BSDIFF)
err, errRecover := up.FromUrl("http://release.example.com/2.0/mypatch")

Checksum Verification

You should also verify the checksum of new updates as well as verify the digital signature of an update. Note that even when you choose to apply a patch, the checksum is verified against the complete update after that patch has been applied.

up := update.New().ApplyPatch(update.PATCHTYPE_BSDIFF).VerifyChecksum(checksum)
err, errRecover := up.FromUrl("http://release.example.com/2.0/mypatch")

Updating other files

Updating arbitrary files is also supported. You may update files which are not the currently running program:

up := update.New().Target("/usr/local/bin/some-program")
err, errRecover := up.FromUrl("http://release.example.com/2.0/some-program")

Code Signing

Truly secure updates use code signing to verify that the update was issued by a trusted party. To do this, you'll need to generate a public/private key pair. You can do this with openssl, or the equinox.io client (https://equinox.io/client) can easily generate one for you:

# with equinox client
equinox genkey --private-key=private.pem --public-key=public.pem

# with openssl
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -out public.pem -pubout

Once you have your key pair, you can instruct your program to validate its updates with the public key:

const publicKey = `-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----`

up, err := update.New().VerifySignatureWithPEM(publicKey)
if err != nil {
    return fmt.Errorf("Bad public key: '%v': %v", publicKey, err)
}

Once you've configured your program this way, it will disallow all updates unless they are properly signed. You must now pass in the signature to verify with:

up.VerifySignature(signature).FromUrl("http://dl.example.com/update")

Error Handling and Recovery

To perform an update, the process must be able to read its executable file and to write to the directory that contains its executable file. It can be useful to check whether the process has the necessary permissions to perform an update before trying to apply one. Use the CanUpdate call to provide a useful message to the user if the update can't proceed without elevated permissions:

up := update.New().Target("/etc/hosts")
err := up.CanUpdate()
if err != nil {
    fmt.Printf("Can't update because: '%v'. Try as root or Administrator\n", err)
    return
}
err, errRecover := up.FromUrl("https://example.com/new/hosts")

Although exceedingly unlikely, the update operation itself is not atomic and can fail in such a way that a user's computer is left in an inconsistent state. If that happens, go-update attempts to recover to leave the system in a good state. If the recovery step fails (even more unlikely), a second error, referred to as "errRecover" will be non-nil so that you may inform your users of the bad news. You should handle this case as shown here:

err, errRecover := up.FromUrl("https://example.com/update")
if err != nil {
    fmt.Printf("Update failed: %v\n", err)
    if errRecover != nil {
        fmt.Printf("Failed to recover bad update: %v!\n", errRecover)
        fmt.Printf("Program exectuable may be missing!\n")
    }
}

Subpackages

Sub-package check contains the client functionality for a simple protocol for negotiating whether a new update is available, where it is, and the metadata needed for verifying it.

Sub-package download contains functionality for downloading from an HTTP endpoint while outputting a progress meter and supports resuming partial downloads.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ChecksumForBytes

func ChecksumForBytes(source []byte) ([]byte, error)

ChecksumForBytes returns the sha256 checksum for the given bytes

func ChecksumForFile

func ChecksumForFile(path string) ([]byte, error)

ChecksumForFile returns the sha256 checksum for the given file

func ChecksumForReader

func ChecksumForReader(rd io.Reader) ([]byte, error)

ChecksumForReader returns the sha256 checksum for the entire contents of the given reader.

Types

type PatchType

type PatchType string

The type of a binary patch, if any. Only bsdiff is supported

const (
	PATCHTYPE_BSDIFF PatchType = "bsdiff"
	PATCHTYPE_NONE             = ""
)

type Update

type Update struct {
	// empty string means "path of the current executable"
	TargetPath string

	// type of patch to apply. PATCHTYPE_NONE means "not a patch"
	PatchType

	// sha256 checksum of the new binary to verify against
	Checksum []byte

	// public key to use for signature verification
	PublicKey *rsa.PublicKey

	// signature to use for signature verification
	Signature []byte
}

func New

func New() *Update

New creates a new Update object. A default update object assumes the complete binary content will be used for update (not a patch) and that the intended target is the running executable.

Use this as the start of a chain of calls on the Update object to build up your configuration. Example:

up := update.New().ApplyPatch(update.PATCHTYPE_BSDIFF).VerifyChecksum(checksum)

func (*Update) ApplyPatch

func (u *Update) ApplyPatch(patchType PatchType) *Update

ApplyPatch configures the update to treat the contents of the update as a patch to apply to the existing to target. You must specify the format of the patch. Only PATCHTYPE_BSDIFF is supported at the moment.

func (*Update) CanUpdate

func (u *Update) CanUpdate() (err error)

CanUpdate() determines whether the process has the correct permissions to perform the requested update. If the update can proceed, it returns nil, otherwise it returns the error that would occur if an update were attempted.

func (*Update) FromFile

func (u *Update) FromFile(path string) (err error, errRecover error)

FromFile updates the target the contents of the given file.

func (*Update) FromStream

func (u *Update) FromStream(updateWith io.Reader) (err error, errRecover error)

FromStream updates the target file with the contents of the supplied io.Reader.

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

1. If configured, applies the contents of the io.Reader as a binary patch.

2. If configured, computes the sha256 checksum and verifies it matches.

3. If configured, verifies the RSA signature with a public key.

4. Creates a new file, /path/to/.target.new with mode 0755 with the contents of the updated file

5. Renames /path/to/target to /path/to/.target.old

6. Renames /path/to/.target.new to /path/to/target

7. If the rename is successful, deletes /path/to/.target.old, returns no error

8. If the rename fails, attempts to rename /path/to/.target.old back to /path/to/target If this operation fails, it is reported in the errRecover return value so as not to mask the original error that caused the recovery attempt.

On Windows, the removal of /path/to/.target.old always fails, so instead, we just make the old file hidden instead.

func (*Update) FromUrl

func (u *Update) FromUrl(url string) (err error, errRecover error)

FromUrl updates the target with the contents of the given URL.

func (*Update) Target

func (u *Update) Target(path string) *Update

Target configures the update to update the file at the given path. The emptry string means 'the executable file of the running program'.

func (*Update) VerifyChecksum

func (u *Update) VerifyChecksum(checksum []byte) *Update

VerifyChecksum configures the update to verify that the the update has the given sha256 checksum.

func (*Update) VerifySignature

func (u *Update) VerifySignature(signature []byte) *Update

VerifySignature configures the update to verify the given signature of the update. You must also call one of the VerifySignatureWith* functions to specify a public key to use for verification.

func (*Update) VerifySignatureWith

func (u *Update) VerifySignatureWith(publicKey *rsa.PublicKey) *Update

VerifySignatureWith configures the update to use the given RSA public key to verify the update's signature. You must also call VerifySignature() with a signature to check.

You'll probably want to use VerifySignatureWithPEM instead of parsing the public key yourself.

func (*Update) VerifySignatureWithPEM

func (u *Update) VerifySignatureWithPEM(publicKeyPEM []byte) (*Update, error)

VerifySignatureWithPEM configures the update to use the given PEM-formatted RSA public key to verify the update's signature. You must also call VerifySignature() with a signature to check.

A PEM formatted public key typically begins with

-----BEGIN PUBLIC KEY-----

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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