Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Auditbeat] Package: Auto-detect package directories #12289

Merged
merged 2 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Fix formatting of config files on macOS and Windows. {pull}12148[12148]
- Fix direction of incoming IPv6 sockets. {pull}12248[12248]
- Package dataset: Close librpm handle. {pull}12215[12215]
- Package dataset: Auto-detect package directories. {pull}12289[12289]

*Filebeat*

Expand Down
118 changes: 55 additions & 63 deletions x-pack/auditbeat/module/system/package/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"time"
Expand All @@ -30,22 +31,13 @@ import (
"github.com/elastic/beats/metricbeat/mb"
"github.com/elastic/beats/x-pack/auditbeat/cache"
"github.com/elastic/beats/x-pack/auditbeat/module/system"
"github.com/elastic/go-sysinfo"
"github.com/elastic/go-sysinfo/types"
)

const (
moduleName = "system"
metricsetName = "package"
namespace = "system.audit.package"

redhat = "redhat"
suse = "suse"
debian = "debian"
darwin = "darwin"

dpkgStatusFile = "/var/lib/dpkg/status"

bucketName = "package.v1"
bucketKeyPackages = "packages"
bucketKeyStateTimestamp = "state_timestamp"
Expand All @@ -55,6 +47,8 @@ const (
)

var (
rpmPath = "/var/lib/rpm"
dpkgPath = "/var/lib/dpkg"
homebrewCellarPath = "/usr/local/Cellar"
)

Expand Down Expand Up @@ -97,7 +91,8 @@ type MetricSet struct {
cache *cache.Cache
bucket datastore.Bucket
lastState time.Time
osFamily string

suppressNoPackageWarnings bool
}

// Package represents information for a package.
Expand Down Expand Up @@ -170,20 +165,6 @@ func (pkg Package) entityID(hostID string) string {
return h.Sum()
}

func getOS() (*types.OSInfo, error) {
host, err := sysinfo.Host()
if err != nil {
return nil, errors.Wrap(err, "error getting the OS")
}

hostInfo := host.Info()
if hostInfo.OS == nil {
return nil, errors.New("no host info")
}

return hostInfo.OS, nil
}

// New constructs a new MetricSet.
func New(base mb.BaseMetricSet) (mb.MetricSet, error) {
cfgwarn.Beta("The %v/%v dataset is beta", moduleName, metricsetName)
Expand All @@ -206,26 +187,6 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {
bucket: bucket,
}

osInfo, err := getOS()
if err != nil {
return nil, errors.Wrap(err, "error determining operating system")
}
ms.osFamily = osInfo.Family
switch osInfo.Family {
case redhat, suse:
// ok
case debian:
if _, err := os.Stat(dpkgStatusFile); err != nil {
return nil, errors.Wrapf(err, "error looking up %s", dpkgStatusFile)
}
case darwin:
if _, err := os.Stat(homebrewCellarPath); err != nil {
ms.log.Errorf("Homebrew does not seem to be installed. Will keep trying. Error: %v", err)
}
default:
return nil, fmt.Errorf("this metricset does not support OS family %v", osInfo.Family)
}

// Load from disk: Time when state was last sent
err = bucket.Load(bucketKeyStateTimestamp, func(blob []byte) error {
if len(blob) > 0 {
Expand Down Expand Up @@ -291,11 +252,10 @@ func (ms *MetricSet) Fetch(report mb.ReporterV2) {
func (ms *MetricSet) reportState(report mb.ReporterV2) error {
ms.lastState = time.Now()

packages, err := ms.getPackages(ms.osFamily)
packages, err := ms.getPackages()
if err != nil {
return errors.Wrap(err, "failed to get packages")
}
ms.log.Debugf("Found %v packages", len(packages))

stateID, err := uuid.NewV4()
if err != nil {
Expand Down Expand Up @@ -325,11 +285,10 @@ func (ms *MetricSet) reportState(report mb.ReporterV2) error {

// reportChanges detects and reports any changes to installed packages on this system since the last call.
func (ms *MetricSet) reportChanges(report mb.ReporterV2) error {
packages, err := ms.getPackages(ms.osFamily)
packages, err := ms.getPackages()
if err != nil {
return errors.Wrap(err, "failed to get packages")
}
ms.log.Debugf("Found %v packages", len(packages))

newInCache, missingFromCache := ms.cache.DiffAndUpdateCache(convertToCacheable(packages))
newPackages := convertToPackage(newInCache)
Expand Down Expand Up @@ -479,35 +438,68 @@ func (ms *MetricSet) savePackagesToDisk(packages []*Package) error {
return nil
}

func (ms *MetricSet) getPackages(osFamily string) (packages []*Package, err error) {
switch osFamily {
case redhat, suse:
packages, err = listRPMPackages()
func (ms *MetricSet) getPackages() (packages []*Package, err error) {
var foundPackageManager bool

_, err = os.Stat(rpmPath)
if err == nil {
foundPackageManager = true

rpmPackages, err := listRPMPackages()
if err != nil {
return nil, errors.Wrap(err, "error getting RPM packages")
}
case debian:
packages, err = listDebPackages()
ms.log.Debugf("RPM packages: %v", len(rpmPackages))

packages = append(packages, rpmPackages...)
} else if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "error opening %v", rpmPath)
}

_, err = os.Stat(dpkgPath)
if err == nil {
foundPackageManager = true

dpkgPackages, err := listDebPackages()
if err != nil {
return nil, errors.Wrap(err, "error getting DEB packages")
}
case darwin:
packages, err = listBrewPackages()
ms.log.Debugf("DEB packages: %v", len(dpkgPackages))

packages = append(packages, dpkgPackages...)
} else if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "error opening %v", dpkgPath)
}

_, err = os.Stat(homebrewCellarPath)
if err == nil {
foundPackageManager = true

homebrewPackages, err := listBrewPackages()
if err != nil {
if os.IsNotExist(err) {
ms.log.Debugf("Homebrew not installed: %v", err)
} else {
return nil, errors.Wrap(err, "error getting Homebrew packages")
}
return nil, errors.Wrap(err, "error getting Homebrew packages")
}
default:
return nil, errors.Errorf("unknown OS %v - this should not have happened", osFamily)
ms.log.Debugf("Homebrew packages: %v", len(homebrewPackages))

packages = append(packages, homebrewPackages...)
} else if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "error opening %v", homebrewCellarPath)
}

if !foundPackageManager && !ms.suppressNoPackageWarnings {
ms.log.Warnf("No supported package managers found. None of %v, %v, %v exist.",
rpmPath, dpkgPath, homebrewCellarPath)

// Only warn once at the start of Auditbeat.
ms.suppressNoPackageWarnings = true
}

return packages, nil
}

func listDebPackages() ([]*Package, error) {
dpkgStatusFile := filepath.Join(dpkgPath, "status")

file, err := os.Open(dpkgStatusFile)
if err != nil {
return nil, errors.Wrapf(err, "error opening %s", dpkgStatusFile)
Expand Down
12 changes: 5 additions & 7 deletions x-pack/auditbeat/module/system/package/rpm_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,20 @@
package pkg

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestRPMPackages(t *testing.T) {
os, err := getOS()
if err != nil {
_, err := os.Stat(rpmPath)
if os.IsNotExist(err) {
t.Skipf("RPM test only on systems where %v exists", rpmPath)
} else if err != nil {
t.Fatal(err)
}

if os.Family != "redhat" {
t.Skip("RPM test only on Redhat systems")
}

// Control using the exec command
packagesExpected, err := rpmPackagesByExec()
if err != nil {
Expand All @@ -34,5 +33,4 @@ func TestRPMPackages(t *testing.T) {
}

assert.EqualValues(t, packagesExpected, packages)

}