mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
> Closes #30913 # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [x] Manual QA for all new/changed functionality
113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
package installer_cache
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/fleetdm/fleet/v4/cmd/osquery-perf/osquery_perf"
|
|
"github.com/fleetdm/fleet/v4/pkg/file"
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
"github.com/fleetdm/fleet/v4/server/service"
|
|
)
|
|
|
|
// Metadata holds the metadata for software installers.
|
|
// To extract the metadata, we must download the file. Once the file has been downloaded once and analyzed,
|
|
// the other agents can use the cache to get the appropriate metadata.
|
|
type Metadata struct {
|
|
mu sync.Mutex
|
|
cache map[uint]*file.InstallerMetadata
|
|
Stats *osquery_perf.Stats
|
|
}
|
|
|
|
func (c *Metadata) Get(installer *fleet.SoftwareInstallDetails, orbitClient *service.OrbitClient) (meta *file.InstallerMetadata,
|
|
cacheMiss bool, err error,
|
|
) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if c.cache == nil {
|
|
c.cache = make(map[uint]*file.InstallerMetadata, 1)
|
|
}
|
|
|
|
meta, ok := c.cache[installer.InstallerID]
|
|
if !ok {
|
|
var err error
|
|
meta, err = c.populateMetadata(installer, orbitClient)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
c.cache[installer.InstallerID] = meta
|
|
cacheMiss = true
|
|
}
|
|
return meta, cacheMiss, nil
|
|
}
|
|
|
|
func (c *Metadata) populateMetadata(installer *fleet.SoftwareInstallDetails, orbitClient *service.OrbitClient) (*file.InstallerMetadata,
|
|
error,
|
|
) {
|
|
tmpDir, err := os.MkdirTemp("", "")
|
|
if err != nil {
|
|
c.Stats.IncrementOrbitErrors()
|
|
log.Println("level=error, create temp dir:", err)
|
|
return nil, err
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
var installerPath string
|
|
if installer.SoftwareInstallerURL != nil {
|
|
installerPath, err = orbitClient.DownloadSoftwareInstallerFromURL(installer.SoftwareInstallerURL.URL,
|
|
installer.SoftwareInstallerURL.Filename, tmpDir, func(n int) {
|
|
})
|
|
if err != nil {
|
|
log.Printf("level=error, msg=download software installer from URL; is CloudFront CDN configured correctly?, err=%s", err)
|
|
c.Stats.IncrementOrbitErrors()
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if installerPath == "" {
|
|
installerPath, err = orbitClient.DownloadSoftwareInstaller(installer.InstallerID, tmpDir, func(n int) {
|
|
})
|
|
if err != nil {
|
|
log.Printf("level=error, msg=download software installer, err=%s", err)
|
|
c.Stats.IncrementOrbitErrors()
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Installer might from a Fleet-maintained app. If so, it might be a .dmg or .zip.
|
|
// file.ExtractInstallerMetadata doesn't support .dmg or .zip files, so we have to create
|
|
// an InstallerMetadata manually.
|
|
var extension string
|
|
switch {
|
|
case strings.HasSuffix(installerPath, ".dmg"):
|
|
extension = ".dmg"
|
|
case strings.HasSuffix(installerPath, ".zip"):
|
|
extension = ".zip"
|
|
}
|
|
|
|
if extension != "" {
|
|
return &file.InstallerMetadata{
|
|
Name: path.Base(installerPath),
|
|
Extension: extension,
|
|
}, nil
|
|
}
|
|
|
|
// Figure out what we're actually installing here and add it to software inventory
|
|
tfr, err := fleet.NewKeepFileReader(installerPath)
|
|
if err != nil {
|
|
c.Stats.IncrementOrbitErrors()
|
|
log.Println("level=error, open installer:", err)
|
|
return nil, err
|
|
}
|
|
defer tfr.Close()
|
|
item, err := file.ExtractInstallerMetadata(tfr)
|
|
if err != nil {
|
|
c.Stats.IncrementOrbitErrors()
|
|
log.Println("level=error, extract installer metadata:", err)
|
|
return nil, err
|
|
}
|
|
return item, nil
|
|
}
|