Documentation ¶
Overview ¶
Package update provides functionality to implement secure, self-updating Go programs (or other single-file targets).
For complete updating solutions please see Equinox (https://equinox.io) and go-tuf (https://github.com/flynn/go-tuf).
Basic Example ¶
This example shows how to update a program remotely from a URL.
import ( "fmt" "net/http" "github.com/ruishantech/selfupdate" ) func doUpdate(url string) error { // request the new file resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() err := selfupdate.Apply(resp.Body, selfupdate.Options{}) if err != nil { if rerr := selfupdate.RollbackError(err); rerr != nil { fmt.Println("Failed to rollback from bad update: %v", rerr) } } return err }
Binary Patching ¶
Go binaries can often be large. It can be advantageous to only ship a binary patch to a client instead of the complete program text of a new version.
This example shows how to update a program with a bsdiff binary patch. Other patch formats may be applied by implementing the Patcher interface.
import ( "encoding/hex" "io" "github.com/ruishantech/selfupdate" ) func updateWithPatch(patch io.Reader) error { err := selfupdate.Apply(patch, selfupdate.Options{ Patcher: selfupdate.NewBSDiffPatcher() }) if err != nil { // error handling } 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.
selfupdate 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/ruishantech/selfupdate" ) func updateWithChecksum(binary io.Reader, hexChecksum string) error { checksum, err := hex.DecodeString(hexChecksum) if err != nil { return err } err = selfupdate.Apply(binary, selfupdate.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 means nothing changes if you apply your update with a patch.
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 selfupdate.
import ( "crypto" _ "crypto/sha256" "encoding/hex" "io" "github.com/ruishantech/selfupdate" ) func verifiedUpdate(binary io.Reader, hexChecksum string) { checksum, err := hex.DecodeString(hexChecksum) if err != nil { return err } opts := selfupdate.Options{ Checksum: checksum, Hash: crypto.SHA256, // this is the default, you don't need to specify it } err = selfupdate.Apply(binary, opts) if err != nil { // error handling } return err }
Building Single-File Go Binaries ¶
In order to update a Go application with selfupdate, you must distribute it as a single executable. This is often easy, but some applications require static assets (like HTML and CSS asset files or TLS certificates). In order to update applications like these, you'll want to make sure to embed those asset files into the distributed binary with a tool like go-bindata (my favorite): https://github.com/jteeuwen/go-bindata
Non-Goals ¶
Mechanisms and protocols for determining whether an update should be applied and, if so, which one are out of scope for this package. Please consult go-tuf (https://github.com/flynn/go-tuf) or Equinox (https://equinox.io) for more complete solutions.
selfupdate only works for self-updating applications that are distributed as a single binary, i.e. applications that do not have additional assets or dependency files. Updating application that are distributed as multiple on-disk files is out of scope, although this may change in future versions of this library.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
Apply performs an update of the current executable or opts.TargetFile, with the contents of the given io.Reader. When the update fails, it is unlikely that old executable is corrupted, but still, applications need to check the returned error with RollbackError() and notify the user of the bad news and ask them to recover manually.
func CommitBinary ¶
CommitBinary moves the new executable to the location of the current executable or opts.TargetPath if specified. It performs the following operations:
- Renames /path/to/target to /path/to/.target.old
- Renames /path/to/.target.new to /path/to/target
- 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.
- 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 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 PrepareAndCheckBinary ¶
PrepareAndCheckBinary reads the new binary content from io.Reader and performs the following actions:
- If configured, applies the contents of the update io.Reader as a binary patch.
- If configured, computes the checksum of the executable and verifies it matches.
- If configured, verifies the signature with a public key.
- Creates a new file, /path/to/.target.new with the TargetMode with the contents of the updated file
func RollbackError ¶
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 // Use this hash function to generate the checksum. If not set, SHA256 is used. Hash crypto.Hash // If nil, treat the update as a complete replacement for the contents of the file at TargetPath. // If non-nil, treat the update contents as a patch and use this object to apply the patch. Patcher Patcher // 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 }
func (*Options) CheckPermissions ¶
CheckPermissions 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.
type Patcher ¶
Patcher defines an interface for applying binary patches to an old item to get an updated item.
func NewBSDiffPatcher ¶
func NewBSDiffPatcher() Patcher
NewBSDifferPatcher returns a new Patcher that applies binary patches using the bsdiff algorithm. See http://www.daemonology.net/bsdiff/
Directories ¶
Path | Synopsis |
---|---|
internal
|
|
binarydist
Package binarydist implements binary diff and patch as described on http://www.daemonology.net/bsdiff/.
|
Package binarydist implements binary diff and patch as described on http://www.daemonology.net/bsdiff/. |
osext
Extensions to the standard "os" package.
|
Extensions to the standard "os" package. |