Skip to content

Commit

Permalink
Expand TUF checkup
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMahany committed Oct 16, 2023
1 parent 12c0837 commit 3c18d7d
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 19 deletions.
4 changes: 2 additions & 2 deletions pkg/autoupdate/tuf/autoupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func NewTufAutoupdater(k types.Knapsack, metadataHttpClient *http.Client, mirror
// If the update directory wasn't set by a flag, use the default location of <launcher root>/updates.
updateDirectory := k.UpdateDirectory()
if updateDirectory == "" {
updateDirectory = defaultLibraryDirectory(k.RootDirectory())
updateDirectory = DefaultLibraryDirectory(k.RootDirectory())
}
ta.libraryManager, err = newUpdateLibraryManager(k.MirrorServerURL(), mirrorHttpClient, updateDirectory, ta.logger)
if err != nil {
Expand Down Expand Up @@ -170,7 +170,7 @@ func LocalTufDirectory(rootDirectory string) string {
return filepath.Join(rootDirectory, tufDirectoryName)
}

func defaultLibraryDirectory(rootDirectory string) string {
func DefaultLibraryDirectory(rootDirectory string) string {
return filepath.Join(rootDirectory, "updates")
}

Expand Down
10 changes: 9 additions & 1 deletion pkg/autoupdate/tuf/library_lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ type BinaryUpdateInfo struct {
Version string
}

func (b *BinaryUpdateInfo) String() string {
if b.Version != "" {
return fmt.Sprintf("%s at %s", b.Version, b.Path)
}

return b.Path
}

type autoupdateConfig struct {
rootDirectory string
updateDirectory string
Expand Down Expand Up @@ -103,7 +111,7 @@ func CheckOutLatest(binary autoupdatableBinary, rootDirectory string, updateDire
}

if updateDirectory == "" {
updateDirectory = defaultLibraryDirectory(rootDirectory)
updateDirectory = DefaultLibraryDirectory(rootDirectory)
}

update, err := findExecutableFromRelease(binary, LocalTufDirectory(rootDirectory), channel, updateDirectory)
Expand Down
4 changes: 2 additions & 2 deletions pkg/autoupdate/tuf/library_lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestCheckOutLatest_withTufRepository(t *testing.T) {

// Set up an update library
rootDir := t.TempDir()
updateDir := defaultLibraryDirectory(rootDir)
updateDir := DefaultLibraryDirectory(rootDir)

// Set up a local TUF repo
tufDir := LocalTufDirectory(rootDir)
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestCheckOutLatest_withoutTufRepository(t *testing.T) {

// Set up an update library, but no TUF repo
rootDir := t.TempDir()
updateDir := defaultLibraryDirectory(rootDir)
updateDir := DefaultLibraryDirectory(rootDir)
target := fmt.Sprintf("%s-1.1.1.tar.gz", binary)
executablePath, executableVersion := pathToTargetVersionExecutable(binary, target, updateDir)
require.NoError(t, os.MkdirAll(filepath.Dir(executablePath), 0755))
Expand Down
2 changes: 1 addition & 1 deletion pkg/autoupdate/tuf/library_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func Test_newUpdateLibraryManager(t *testing.T) {
func Test_pathToTargetVersionExecutable(t *testing.T) {
t.Parallel()

testBaseDir := defaultLibraryDirectory(t.TempDir())
testBaseDir := DefaultLibraryDirectory(t.TempDir())

testVersion := "1.0.7-30-abcdabcd"
testTargetFilename := fmt.Sprintf("launcher-%s.tar.gz", testVersion)
Expand Down
137 changes: 124 additions & 13 deletions pkg/debug/checkups/tuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"

"github.com/go-kit/kit/log"
"github.com/kolide/launcher/pkg/agent/types"
"github.com/kolide/launcher/pkg/autoupdate/tuf"
)
Expand All @@ -30,13 +33,14 @@ type (
tufRelease struct {
Signed struct {
Targets map[string]customTufRelease `json:"targets"`
Version int `json:"version"`
} `json:"signed"`
}
)

func (tc *tufCheckup) Data() map[string]any { return tc.data }
func (tc *tufCheckup) ExtraFileName() string { return "" }
func (tc *tufCheckup) Name() string { return "Tuf Version" }
func (tc *tufCheckup) ExtraFileName() string { return "tuf.json" }
func (tc *tufCheckup) Name() string { return "TUF" }
func (tc *tufCheckup) Status() Status { return tc.status }
func (tc *tufCheckup) Summary() string { return tc.summary }

Expand All @@ -54,51 +58,158 @@ func (tc *tufCheckup) Run(ctx context.Context, extraFH io.Writer) error {
return err
}

response, err := tc.fetchTufVersion(httpClient, tufUrl)
// Primarily, we want to validate that we can talk to the TUF server
releaseVersion, remoteMetadataVersion, err := tc.remoteTufMetadata(httpClient, tufUrl)
if err != nil {
tc.status = Erroring
tc.data[tufUrl.String()] = err.Error()
tc.summary = "Unable to gather tuf version response"
return nil
}

if response == "" {
if releaseVersion == "" {
tc.status = Failing
tc.data[tufUrl.String()] = "missing from tuf response"
tc.summary = "missing version from tuf targets response"
return nil
}

tc.status = Passing
tc.data[tufUrl.String()] = response
tc.summary = fmt.Sprintf("Successfully gathered release version %s from %s", response, tufUrl.String())
tc.data[tufUrl.String()] = releaseVersion
tc.summary = fmt.Sprintf("Successfully gathered release version %s from %s", releaseVersion, tufUrl.String())

// Gather additional data only if we're running flare
if extraFH == io.Discard {
return nil
}

tufData := map[string]any{
"remote_metadata_version": remoteMetadataVersion,
}

if localMetadataVersion, err := tc.localTufMetadata(); err != nil {
tufData["local_metadata_version"] = fmt.Sprintf("error getting local metadata version: %v", err)
} else {
tufData["local_metadata_version"] = localMetadataVersion
}

if localLauncherVersions, err := tc.versionsInLauncherLibrary(); err != nil {
tufData["launcher_versions_in_library"] = fmt.Sprintf("error getting versions available in local launcher library: %v", err)
} else {
tufData["launcher_versions_in_library"] = localLauncherVersions
}

if localOsqueryVersions, err := tc.versionsInOsquerydLibrary(); err != nil {
tufData["osqueryd_versions_in_library"] = fmt.Sprintf("error getting versions available in local osqueryd library: %v", err)
} else {
tufData["osqueryd_versions_in_library"] = localOsqueryVersions
}

tufData["selected_versions"] = tc.selectedVersions()

if b, err := json.Marshal(tufData); err == nil {
_, _ = extraFH.Write(b)
}

return nil
}

// fetchTufVersion retrieves the latest targets.json from the tuf URL provided.
// remoteTufMetadata retrieves the latest targets.json from the tuf URL provided.
// We're attempting to key into the current target for the current platform here,
// which should look like:
// https://[TUF_HOST]/repository/targets.json -> full targets blob
// ---> signed -> targets -> launcher/<GOOS>/<GOARCH|universal>/<RELEASE_CHANNEL>/release.json -> custom -> target
func (tc *tufCheckup) fetchTufVersion(client *http.Client, url *url.URL) (string, error) {
response, err := client.Get(url.String())
func (tc *tufCheckup) remoteTufMetadata(client *http.Client, tufUrl *url.URL) (string, int, error) {
response, err := client.Get(tufUrl.String())
if err != nil {
return "", err
return "", 0, err
}
defer response.Body.Close()

var releaseTargets tufRelease
if err := json.NewDecoder(response.Body).Decode(&releaseTargets); err != nil {
return "", err
return "", 0, err
}

upgradePathKey := fmt.Sprintf("launcher/%s/%s/%s/release.json", runtime.GOOS, tuf.PlatformArch(), tc.k.UpdateChannel())
hostTargets, ok := releaseTargets.Signed.Targets[upgradePathKey]

if !ok {
return "", fmt.Errorf("unable to find matching release data for %s", upgradePathKey)
return "", releaseTargets.Signed.Version, fmt.Errorf("unable to find matching release data for %s", upgradePathKey)
}

return hostTargets.Custom.Target, releaseTargets.Signed.Version, nil
}

// localTufMetadata inspects the local target metadata to extract the metadata version.
// We can compare this version to the one retrieved by `remoteTufMetadata` to see whether
// this installation of launcher is successfully updating its TUF repository.
func (tc *tufCheckup) localTufMetadata() (int, error) {
targetsMetadataFile := filepath.Join(tuf.LocalTufDirectory(tc.k.RootDirectory()), "targets.json")
b, err := os.ReadFile(targetsMetadataFile)
if err != nil {
return 0, fmt.Errorf("reading file %s: %w", targetsMetadataFile, err)
}

var releaseTargets tufRelease
if err := json.Unmarshal(b, &releaseTargets); err != nil {
return 0, fmt.Errorf("unmarshalling file %s: %w", targetsMetadataFile, err)
}

return releaseTargets.Signed.Version, nil
}

// versionsInLauncherLibrary returns all updates available in the launcher update directory.
func (tc *tufCheckup) versionsInLauncherLibrary() ([]string, error) {
updatesDir := tc.k.UpdateDirectory()
if updatesDir == "" {
updatesDir = tuf.DefaultLibraryDirectory(tc.k.RootDirectory())
}

launcherVersionMatchPattern := filepath.Join(updatesDir, "launcher", "*")
launcherMatches, err := filepath.Glob(launcherVersionMatchPattern)
if err != nil {
return nil, fmt.Errorf("globbing for launcher matches at %s: %w", launcherVersionMatchPattern, err)
}

return launcherMatches, nil
}

// versionsInOsquerydLibrary returns all updates available in the osqueryd update directory.
func (tc *tufCheckup) versionsInOsquerydLibrary() ([]string, error) {
updatesDir := tc.k.UpdateDirectory()
if updatesDir == "" {
updatesDir = tuf.DefaultLibraryDirectory(tc.k.RootDirectory())
}

osquerydVersionMatchPattern := filepath.Join(updatesDir, "osqueryd", "*")
osquerydMatches, err := filepath.Glob(osquerydVersionMatchPattern)
if err != nil {
return nil, fmt.Errorf("globbing for osqueryd matches at %s: %w", osquerydVersionMatchPattern, err)
}

return osquerydMatches, nil
}

// selectedVersions returns the versions of launcher and osqueryd that the current
// installation would select as the correct version to run.
func (tc *tufCheckup) selectedVersions() map[string]map[string]string {
selectedVersions := map[string]map[string]string{
"launcher": make(map[string]string),
"osqueryd": make(map[string]string),
}

if launcherVersion, err := tuf.CheckOutLatestWithoutConfig("launcher", log.NewNopLogger()); err != nil {
selectedVersions["launcher"]["version"] = fmt.Sprintf("error checking out latest version: %v", err)
} else {
selectedVersions["launcher"]["version"] = launcherVersion.String()
}

if osquerydVersion, err := tuf.CheckOutLatestWithoutConfig("osqueryd", log.NewNopLogger()); err != nil {
selectedVersions["osqueryd"]["version"] = fmt.Sprintf("error checking out latest version: %v", err)
} else {
selectedVersions["osqueryd"]["version"] = osquerydVersion.String()
}

return hostTargets.Custom.Target, nil
return selectedVersions
}

0 comments on commit 3c18d7d

Please sign in to comment.