diff --git a/main.go b/main.go index bdf0786d0b..ce6b91ed8d 100644 --- a/main.go +++ b/main.go @@ -2,26 +2,23 @@ package main import ( "context" - "crypto/tls" - "encoding/hex" "fmt" "io/ioutil" "log" - "net/http" "os" + "github.com/fleetdm/orbit/pkg/constant" "github.com/fleetdm/orbit/pkg/insecure" "github.com/fleetdm/orbit/pkg/osquery" + "github.com/fleetdm/orbit/pkg/update" "github.com/oklog/run" "github.com/pkg/errors" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/trustpinning" - "github.com/theupdateframework/notary/tuf/data" "github.com/urfave/cli/v2" ) const ( serverURL = "localhost:8080" + notaryURL = "https://localhost:4443" certPath = "/tmp/fleet.pem" ) @@ -41,64 +38,79 @@ func main() { Usage: "Disable TLS certificate verification", }, &cli.StringFlag{ - Name: "fleet_url", - Usage: "URL (host:port) to Fleet server", + Name: "fleet-url", + Usage: "URL (host:port) to Fleet server", Value: serverURL, }, + &cli.StringFlag{ + Name: "notary-url", + Usage: "URL (host:port) to Notary update server", + Value: notaryURL, + }, &cli.StringFlag{ Name: "enroll-secret", Usage: "Enroll secret for authenticating to Fleet server", }, } app.Action = func(c *cli.Context) error { - err := initialize(c) + // err := initialize(c) + // if err != nil { + // return errors.Wrap(err, "initialize") + // } + + // rootDir := ".trust" + // if err := os.MkdirAll(rootDir, 0700); err != nil { + // panic(err) + // } + + // server := "https://localhost:4443" + // image := "example.com/collection" + // transport := http.DefaultTransport.(*http.Transport).Clone() + // transport.TLSClientConfig = &tls.Config{ + // InsecureSkipVerify: true, + // } + // repo, err := client.NewFileCachedRepository( + // rootDir, + // data.GUN(image), + // server, + // transport, + // nil, + // trustpinning.TrustPinConfig{}, + // ) + // if err != nil { + // panic(err) + // } + + // targets, err := repo.ListTargets() + // if err != nil { + // panic(err) + // } + + // for _, tgt := range targets { + // fmt.Printf("%s\t%s\n", tgt.Name, hex.EncodeToString(tgt.Hashes["sha256"])) + // } + + // tgt, err := repo.GetTargetByName("LICENSE") + // if err != nil { + // panic(err) + // } + // fmt.Printf("%+v\n", tgt) + + updater, err := update.New(update.Options{ + RootDirectory: c.String("root-dir"), + ServerURL: c.String("notary-url"), + InsecureTransport: true, + }) if err != nil { - return errors.Wrap(err, "initialize") + return err } - - rootDir := ".trust" - if err := os.MkdirAll(rootDir, 0700); err != nil { - panic(err) - } - - server := "https://localhost:4443" - image := "example.com/collection" - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.TLSClientConfig = &tls.Config{ - InsecureSkipVerify: true, - } - repo, err := client.NewFileCachedRepository( - rootDir, - data.GUN(image), - server, - transport, - nil, - trustpinning.TrustPinConfig{}, - ) - if err != nil { - panic(err) - } - - targets, err := repo.ListTargets() - if err != nil { - panic(err) - } - - for _, tgt := range targets { - fmt.Printf("%s\t%s\n", tgt.Name, hex.EncodeToString(tgt.Hashes["sha256"])) - } - - tgt, err := repo.GetTargetByName("LICENSE") - if err != nil { - panic(err) - } - fmt.Printf("%+v\n", tgt) ->>>>>>> Stashed changes + fmt.Println(updater.Lookup("test", "LICENSE")) + _ = updater var g run.Group var options []func(*osquery.Runner) error - fleetURL := c.String("fleet_url") + fleetURL := c.String("fleet-url") if c.Bool("insecure") { proxy, err := insecure.NewTLSProxy(fleetURL) @@ -174,7 +186,7 @@ func main() { func initialize(c *cli.Context) error { fmt.Println(c.String("root-dir")) - err := os.MkdirAll(c.String("root-dir"), 0o600) + err := os.MkdirAll(c.String("root-dir"), constant.DefaultDirMode) if err != nil { return errors.Wrap(err, "make root directory") } diff --git a/pkg/update/update.go b/pkg/update/update.go index 5f5c6bcbce..0513ff624e 100644 --- a/pkg/update/update.go +++ b/pkg/update/update.go @@ -4,13 +4,110 @@ package update import ( "bytes" "crypto/sha512" + "crypto/tls" "encoding/base64" "io" "net/http" + "os" + "path/filepath" + "github.com/fleetdm/orbit/pkg/constant" "github.com/pkg/errors" + "github.com/theupdateframework/notary/client" + "github.com/theupdateframework/notary/trustpinning" + "github.com/theupdateframework/notary/tuf/data" ) +const ( + binDir = "bin" + osqueryDir = "osquery" + orbitDir = "orbit" + + notaryDir = "notary" +) + +// Updater is responsible for managing update state. +type Updater struct { + opt Options + transport *http.Transport +} + +// Options are the options that can be provided when creating an Updater. +type Options struct { + // RootDirectory is the root directory from which other directories should be referenced. + RootDirectory string + // ServerURL is the URL of the update server. + ServerURL string + // GUN is the Globally Unique Name to look up with the Notary server. + GUN string + // InsecureTransport skips TLS certificate verification in the transport if + // set to true. + InsecureTransport bool +} + +// New creates a new updater given the provided options. All the necessary +// directories are initialized. +func New(opt Options) (*Updater, error) { + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: opt.InsecureTransport, + } + + updater := &Updater{ + opt: opt, + transport: transport, + } + + err := updater.initializeDirectories() + if err != nil { + return nil, err + } + + return updater, nil +} + +// Lookup returns the target metadata for the provided GUN and target name. +func (u *Updater) Lookup(GUN, target string) (*client.Target, error) { + client, err := client.NewFileCachedRepository( + u.pathFromRoot(notaryDir), + data.GUN(GUN), + u.opt.ServerURL, + u.transport, + nil, + trustpinning.TrustPinConfig{}, + ) + if err != nil { + return nil, errors.Wrap(err, "make notary client") + } + + targetWithRole, err := client.GetTargetByName(target) + if err != nil { + return nil, errors.Wrap(err, "get target by name") + } + + return &targetWithRole.Target, nil +} + +func (u *Updater) pathFromRoot(parts ...string) string { + return filepath.Join(append([]string{u.opt.RootDirectory}, parts...)...) +} + +func (u *Updater) initializeDirectories() error { + for _, dir := range []string{ + u.pathFromRoot(binDir), + u.pathFromRoot(binDir, osqueryDir), + u.pathFromRoot(binDir, orbitDir), + u.pathFromRoot(notaryDir), + } { + err := os.MkdirAll(dir, constant.DefaultDirMode) + if err != nil { + return errors.Wrap(err, "initialize directories") + } + } + + return nil +} + // DownloadWithSHA512Hash downloads the contents of the given URL, writing // results to the provided writer. The size is used as an upper limit on the // amount of data read. An error is returned if the hash of the data received diff --git a/pkg/update/update_test.go b/pkg/update/update_test.go index 75ed1cca3c..c7f8175bda 100644 --- a/pkg/update/update_test.go +++ b/pkg/update/update_test.go @@ -7,6 +7,8 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -109,3 +111,24 @@ func sha512Hash(data []byte) []byte { } return hash.Sum(nil) } + +func TestInitializeDirectories(t *testing.T) { + t.Parallel() + + tmpDir, err := ioutil.TempDir("", "orbit-test") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + + _, err = New(Options{RootDirectory: tmpDir}) + require.NoError(t, err) + assertDir(t, filepath.Join(tmpDir, binDir)) + assertDir(t, filepath.Join(tmpDir, binDir, osqueryDir)) + assertDir(t, filepath.Join(tmpDir, binDir, orbitDir)) + assertDir(t, filepath.Join(tmpDir, notaryDir)) +} + +func assertDir(t *testing.T, path string) { + info, err := os.Stat(path) + assert.NoError(t, err, "stat should succeed") + assert.True(t, info.IsDir()) +}