cscli setup (#1923)
Detect running services and generate acquisition configuration
This commit is contained in:
parent
7e871d2278
commit
b6be18ca65
15 changed files with 4040 additions and 12 deletions
|
@ -242,6 +242,10 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
||||||
rootCmd.AddCommand(NewNotificationsCmd())
|
rootCmd.AddCommand(NewNotificationsCmd())
|
||||||
rootCmd.AddCommand(NewSupportCmd())
|
rootCmd.AddCommand(NewSupportCmd())
|
||||||
|
|
||||||
|
if fflag.CscliSetup.IsEnabled() {
|
||||||
|
rootCmd.AddCommand(NewSetupCmd())
|
||||||
|
}
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
if bincoverTesting != "" {
|
if bincoverTesting != "" {
|
||||||
log.Debug("coverage report is enabled")
|
log.Debug("coverage report is enabled")
|
||||||
|
|
312
cmd/crowdsec-cli/setup.go
Normal file
312
cmd/crowdsec-cli/setup.go
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
goccyyaml "github.com/goccy/go-yaml"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/setup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSetupCmd defines the "cscli setup" command.
|
||||||
|
func NewSetupCmd() *cobra.Command {
|
||||||
|
cmdSetup := &cobra.Command{
|
||||||
|
Use: "setup",
|
||||||
|
Short: "Tools to configure crowdsec",
|
||||||
|
Long: "Manage hub configuration and service detection",
|
||||||
|
Args: cobra.MinimumNArgs(0),
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cscli setup detect
|
||||||
|
//
|
||||||
|
{
|
||||||
|
cmdSetupDetect := &cobra.Command{
|
||||||
|
Use: "detect",
|
||||||
|
Short: "detect running services, generate a setup file",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: runSetupDetect,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultServiceDetect := csconfig.DefaultConfigPath("hub", "detect.yaml")
|
||||||
|
|
||||||
|
flags := cmdSetupDetect.Flags()
|
||||||
|
flags.String("detect-config", defaultServiceDetect, "path to service detection configuration")
|
||||||
|
flags.Bool("list-supported-services", false, "do not detect; only print supported services")
|
||||||
|
flags.StringSlice("force-unit", nil, "force detection of a systemd unit (can be repeated)")
|
||||||
|
flags.StringSlice("force-process", nil, "force detection of a running process (can be repeated)")
|
||||||
|
flags.StringSlice("skip-service", nil, "ignore a service, don't recommend hub/datasources (can be repeated)")
|
||||||
|
flags.String("force-os-family", "", "override OS.Family: one of linux, freebsd, windows or darwin")
|
||||||
|
flags.String("force-os-id", "", "override OS.ID=[debian | ubuntu | , redhat...]")
|
||||||
|
flags.String("force-os-version", "", "override OS.RawVersion (of OS or Linux distribution)")
|
||||||
|
flags.Bool("snub-systemd", false, "don't use systemd, even if available")
|
||||||
|
flags.Bool("yaml", false, "output yaml, not json")
|
||||||
|
cmdSetup.AddCommand(cmdSetupDetect)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cscli setup install-hub
|
||||||
|
//
|
||||||
|
{
|
||||||
|
cmdSetupInstallHub := &cobra.Command{
|
||||||
|
Use: "install-hub [setup_file] [flags]",
|
||||||
|
Short: "install items from a setup file",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: runSetupInstallHub,
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmdSetupInstallHub.Flags()
|
||||||
|
flags.Bool("dry-run", false, "don't install anything; print out what would have been")
|
||||||
|
cmdSetup.AddCommand(cmdSetupInstallHub)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cscli setup datasources
|
||||||
|
//
|
||||||
|
{
|
||||||
|
cmdSetupDataSources := &cobra.Command{
|
||||||
|
Use: "datasources [setup_file] [flags]",
|
||||||
|
Short: "generate datasource (acquisition) configuration from a setup file",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: runSetupDataSources,
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmdSetupDataSources.Flags()
|
||||||
|
flags.String("to-dir", "", "write the configuration to a directory, in multiple files")
|
||||||
|
cmdSetup.AddCommand(cmdSetupDataSources)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cscli setup validate
|
||||||
|
//
|
||||||
|
{
|
||||||
|
cmdSetupValidate := &cobra.Command{
|
||||||
|
Use: "validate [setup_file]",
|
||||||
|
Short: "validate a setup file",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: runSetupValidate,
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdSetup.AddCommand(cmdSetupValidate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdSetup
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSetupDetect(cmd *cobra.Command, args []string) error {
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
detectConfigFile, err := flags.GetString("detect-config")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
listSupportedServices, err := flags.GetBool("list-supported-services")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
forcedUnits, err := flags.GetStringSlice("force-unit")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
forcedProcesses, err := flags.GetStringSlice("force-process")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
forcedOSFamily, err := flags.GetString("force-os-family")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
forcedOSID, err := flags.GetString("force-os-id")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
forcedOSVersion, err := flags.GetString("force-os-version")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
skipServices, err := flags.GetStringSlice("skip-service")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
snubSystemd, err := flags.GetBool("snub-systemd")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !snubSystemd {
|
||||||
|
_, err := exec.LookPath("systemctl")
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("systemctl not available: snubbing systemd")
|
||||||
|
snubSystemd = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outYaml, err := flags.GetBool("yaml")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if forcedOSFamily == "" && forcedOSID != "" {
|
||||||
|
log.Debug("force-os-id is set: force-os-family defaults to 'linux'")
|
||||||
|
forcedOSFamily = "linux"
|
||||||
|
}
|
||||||
|
|
||||||
|
if listSupportedServices {
|
||||||
|
supported, err := setup.ListSupported(detectConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, svc := range supported {
|
||||||
|
fmt.Println(svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := setup.DetectOptions{
|
||||||
|
ForcedUnits: forcedUnits,
|
||||||
|
ForcedProcesses: forcedProcesses,
|
||||||
|
ForcedOS: setup.ExprOS{
|
||||||
|
Family: forcedOSFamily,
|
||||||
|
ID: forcedOSID,
|
||||||
|
RawVersion: forcedOSVersion,
|
||||||
|
},
|
||||||
|
SkipServices: skipServices,
|
||||||
|
SnubSystemd: snubSystemd,
|
||||||
|
}
|
||||||
|
|
||||||
|
hubSetup, err := setup.Detect(detectConfigFile, opts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("detecting services: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
setup, err := setupAsString(hubSetup, outYaml)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(setup)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupAsString(cs setup.Setup, outYaml bool) (string, error) {
|
||||||
|
var (
|
||||||
|
ret []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
wrap := func(err error) error {
|
||||||
|
return fmt.Errorf("while marshaling setup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
indentLevel := 2
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
enc := yaml.NewEncoder(buf)
|
||||||
|
enc.SetIndent(indentLevel)
|
||||||
|
|
||||||
|
if err = enc.Encode(cs); err != nil {
|
||||||
|
return "", wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = enc.Close(); err != nil {
|
||||||
|
return "", wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = buf.Bytes()
|
||||||
|
|
||||||
|
if !outYaml {
|
||||||
|
// take a general approach to output json, so we avoid the
|
||||||
|
// double tags in the structures and can use go-yaml features
|
||||||
|
// missing from the json package
|
||||||
|
ret, err = goccyyaml.YAMLToJSON(ret)
|
||||||
|
if err != nil {
|
||||||
|
return "", wrap(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSetupDataSources(cmd *cobra.Command, args []string) error {
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
fromFile := args[0]
|
||||||
|
|
||||||
|
toDir, err := flags.GetString("to-dir")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
input, err := os.ReadFile(fromFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("while reading setup file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := setup.DataSources(input, toDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if toDir == "" {
|
||||||
|
fmt.Println(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSetupInstallHub(cmd *cobra.Command, args []string) error {
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
fromFile := args[0]
|
||||||
|
|
||||||
|
dryRun, err := flags.GetBool("dry-run")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
input, err := os.ReadFile(fromFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("while reading file %s: %w", fromFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = setup.InstallHubItems(csConfig, input, dryRun); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSetupValidate(cmd *cobra.Command, args []string) error {
|
||||||
|
fromFile := args[0]
|
||||||
|
input, err := os.ReadFile(fromFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("while reading stdin: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = setup.Validate(input); err != nil {
|
||||||
|
fmt.Printf("%v\n", err)
|
||||||
|
return fmt.Errorf("invalid setup file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
482
config/detect.yaml
Normal file
482
config/detect.yaml
Normal file
|
@ -0,0 +1,482 @@
|
||||||
|
---
|
||||||
|
version: 1.0
|
||||||
|
|
||||||
|
# TODO: This file must be reviewed before the `cscli setup` command becomes GA
|
||||||
|
|
||||||
|
detect:
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/apache2
|
||||||
|
#
|
||||||
|
|
||||||
|
# XXX some distro is using this path?
|
||||||
|
# - /var/log/*http*/*.log
|
||||||
|
|
||||||
|
apache2-systemd-deb:
|
||||||
|
when:
|
||||||
|
- UnitFound("apache2.service")
|
||||||
|
- PathExists("/etc/debian_version")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
|
||||||
|
apache2-systemd-rpm:
|
||||||
|
when:
|
||||||
|
- UnitFound("httpd.service")
|
||||||
|
- PathExists("/etc/redhat-release")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
filenames:
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
# XXX /var/log/*http*/*.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/asterisk
|
||||||
|
#
|
||||||
|
|
||||||
|
asterisk-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("asterisk.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/asterisk
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: asterisk
|
||||||
|
filenames:
|
||||||
|
- /var/log/asterisk/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/caddy
|
||||||
|
#
|
||||||
|
|
||||||
|
caddy-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("caddy.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/caddy
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: caddy
|
||||||
|
filenames:
|
||||||
|
- /var/log/caddy/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/dovecot
|
||||||
|
#
|
||||||
|
|
||||||
|
dovecot-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("dovecot.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/dovecot
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/mail.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# LePresidente/emby
|
||||||
|
#
|
||||||
|
|
||||||
|
emby-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("emby-server.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- LePresidente/emby
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: emby
|
||||||
|
filenames:
|
||||||
|
- /var/log/embyserver.txt
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/endlessh
|
||||||
|
#
|
||||||
|
|
||||||
|
endlessh-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("endlessh.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/endlessh
|
||||||
|
datasource:
|
||||||
|
source: journalctl
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
# XXX this? or /var/log/syslog?
|
||||||
|
journalctl_filter:
|
||||||
|
- "_SYSTEMD_UNIT=endlessh.service"
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/gitea
|
||||||
|
#
|
||||||
|
|
||||||
|
# XXX untested
|
||||||
|
|
||||||
|
gitea-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("gitea.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/gitea
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: gitea
|
||||||
|
filenames:
|
||||||
|
- /var/log/gitea.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/haproxy
|
||||||
|
#
|
||||||
|
|
||||||
|
haproxy-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("haproxy.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/haproxy
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: haproxy
|
||||||
|
filenames:
|
||||||
|
- /var/log/haproxy/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# firewallservices/lemonldap-ng
|
||||||
|
#
|
||||||
|
|
||||||
|
lemonldap-ng-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("lemonldap-ng-fastcgi-server.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- firewallservices/lemonldap-ng
|
||||||
|
#datasource:
|
||||||
|
# # XXX todo where are the logs?
|
||||||
|
# labels:
|
||||||
|
# type: syslog
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/mariadb
|
||||||
|
#
|
||||||
|
|
||||||
|
mariadb-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("mariadb.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/mariadb
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: mysql
|
||||||
|
filenames:
|
||||||
|
- /var/log/mysql/error.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/mysql
|
||||||
|
#
|
||||||
|
|
||||||
|
mysql-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("mysql.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/mysql
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: mysql
|
||||||
|
filenames:
|
||||||
|
- /var/log/mysql/error.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/nginx
|
||||||
|
#
|
||||||
|
|
||||||
|
nginx-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("nginx.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/nginx
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: nginx
|
||||||
|
filenames:
|
||||||
|
- /var/log/nginx/*.log
|
||||||
|
|
||||||
|
openresty-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("openresty.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/nginx
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: nginx
|
||||||
|
filenames:
|
||||||
|
- /usr/local/openresty/nginx/logs/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/odoo
|
||||||
|
#
|
||||||
|
|
||||||
|
odoo-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("odoo.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/odoo
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: odoo
|
||||||
|
filenames:
|
||||||
|
- /var/log/odoo/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# LePresidente/ombi
|
||||||
|
#
|
||||||
|
|
||||||
|
# This only works on deb-based systems. On other distributions, the
|
||||||
|
# application is run from the release tarball and the log location depends on
|
||||||
|
# the location it's run from.
|
||||||
|
|
||||||
|
ombi-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("ombi.service")
|
||||||
|
- PathExists("/etc/debian_version")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- LePresidente/ombi
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: ombi
|
||||||
|
filenames:
|
||||||
|
- /var/log/ombi/log-*.txt
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/pgsql
|
||||||
|
#
|
||||||
|
|
||||||
|
pgsql-systemd-deb:
|
||||||
|
when:
|
||||||
|
- UnitFound("postgresql.service")
|
||||||
|
- PathExists("/etc/debian_version")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/pgsql
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: postgres
|
||||||
|
filenames:
|
||||||
|
- /var/log/postgresql/*.log
|
||||||
|
|
||||||
|
pgsql-systemd-rpm:
|
||||||
|
when:
|
||||||
|
- UnitFound("postgresql.service")
|
||||||
|
- PathExists("/etc/redhat-release")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/pgsql
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: postgres
|
||||||
|
filenames:
|
||||||
|
- /var/lib/pgsql/data/log/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/postfix
|
||||||
|
#
|
||||||
|
|
||||||
|
postfix-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("postfix.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/postfix
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/mail.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/proftpd
|
||||||
|
#
|
||||||
|
|
||||||
|
proftpd-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("proftpd.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/proftpd
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: proftpd
|
||||||
|
filenames:
|
||||||
|
- /var/log/proftpd/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# fulljackz/pureftpd
|
||||||
|
#
|
||||||
|
|
||||||
|
pureftpd-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("pure-ftpd.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- fulljackz/pureftpd
|
||||||
|
# XXX ?
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/pure-ftpd/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/smb
|
||||||
|
#
|
||||||
|
|
||||||
|
smb-systemd:
|
||||||
|
when:
|
||||||
|
# deb -> smbd.service
|
||||||
|
# rpm -> smb.service
|
||||||
|
- UnitFound("smbd.service") or UnitFound("smb.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/smb
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: smb
|
||||||
|
filenames:
|
||||||
|
- /var/log/samba*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/sshd
|
||||||
|
#
|
||||||
|
|
||||||
|
sshd-systemd:
|
||||||
|
when:
|
||||||
|
# deb -> ssh.service
|
||||||
|
# rpm -> sshd.service
|
||||||
|
- UnitFound("ssh.service") or UnitFound("sshd.service") or UnitFound("ssh.socket") or UnitFound("sshd.socket")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/sshd
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/auth.log
|
||||||
|
- /var/log/sshd.log
|
||||||
|
- /var/log/secure
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/suricata
|
||||||
|
#
|
||||||
|
|
||||||
|
suricata-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("suricata.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/suricata
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: suricata-evelogs
|
||||||
|
filenames:
|
||||||
|
- /var/log/suricata/eve.json
|
||||||
|
|
||||||
|
#
|
||||||
|
# crowdsecurity/vsftpd
|
||||||
|
#
|
||||||
|
|
||||||
|
vsftpd-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("vsftpd.service")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/vsftpd
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: vsftpd
|
||||||
|
filenames:
|
||||||
|
- /var/log/vsftpd/*.log
|
||||||
|
|
||||||
|
#
|
||||||
|
# Operating Systems
|
||||||
|
#
|
||||||
|
|
||||||
|
linux:
|
||||||
|
when:
|
||||||
|
- OS.Family == "linux"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/linux
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/syslog
|
||||||
|
- /var/log/kern.log
|
||||||
|
- /var/log/messages
|
||||||
|
|
||||||
|
freebsd:
|
||||||
|
when:
|
||||||
|
- OS.Family == "freebsd"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/freebsd
|
||||||
|
|
||||||
|
windows:
|
||||||
|
when:
|
||||||
|
- OS.Family == "windows"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/windows
|
||||||
|
|
||||||
|
#
|
||||||
|
# anti-lockout
|
||||||
|
#
|
||||||
|
|
||||||
|
whitelists:
|
||||||
|
install:
|
||||||
|
parsers:
|
||||||
|
- crowdsecurity/whitelists
|
17
go.mod
17
go.mod
|
@ -53,7 +53,7 @@ require (
|
||||||
github.com/r3labs/diff/v2 v2.14.1
|
github.com/r3labs/diff/v2 v2.14.1
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.1
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||||
google.golang.org/grpc v1.47.0
|
google.golang.org/grpc v1.47.0
|
||||||
|
@ -65,6 +65,7 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Masterminds/semver v1.5.0
|
||||||
github.com/Masterminds/sprig/v3 v3.2.2
|
github.com/Masterminds/sprig/v3 v3.2.2
|
||||||
github.com/aquasecurity/table v1.8.0
|
github.com/aquasecurity/table v1.8.0
|
||||||
github.com/beevik/etree v1.1.0
|
github.com/beevik/etree v1.1.0
|
||||||
|
@ -75,11 +76,14 @@ require (
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||||
github.com/google/winops v0.0.0-20211216095627-f0e86eb1453b
|
github.com/google/winops v0.0.0-20211216095627-f0e86eb1453b
|
||||||
github.com/ivanpirog/coloredcobra v1.0.1
|
github.com/ivanpirog/coloredcobra v1.0.1
|
||||||
|
github.com/lithammer/dedent v1.1.0
|
||||||
github.com/mattn/go-isatty v0.0.14
|
github.com/mattn/go-isatty v0.0.14
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||||
github.com/segmentio/kafka-go v0.4.34
|
github.com/segmentio/kafka-go v0.4.34
|
||||||
|
github.com/shirou/gopsutil/v3 v3.22.12
|
||||||
github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c
|
github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
golang.org/x/sys v0.3.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
k8s.io/apiserver v0.22.5
|
k8s.io/apiserver v0.22.5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -101,6 +105,7 @@ require (
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-openapi/analysis v0.19.16 // indirect
|
github.com/go-openapi/analysis v0.19.16 // indirect
|
||||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
|
@ -115,7 +120,7 @@ require (
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect
|
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/gorilla/mux v1.7.3 // indirect
|
github.com/gorilla/mux v1.7.3 // indirect
|
||||||
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
|
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
|
||||||
|
@ -136,6 +141,7 @@ require (
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/klauspost/compress v1.15.7 // indirect
|
github.com/klauspost/compress v1.15.7 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.6 // indirect
|
github.com/mailru/easyjson v0.7.6 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
@ -154,6 +160,7 @@ require (
|
||||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
@ -163,9 +170,12 @@ require (
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/tidwall/gjson v1.13.0 // indirect
|
github.com/tidwall/gjson v1.13.0 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.6 // indirect
|
github.com/ugorji/go/codec v1.2.6 // indirect
|
||||||
github.com/vjeantet/grok v1.0.1 // indirect
|
github.com/vjeantet/grok v1.0.1 // indirect
|
||||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
github.com/zclconf/go-cty v1.8.0 // indirect
|
github.com/zclconf/go-cty v1.8.0 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.9.0 // indirect
|
go.mongodb.org/mongo-driver v1.9.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||||
|
@ -177,7 +187,6 @@ require (
|
||||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
k8s.io/api v0.25.2 // indirect
|
k8s.io/api v0.25.2 // indirect
|
||||||
k8s.io/apimachinery v0.25.2 // indirect
|
k8s.io/apimachinery v0.25.2 // indirect
|
||||||
k8s.io/klog/v2 v2.70.1 // indirect
|
k8s.io/klog/v2 v2.70.1 // indirect
|
||||||
|
|
37
go.sum
37
go.sum
|
@ -57,6 +57,8 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
|
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||||
|
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
|
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
|
||||||
|
@ -120,8 +122,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
|
github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
|
||||||
github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
|
github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
|
||||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
|
||||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
|
||||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||||
|
@ -251,6 +251,8 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
|
||||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||||
|
@ -438,8 +440,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
@ -641,8 +643,12 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
|
||||||
|
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
@ -770,6 +776,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
@ -836,6 +844,8 @@ github.com/segmentio/kafka-go v0.4.34 h1:Dm6YlLMiVSiwwav20KY0AoY63s661FXevwJ3CVH
|
||||||
github.com/segmentio/kafka-go v0.4.34/go.mod h1:GAjxBQJdQMB5zfNA21AhpaqOB2Mu+w3De4ni3Gbm8y0=
|
github.com/segmentio/kafka-go v0.4.34/go.mod h1:GAjxBQJdQMB5zfNA21AhpaqOB2Mu+w3De4ni3Gbm8y0=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.22.12 h1:oG0ns6poeUSxf78JtOsfygNWuEHYYz8hnnNg7P04TJs=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.22.12/go.mod h1:Xd7P1kwZcp5VW52+9XsirIKd/BROzbb2wdX3Kqlz9uI=
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
@ -874,8 +884,9 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
@ -885,8 +896,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg=
|
github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg=
|
||||||
github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo=
|
github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo=
|
||||||
|
@ -898,6 +910,10 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||||
|
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||||
|
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
@ -928,6 +944,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
|
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
|
||||||
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
@ -1136,6 +1154,7 @@ golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -1167,6 +1186,7 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -1186,8 +1206,9 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
|
340
pkg/setup/README.md
Normal file
340
pkg/setup/README.md
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
|
||||||
|
> **_NOTE_**: The following document describes an experimental, work-in-progress feature. To enable the `cscli setup` command, set the environment variable `CROWDSEC_FEATURE_CSCLI_SETUP=true` or add the line " - cscli_setup" to `/etc/crowdsec/feature.yaml`. Any feedback is welcome.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# cscli setup
|
||||||
|
|
||||||
|
The "cscli setup" command can configure a crowdsec instance based on the services that are installed or running on the server.
|
||||||
|
|
||||||
|
There are three main subcommands:
|
||||||
|
|
||||||
|
- `cscli setup detect`: *detect* the services, the OS family, version or the Linux distribution
|
||||||
|
- `cscli setup install-hub`: *install* the recommended collections, parsers, etc. based on the detection result
|
||||||
|
- `cscli setup datasources`: *generate* the appropriate acquisition rules
|
||||||
|
|
||||||
|
The setup command is used in the `wizard.sh` script, but can also be invoked by hand or customized via a configuration file
|
||||||
|
by adding new services, log locations and detection rules.
|
||||||
|
|
||||||
|
Detection and installation are performed as separate steps, as you can see in the following diagram:
|
||||||
|
|
||||||
|
```
|
||||||
|
+-------------+
|
||||||
|
| |
|
||||||
|
| detect.yaml |
|
||||||
|
| |
|
||||||
|
+-------------+
|
||||||
|
|
|
||||||
|
v
|
||||||
|
setup detect
|
||||||
|
|
|
||||||
|
v
|
||||||
|
+--------------+
|
||||||
|
| +---> setup install-hub +-----------------------+
|
||||||
|
| setup.yaml | | |
|
||||||
|
| +---> setup datasources --->| etc/crowdsec/acquis.d |
|
||||||
|
+--------------+ | |
|
||||||
|
+-----------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
You can inspect and customize the intermediary file (`setup.yaml`), which is useful
|
||||||
|
in case of many instances, deployment automation or unusual setups.
|
||||||
|
|
||||||
|
A subcommand can be used to check your changes in this case:
|
||||||
|
|
||||||
|
- `cscli setup validate`: *validate* or report errors on a setup file
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
Identify the existing services and write out what was detected:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# cscli setup detect > setup.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
See what was found.
|
||||||
|
|
||||||
|
```console
|
||||||
|
# cscli setup install-hub setup.yaml --dry-run
|
||||||
|
dry-run: would install collection crowdsecurity/apache2
|
||||||
|
dry-run: would install collection crowdsecurity/linux
|
||||||
|
dry-run: would install collection crowdsecurity/pgsql
|
||||||
|
dry-run: would install parser crowdsecurity/whitelists
|
||||||
|
```
|
||||||
|
|
||||||
|
Install the objects (parsers, scenarios...) required to support the detected services:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# cscli setup install-hub setup.yaml
|
||||||
|
INFO[29-06-2022 03:16:14 PM] crowdsecurity/apache2-logs : OK
|
||||||
|
INFO[29-06-2022 03:16:14 PM] Enabled parsers : crowdsecurity/apache2-logs
|
||||||
|
INFO[29-06-2022 03:16:14 PM] crowdsecurity/http-logs : OK
|
||||||
|
[...]
|
||||||
|
INFO[29-06-2022 03:16:18 PM] Enabled crowdsecurity/linux
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate the datasource configuration:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# cscli setup datasources setup.yaml --to-dir /etc/crowdsec/acquis.d
|
||||||
|
```
|
||||||
|
|
||||||
|
With the above command, each detected service gets a corresponding file in the
|
||||||
|
`acquis.d` directory. Running `cscli setup` again may add more services as they
|
||||||
|
are detected, but datasource files or hub items are never removed
|
||||||
|
automatically.
|
||||||
|
|
||||||
|
|
||||||
|
## The detect.yaml file
|
||||||
|
|
||||||
|
A detect.yaml file is downloaded when you first install crowdsec, and is updated by the `cscli hub update`
|
||||||
|
command.
|
||||||
|
|
||||||
|
> **_NOTE_**: XXX XXX - this is currently not the case, the file is distributed in the crowdsec repository, but it should change.
|
||||||
|
|
||||||
|
You can see the default location with `cscli setup detect --help | grep detect-config`
|
||||||
|
|
||||||
|
The YAML file contains a version number (always 1.0) and a list of sections, one per supported service.
|
||||||
|
|
||||||
|
Each service defines its detection rules, the recommended hub items and
|
||||||
|
recommended datasources. The same software can be defined in multiple service
|
||||||
|
sections: for example, apache on debian and fedora have different detection
|
||||||
|
rules and different datasources so it requires two sections to support both platforms.
|
||||||
|
|
||||||
|
The following are minimal `detect.yaml` examples just to show a few concepts.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: 1.0
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("apache2")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasources:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
- `ProcessRunning()` matches the process name of a running application. The
|
||||||
|
`when:` clause can contain any number of expressions, they are all evaluated
|
||||||
|
and must all return true for a service to be detected (implied *and* clause, no
|
||||||
|
short-circuit). A missing or empty `when:` section is evaluated as true.
|
||||||
|
The [expression
|
||||||
|
engine](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md)
|
||||||
|
is the same one used by CrowdSec parser filters. You can force the detection of
|
||||||
|
a process by using the `cscli setup detect... --force-process <processname>`
|
||||||
|
flag. It will always behave as if `<processname>` was running.
|
||||||
|
|
||||||
|
The `install:` section can contain any number of collections, parsers, scenarios
|
||||||
|
and postoverflows. In practices, it's most often a single collection.
|
||||||
|
|
||||||
|
The `datasource:` section is copied as-is in the acquisition file.
|
||||||
|
|
||||||
|
> **_NOTE_**: XXX TODO - the current version does not validate the `datasource:` mapping. Bad content is written to acquis.d until crowdsec chokes on it.
|
||||||
|
|
||||||
|
Detecting a running process may seem a good idea, but if a process manager like
|
||||||
|
systemd is available it's better to ask it for the information we want.
|
||||||
|
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: 1.0
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
apache2-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("apache2.service")
|
||||||
|
- OS.ID != "centos"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
|
||||||
|
apache2-systemd-centos:
|
||||||
|
when:
|
||||||
|
- UnitFound("httpd.service")
|
||||||
|
- OS.ID == "centos"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we see two more detection methods:
|
||||||
|
|
||||||
|
- `UnitFound()` matches the name of systemd units, if the are in state enabled,
|
||||||
|
generated or static. You can see here that CentOS is using a different unit
|
||||||
|
name for Apache so it must have its own service section. You can force the
|
||||||
|
detection of a unit by using the `cscli setup detect... --force-unit <unitname>` flag.
|
||||||
|
|
||||||
|
- OS.Family, OS.ID and OS.RawVersion are read from /etc/os-release in case of
|
||||||
|
Linux, and detected by other methods for FreeBSD and Windows. Under FreeBSD
|
||||||
|
and Windows, the value of OS.ID is the same as OS.Family. If OS detection
|
||||||
|
fails, it can be overridden with the flags `--force-os-family`, `--force-os-id`
|
||||||
|
and `--force-os-version`.
|
||||||
|
|
||||||
|
If you want to ignore one or more services (i.e. not install anything and not
|
||||||
|
generate acquisition rules) you can specify it with `cscli setup detect...
|
||||||
|
--skip-service <servicename>`. For example, `--skip-service apache2-systemd`.
|
||||||
|
If you want to disable systemd unit detection, use `cscli setup detect... --snub-systemd`.
|
||||||
|
|
||||||
|
If you used the `--force-process` or `--force-unit` flags, but none of the
|
||||||
|
defined services is looking for them, you'll have an error like "detecting
|
||||||
|
services: process(es) forced but not supported".
|
||||||
|
|
||||||
|
> **_NOTE_**: XXX XXX - having an error for this is maybe too much, but can tell that a configuration is outdated. Could this be a warning with optional flag to make it an error?
|
||||||
|
|
||||||
|
We used the `OS.ID` value to check for the linux distribution, but since the same configuration
|
||||||
|
is required for CentOS and the other RedHat derivatives, it's better to check for the existence
|
||||||
|
of a file that is known to exist in all of them:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: 1.0
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
apache2-systemd-deb:
|
||||||
|
when:
|
||||||
|
- UnitFound("apache2.service")
|
||||||
|
- PathExists("/etc/debian_version")
|
||||||
|
install:
|
||||||
|
# [...]
|
||||||
|
|
||||||
|
apache2-systemd-rpm:
|
||||||
|
when:
|
||||||
|
- UnitFound("httpd.service")
|
||||||
|
- PathExists("/etc/redhat-release")
|
||||||
|
install:
|
||||||
|
# [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `PathExists()` evaluates to true if a file, directory or link exists at the
|
||||||
|
given path. It does not check for broken links.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Rules can be used to detect operating systems and environments:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: 1.0
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
linux:
|
||||||
|
when:
|
||||||
|
- OS.Family == "linux"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/linux
|
||||||
|
datasource:
|
||||||
|
type: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
log_files:
|
||||||
|
- /var/log/syslog
|
||||||
|
- /var/log/kern.log
|
||||||
|
- /var/log/messages
|
||||||
|
|
||||||
|
freebsd:
|
||||||
|
when:
|
||||||
|
- OS.Family == "freebsd"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/freebsd
|
||||||
|
|
||||||
|
windows:
|
||||||
|
when:
|
||||||
|
- OS.Family == "windows"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/windows
|
||||||
|
```
|
||||||
|
|
||||||
|
The OS object contains a methods to check for version numbers:
|
||||||
|
`OS.VersionCheck("<constraint>")`. It uses the
|
||||||
|
[Masterminds/semver](https://github.com/Masterminds/semver) package and accepts
|
||||||
|
a variety of operators.
|
||||||
|
|
||||||
|
Instead of: OS.RawVersion == "1.2.3" you should use `OS.VersionCheck("~1")`,
|
||||||
|
`OS.VersionCheck("~1.2")` depending if you want to match the major or the minor
|
||||||
|
version. It's unlikely that you need to match the exact patch level.
|
||||||
|
|
||||||
|
Leading zeroes are permitted, to allow comparison of Ubuntu versions: strict semver rules would treat "22.04" as invalid.
|
||||||
|
|
||||||
|
|
||||||
|
# The `setup.yaml` file
|
||||||
|
|
||||||
|
This file does not actually have a specific name, as it's usually written to standard output.
|
||||||
|
|
||||||
|
For example, on a Debian system running Apache under systemd you can execute:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ cscli setup detect --yaml
|
||||||
|
setup:
|
||||||
|
- detected_service: apache2-systemd-deb
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
- detected_service: linux
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/linux
|
||||||
|
datasource:
|
||||||
|
filenames:
|
||||||
|
- /var/log/syslog
|
||||||
|
- /var/log/kern.log
|
||||||
|
- /var/log/messages
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
- detected_service: whitelists
|
||||||
|
install:
|
||||||
|
parsers:
|
||||||
|
- crowdsecurity/whitelists
|
||||||
|
```
|
||||||
|
|
||||||
|
The default output format is JSON, which is compatible with YAML but less readable to humans.
|
||||||
|
|
||||||
|
- `detected_service`: used to generate a name for the files written to `acquis.d`
|
||||||
|
- `install`: can contain collections, parsers, scenarios, postoverflows
|
||||||
|
- `datasource`: copied to `acquis.d`
|
||||||
|
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ cscli setup datasources --help
|
||||||
|
generate datasource (acquisition) configuration from a setup file
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
cscli setup datasources [setup_file] [flags]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for datasources
|
||||||
|
--to-dir string write the configuration to a directory, in multiple files
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
|
||||||
|
If the `--to-dir` option is not specified, a single monolithic `acquis.yaml` is printed to the standard output.
|
||||||
|
|
581
pkg/setup/detect.go
Normal file
581
pkg/setup/detect.go
Normal file
|
@ -0,0 +1,581 @@
|
||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
"github.com/antonmedv/expr"
|
||||||
|
"github.com/blackfireio/osinfo"
|
||||||
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
// goccyyaml "github.com/goccy/go-yaml"
|
||||||
|
|
||||||
|
// "github.com/k0kubun/pp"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/acquisition"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecCommand can be replaced with a mock during tests.
|
||||||
|
var ExecCommand = exec.Command
|
||||||
|
|
||||||
|
// HubItems contains the objects that are recommended to support a service.
|
||||||
|
type HubItems struct {
|
||||||
|
Collections []string `yaml:"collections,omitempty"`
|
||||||
|
Parsers []string `yaml:"parsers,omitempty"`
|
||||||
|
Scenarios []string `yaml:"scenarios,omitempty"`
|
||||||
|
PostOverflows []string `yaml:"postoverflows,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataSourceItem map[string]interface{}
|
||||||
|
|
||||||
|
// ServiceSetup describes the recommendations (hub objects and datasources) for a detected service.
|
||||||
|
type ServiceSetup struct {
|
||||||
|
DetectedService string `yaml:"detected_service"`
|
||||||
|
Install *HubItems `yaml:"install,omitempty"`
|
||||||
|
DataSource DataSourceItem `yaml:"datasource,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup is a container for a list of ServiceSetup objects, allowing for future extensions.
|
||||||
|
type Setup struct {
|
||||||
|
Setup []ServiceSetup `yaml:"setup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDataSource(opaqueDS DataSourceItem) error {
|
||||||
|
if len(opaqueDS) == 0 {
|
||||||
|
// empty datasource is valid
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// formally validate YAML
|
||||||
|
|
||||||
|
commonDS := configuration.DataSourceCommonCfg{}
|
||||||
|
body, err := yaml.Marshal(opaqueDS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(body, &commonDS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// source is mandatory // XXX unless it's not?
|
||||||
|
|
||||||
|
if commonDS.Source == "" {
|
||||||
|
return fmt.Errorf("source is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// source must be known
|
||||||
|
|
||||||
|
ds := acquisition.GetDataSourceIface(commonDS.Source)
|
||||||
|
if ds == nil {
|
||||||
|
return fmt.Errorf("unknown source '%s'", commonDS.Source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal and validate the rest with the specific implementation
|
||||||
|
|
||||||
|
err = ds.UnmarshalConfig(body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// pp.Println(ds)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDetectConfig(file string) (DetectConfig, error) {
|
||||||
|
var dc DetectConfig
|
||||||
|
|
||||||
|
yamlBytes, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return DetectConfig{}, fmt.Errorf("while reading file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dec := yaml.NewDecoder(bytes.NewBuffer(yamlBytes))
|
||||||
|
dec.KnownFields(true)
|
||||||
|
|
||||||
|
if err = dec.Decode(&dc); err != nil {
|
||||||
|
return DetectConfig{}, fmt.Errorf("while parsing %s: %w", file, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch dc.Version {
|
||||||
|
case "":
|
||||||
|
return DetectConfig{}, fmt.Errorf("missing version tag (must be 1.0)")
|
||||||
|
case "1.0":
|
||||||
|
// all is well
|
||||||
|
default:
|
||||||
|
return DetectConfig{}, fmt.Errorf("unsupported version tag '%s' (must be 1.0)", dc.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, svc := range dc.Detect {
|
||||||
|
err = validateDataSource(svc.DataSource)
|
||||||
|
if err != nil {
|
||||||
|
return DetectConfig{}, fmt.Errorf("invalid datasource for %s: %w", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service describes the rules for detecting a service and its recommended items.
|
||||||
|
type Service struct {
|
||||||
|
When []string `yaml:"when"`
|
||||||
|
Install *HubItems `yaml:"install,omitempty"`
|
||||||
|
DataSource DataSourceItem `yaml:"datasource,omitempty"`
|
||||||
|
// AcquisYAML []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectConfig is the container of all detection rules (detect.yaml).
|
||||||
|
type DetectConfig struct {
|
||||||
|
Version string `yaml:"version"`
|
||||||
|
Detect map[string]Service `yaml:"detect"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExprState keeps a global state for the duration of the service detection (cache etc.)
|
||||||
|
type ExprState struct {
|
||||||
|
unitsSearched map[string]bool
|
||||||
|
detectOptions DetectOptions
|
||||||
|
|
||||||
|
// cache
|
||||||
|
installedUnits map[string]bool
|
||||||
|
// true if the list of running processes has already been retrieved, we can
|
||||||
|
// avoid getting it a second time.
|
||||||
|
processesSearched map[string]bool
|
||||||
|
// cache
|
||||||
|
runningProcesses map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExprServiceState keep a local state during the detection of a single service. It is reset before each service rules' evaluation.
|
||||||
|
type ExprServiceState struct {
|
||||||
|
detectedUnits []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExprOS contains the detected (or forced) OS fields available to the rule engine.
|
||||||
|
type ExprOS struct {
|
||||||
|
Family string
|
||||||
|
ID string
|
||||||
|
RawVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not required with Masterminds/semver
|
||||||
|
/*
|
||||||
|
// normalizeVersion strips leading zeroes from each part, to allow comparison of ubuntu-like versions.
|
||||||
|
func normalizeVersion(version string) string {
|
||||||
|
// if it doesn't match a version string, return unchanged
|
||||||
|
if ok := regexp.MustCompile(`^(\d+)(\.\d+)?(\.\d+)?$`).MatchString(version); !ok {
|
||||||
|
// definitely not an ubuntu-like version, return unchanged
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := []rune{}
|
||||||
|
|
||||||
|
var cur rune
|
||||||
|
|
||||||
|
trim := true
|
||||||
|
for _, next := range version + "." {
|
||||||
|
if trim && cur == '0' && next != '.' {
|
||||||
|
cur = next
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cur != 0 {
|
||||||
|
ret = append(ret, cur)
|
||||||
|
}
|
||||||
|
|
||||||
|
trim = (cur == '.' || cur == 0)
|
||||||
|
cur = next
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(ret)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// VersionCheck returns true if the version of the OS matches the given constraint
|
||||||
|
func (os ExprOS) VersionCheck(constraint string) (bool, error) {
|
||||||
|
v, err := semver.NewVersion(os.RawVersion)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := semver.NewConstraint(constraint)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Check(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionAtLeast returns true if the version of the OS is at least the given version.
|
||||||
|
func (os ExprOS) VersionAtLeast(constraint string) (bool, error) {
|
||||||
|
return os.VersionCheck(">=" + constraint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionIsLower returns true if the version of the OS is lower than the given version.
|
||||||
|
func (os ExprOS) VersionIsLower(version string) (bool, error) {
|
||||||
|
result, err := os.VersionAtLeast(version)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return !result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExprEnvironment is used to expose functions and values to the rule engine.
|
||||||
|
// It can cache the results of service detection commands, like systemctl etc.
|
||||||
|
type ExprEnvironment struct {
|
||||||
|
OS ExprOS
|
||||||
|
|
||||||
|
_serviceState *ExprServiceState
|
||||||
|
_state *ExprState
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExprEnvironment creates an environment object for the rule engine.
|
||||||
|
func NewExprEnvironment(opts DetectOptions, os ExprOS) ExprEnvironment {
|
||||||
|
return ExprEnvironment{
|
||||||
|
_state: &ExprState{
|
||||||
|
detectOptions: opts,
|
||||||
|
|
||||||
|
unitsSearched: make(map[string]bool),
|
||||||
|
installedUnits: make(map[string]bool),
|
||||||
|
|
||||||
|
processesSearched: make(map[string]bool),
|
||||||
|
runningProcesses: make(map[string]bool),
|
||||||
|
},
|
||||||
|
_serviceState: &ExprServiceState{},
|
||||||
|
OS: os,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathExists returns true if the given path exists.
|
||||||
|
func (e ExprEnvironment) PathExists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnitFound returns true if the unit is listed in the systemctl output.
|
||||||
|
// Whether a disabled or failed unit is considered found or not, depends on the
|
||||||
|
// systemctl parameters used.
|
||||||
|
func (e ExprEnvironment) UnitFound(unitName string) (bool, error) {
|
||||||
|
// fill initial caches
|
||||||
|
if len(e._state.unitsSearched) == 0 {
|
||||||
|
if !e._state.detectOptions.SnubSystemd {
|
||||||
|
units, err := systemdUnitList()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range units {
|
||||||
|
e._state.installedUnits[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range e._state.detectOptions.ForcedUnits {
|
||||||
|
e._state.installedUnits[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e._state.unitsSearched[unitName] = true
|
||||||
|
if e._state.installedUnits[unitName] {
|
||||||
|
e._serviceState.detectedUnits = append(e._serviceState.detectedUnits, unitName)
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessRunning returns true if there is a running process with the given name.
|
||||||
|
func (e ExprEnvironment) ProcessRunning(processName string) (bool, error) {
|
||||||
|
if len(e._state.processesSearched) == 0 {
|
||||||
|
procs, err := process.Processes()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("while looking up running processes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range procs {
|
||||||
|
name, err := p.Name()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("while looking up running processes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e._state.runningProcesses[name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range e._state.detectOptions.ForcedProcesses {
|
||||||
|
e._state.runningProcesses[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e._state.processesSearched[processName] = true
|
||||||
|
|
||||||
|
return e._state.runningProcesses[processName], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyRules checks if the 'when' expressions are true and returns a Service struct,
|
||||||
|
// augmented with default values and anything that might be useful later on
|
||||||
|
//
|
||||||
|
// All expressions are evaluated (no short-circuit) because we want to know if there are errors.
|
||||||
|
func applyRules(svc Service, env ExprEnvironment) (Service, bool, error) {
|
||||||
|
newsvc := svc
|
||||||
|
svcok := true
|
||||||
|
env._serviceState = &ExprServiceState{}
|
||||||
|
|
||||||
|
for _, rule := range svc.When {
|
||||||
|
out, err := expr.Eval(rule, env)
|
||||||
|
log.Tracef(" Rule '%s' -> %t, %v", rule, out, err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Service{}, false, fmt.Errorf("rule '%s': %w", rule, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outbool, ok := out.(bool)
|
||||||
|
if !ok {
|
||||||
|
return Service{}, false, fmt.Errorf("rule '%s': type must be a boolean", rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
svcok = svcok && outbool
|
||||||
|
}
|
||||||
|
|
||||||
|
// if newsvc.Acquis == nil || (newsvc.Acquis.LogFiles == nil && newsvc.Acquis.JournalCTLFilter == nil) {
|
||||||
|
// for _, unitName := range env._serviceState.detectedUnits {
|
||||||
|
// if newsvc.Acquis == nil {
|
||||||
|
// newsvc.Acquis = &AcquisItem{}
|
||||||
|
// }
|
||||||
|
// // if there is reference to more than one unit in the rules, we use the first one
|
||||||
|
// newsvc.Acquis.JournalCTLFilter = []string{fmt.Sprintf(`_SYSTEMD_UNIT=%s`, unitName)}
|
||||||
|
// break //nolint // we want to exit after one iteration
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return newsvc, svcok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterWithRules decorates a DetectConfig map by filtering according to the when: clauses,
|
||||||
|
// and applying default values or whatever useful to the Service items.
|
||||||
|
func filterWithRules(dc DetectConfig, env ExprEnvironment) (map[string]Service, error) {
|
||||||
|
ret := make(map[string]Service)
|
||||||
|
|
||||||
|
for name := range dc.Detect {
|
||||||
|
//
|
||||||
|
// an empty list of when: clauses defaults to true, if we want
|
||||||
|
// to change this behavior, the place is here.
|
||||||
|
// if len(svc.When) == 0 {
|
||||||
|
// log.Warningf("empty 'when' clause: %+v", svc)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
log.Trace("Evaluating rules for: ", name)
|
||||||
|
|
||||||
|
svc, ok, err := applyRules(dc.Detect[name], env)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("while looking for service %s: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Tracef(" Skipping %s", name)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef(" Detected %s", name)
|
||||||
|
|
||||||
|
ret[name] = svc
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// return units that have been forced but not searched yet.
|
||||||
|
func (e ExprEnvironment) unsearchedUnits() []string {
|
||||||
|
ret := []string{}
|
||||||
|
|
||||||
|
for _, unit := range e._state.detectOptions.ForcedUnits {
|
||||||
|
if !e._state.unitsSearched[unit] {
|
||||||
|
ret = append(ret, unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// return processes that have been forced but not searched yet.
|
||||||
|
func (e ExprEnvironment) unsearchedProcesses() []string {
|
||||||
|
ret := []string{}
|
||||||
|
|
||||||
|
for _, proc := range e._state.detectOptions.ForcedProcesses {
|
||||||
|
if !e._state.processesSearched[proc] {
|
||||||
|
ret = append(ret, proc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkConsumedForcedItems checks if all the "forced" options (units or processes) have been evaluated during the service detection.
|
||||||
|
func checkConsumedForcedItems(e ExprEnvironment) error {
|
||||||
|
unconsumed := e.unsearchedUnits()
|
||||||
|
|
||||||
|
unitMsg := ""
|
||||||
|
if len(unconsumed) > 0 {
|
||||||
|
unitMsg = fmt.Sprintf("unit(s) forced but not supported: %v", unconsumed)
|
||||||
|
}
|
||||||
|
|
||||||
|
unconsumed = e.unsearchedProcesses()
|
||||||
|
|
||||||
|
procsMsg := ""
|
||||||
|
if len(unconsumed) > 0 {
|
||||||
|
procsMsg = fmt.Sprintf("process(es) forced but not supported: %v", unconsumed)
|
||||||
|
}
|
||||||
|
|
||||||
|
join := ""
|
||||||
|
if unitMsg != "" && procsMsg != "" {
|
||||||
|
join = "; "
|
||||||
|
}
|
||||||
|
|
||||||
|
if unitMsg != "" || procsMsg != "" {
|
||||||
|
return fmt.Errorf("%s%s%s", unitMsg, join, procsMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectOptions contains parameters for the Detect function.
|
||||||
|
type DetectOptions struct {
|
||||||
|
// slice of unit names that we want to force-detect
|
||||||
|
ForcedUnits []string
|
||||||
|
// slice of process names that we want to force-detect
|
||||||
|
ForcedProcesses []string
|
||||||
|
ForcedOS ExprOS
|
||||||
|
SkipServices []string
|
||||||
|
SnubSystemd bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect performs the service detection from a given configuration.
|
||||||
|
// It outputs a setup file that can be used as input to "cscli setup install-hub"
|
||||||
|
// or "cscli setup datasources".
|
||||||
|
func Detect(serviceDetectionFile string, opts DetectOptions) (Setup, error) {
|
||||||
|
ret := Setup{}
|
||||||
|
|
||||||
|
// explicitly initialize to avoid json mashaling an empty slice as "null"
|
||||||
|
ret.Setup = make([]ServiceSetup, 0)
|
||||||
|
|
||||||
|
log.Tracef("Reading detection rules: %s", serviceDetectionFile)
|
||||||
|
|
||||||
|
sc, err := readDetectConfig(serviceDetectionFile)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// // generate acquis.yaml snippet for this service
|
||||||
|
// for key := range sc.Detect {
|
||||||
|
// svc := sc.Detect[key]
|
||||||
|
// if svc.Acquis != nil {
|
||||||
|
// svc.AcquisYAML, err = yaml.Marshal(svc.Acquis)
|
||||||
|
// if err != nil {
|
||||||
|
// return ret, err
|
||||||
|
// }
|
||||||
|
// sc.Detect[key] = svc
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
var osfull *osinfo.OSInfo
|
||||||
|
|
||||||
|
os := opts.ForcedOS
|
||||||
|
if os == (ExprOS{}) {
|
||||||
|
osfull, err = osinfo.GetOSInfo()
|
||||||
|
if err != nil {
|
||||||
|
return ret, fmt.Errorf("detecting OS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef("Detected OS - %+v", *osfull)
|
||||||
|
|
||||||
|
os = ExprOS{
|
||||||
|
Family: osfull.Family,
|
||||||
|
ID: osfull.ID,
|
||||||
|
RawVersion: osfull.Version,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Tracef("Forced OS - %+v", os)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.ForcedUnits) > 0 {
|
||||||
|
log.Tracef("Forced units - %v", opts.ForcedUnits)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.ForcedProcesses) > 0 {
|
||||||
|
log.Tracef("Forced processes - %v", opts.ForcedProcesses)
|
||||||
|
}
|
||||||
|
|
||||||
|
env := NewExprEnvironment(opts, os)
|
||||||
|
|
||||||
|
detected, err := filterWithRules(sc, env)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = checkConsumedForcedItems(env); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove services the user asked to ignore
|
||||||
|
for _, name := range opts.SkipServices {
|
||||||
|
delete(detected, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the keys (service names) to have them in a predictable
|
||||||
|
// order in the final output
|
||||||
|
|
||||||
|
keys := make([]string, 0)
|
||||||
|
for k := range detected {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, name := range keys {
|
||||||
|
svc := detected[name]
|
||||||
|
// if svc.DataSource != nil {
|
||||||
|
// if svc.DataSource.Labels["type"] == "" {
|
||||||
|
// return Setup{}, fmt.Errorf("missing type label for service %s", name)
|
||||||
|
// }
|
||||||
|
// err = yaml.Unmarshal(svc.AcquisYAML, svc.DataSource)
|
||||||
|
// if err != nil {
|
||||||
|
// return Setup{}, fmt.Errorf("while unmarshaling datasource for service %s: %w", name, err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
ret.Setup = append(ret.Setup, ServiceSetup{
|
||||||
|
DetectedService: name,
|
||||||
|
Install: svc.Install,
|
||||||
|
DataSource: svc.DataSource,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSupported parses the configuration file and outputs a list of the supported services.
|
||||||
|
func ListSupported(serviceDetectionFile string) ([]string, error) {
|
||||||
|
dc, err := readDetectConfig(serviceDetectionFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, 0)
|
||||||
|
for k := range dc.Detect {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
return keys, nil
|
||||||
|
}
|
1017
pkg/setup/detect_test.go
Normal file
1017
pkg/setup/detect_test.go
Normal file
File diff suppressed because it is too large
Load diff
9
pkg/setup/export_test.go
Normal file
9
pkg/setup/export_test.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package setup
|
||||||
|
|
||||||
|
var (
|
||||||
|
SystemdUnitList = systemdUnitList
|
||||||
|
FilterWithRules = filterWithRules
|
||||||
|
ApplyRules = applyRules
|
||||||
|
|
||||||
|
// NormalizeVersion = normalizeVersion
|
||||||
|
)
|
255
pkg/setup/install.go
Normal file
255
pkg/setup/install.go
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
goccyyaml "github.com/goccy/go-yaml"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AcquisDocument is created from a SetupItem. It represents a single YAML document, and can be part of a multi-document file.
|
||||||
|
type AcquisDocument struct {
|
||||||
|
AcquisFilename string
|
||||||
|
DataSource map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSetup(input []byte, fancyErrors bool) (Setup, error) {
|
||||||
|
ret := Setup{}
|
||||||
|
|
||||||
|
// parse with goccy to have better error messages in many cases
|
||||||
|
dec := goccyyaml.NewDecoder(bytes.NewBuffer(input), goccyyaml.Strict())
|
||||||
|
|
||||||
|
if err := dec.Decode(&ret); err != nil {
|
||||||
|
if fancyErrors {
|
||||||
|
return ret, fmt.Errorf("%v", goccyyaml.FormatError(err, true, true))
|
||||||
|
}
|
||||||
|
// XXX errors here are multiline, should we just print them to stderr instead of logging?
|
||||||
|
return ret, fmt.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse again because goccy is not strict enough anyway
|
||||||
|
dec2 := yaml.NewDecoder(bytes.NewBuffer(input))
|
||||||
|
dec2.KnownFields(true)
|
||||||
|
|
||||||
|
if err := dec2.Decode(&ret); err != nil {
|
||||||
|
return ret, fmt.Errorf("while unmarshaling setup file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallHubItems installs the objects recommended in a setup file.
|
||||||
|
func InstallHubItems(csConfig *csconfig.Config, input []byte, dryRun bool) error {
|
||||||
|
setupEnvelope, err := decodeSetup(input, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := csConfig.LoadHub(); err != nil {
|
||||||
|
return fmt.Errorf("loading hub: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cwhub.SetHubBranch(); err != nil {
|
||||||
|
return fmt.Errorf("setting hub branch: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
|
||||||
|
return fmt.Errorf("getting hub index: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, setupItem := range setupEnvelope.Setup {
|
||||||
|
forceAction := false
|
||||||
|
downloadOnly := false
|
||||||
|
install := setupItem.Install
|
||||||
|
|
||||||
|
if install == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(install.Collections) > 0 {
|
||||||
|
for _, collection := range setupItem.Install.Collections {
|
||||||
|
if dryRun {
|
||||||
|
fmt.Println("dry-run: would install collection", collection)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cwhub.InstallItem(csConfig, collection, cwhub.COLLECTIONS, forceAction, downloadOnly); err != nil {
|
||||||
|
return fmt.Errorf("while installing collection %s: %w", collection, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(install.Parsers) > 0 {
|
||||||
|
for _, parser := range setupItem.Install.Parsers {
|
||||||
|
if dryRun {
|
||||||
|
fmt.Println("dry-run: would install parser", parser)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cwhub.InstallItem(csConfig, parser, cwhub.PARSERS, forceAction, downloadOnly); err != nil {
|
||||||
|
return fmt.Errorf("while installing parser %s: %w", parser, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(install.Scenarios) > 0 {
|
||||||
|
for _, scenario := range setupItem.Install.Scenarios {
|
||||||
|
if dryRun {
|
||||||
|
fmt.Println("dry-run: would install scenario", scenario)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cwhub.InstallItem(csConfig, scenario, cwhub.SCENARIOS, forceAction, downloadOnly); err != nil {
|
||||||
|
return fmt.Errorf("while installing scenario %s: %w", scenario, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(install.PostOverflows) > 0 {
|
||||||
|
for _, postoverflow := range setupItem.Install.PostOverflows {
|
||||||
|
if dryRun {
|
||||||
|
fmt.Println("dry-run: would install postoverflow", postoverflow)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cwhub.InstallItem(csConfig, postoverflow, cwhub.PARSERS_OVFLW, forceAction, downloadOnly); err != nil {
|
||||||
|
return fmt.Errorf("while installing postoverflow %s: %w", postoverflow, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalAcquisDocuments creates the monolithic file, or itemized files (if a directory is provided) with the acquisition documents.
|
||||||
|
func marshalAcquisDocuments(ads []AcquisDocument, toDir string) (string, error) {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
dashTerminator := false
|
||||||
|
|
||||||
|
disclaimer := `
|
||||||
|
#
|
||||||
|
# This file was automatically generated by "cscli setup datasources".
|
||||||
|
# You can modify it by hand, but will be responsible for its maintenance.
|
||||||
|
# To add datasources or logfiles, you can instead write a new configuration
|
||||||
|
# in the directory defined by acquisition_dir.
|
||||||
|
#
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
if toDir == "" {
|
||||||
|
sb.WriteString(disclaimer)
|
||||||
|
} else {
|
||||||
|
_, err := os.Stat(toDir)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", fmt.Errorf("directory %s does not exist", toDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ad := range ads {
|
||||||
|
out, err := goccyyaml.MarshalWithOptions(ad.DataSource, goccyyaml.IndentSequence(true))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("while encoding datasource: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if toDir != "" {
|
||||||
|
if ad.AcquisFilename == "" {
|
||||||
|
return "", fmt.Errorf("empty acquis filename")
|
||||||
|
}
|
||||||
|
|
||||||
|
fname := filepath.Join(toDir, ad.AcquisFilename)
|
||||||
|
fmt.Println("creating", fname)
|
||||||
|
|
||||||
|
f, err := os.Create(fname)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("creating acquisition file: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = f.WriteString(disclaimer)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("while writing to %s: %w", ad.AcquisFilename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Write(out)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("while writing to %s: %w", ad.AcquisFilename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Sync()
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if dashTerminator {
|
||||||
|
sb.WriteString("---\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Write(out)
|
||||||
|
|
||||||
|
dashTerminator = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks the validity of a setup file.
|
||||||
|
func Validate(input []byte) error {
|
||||||
|
_, err := decodeSetup(input, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataSources generates the acquisition documents from a setup file.
|
||||||
|
func DataSources(input []byte, toDir string) (string, error) {
|
||||||
|
setupEnvelope, err := decodeSetup(input, false)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ads := make([]AcquisDocument, 0)
|
||||||
|
|
||||||
|
filename := func(basename string, ext string) string {
|
||||||
|
if basename == "" {
|
||||||
|
return basename
|
||||||
|
}
|
||||||
|
|
||||||
|
return basename + ext
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, setupItem := range setupEnvelope.Setup {
|
||||||
|
datasource := setupItem.DataSource
|
||||||
|
|
||||||
|
basename := ""
|
||||||
|
if toDir != "" {
|
||||||
|
basename = "setup." + setupItem.DetectedService
|
||||||
|
}
|
||||||
|
|
||||||
|
if datasource == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ad := AcquisDocument{
|
||||||
|
AcquisFilename: filename(basename, ".yaml"),
|
||||||
|
DataSource: datasource,
|
||||||
|
}
|
||||||
|
ads = append(ads, ad)
|
||||||
|
}
|
||||||
|
|
||||||
|
return marshalAcquisDocuments(ads, toDir)
|
||||||
|
}
|
59
pkg/setup/units.go
Normal file
59
pkg/setup/units.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// systemdUnitList returns all enabled systemd units.
|
||||||
|
// It needs to parse the table because -o json does not work everywhere.
|
||||||
|
func systemdUnitList() ([]string, error) {
|
||||||
|
wrap := func(err error) error {
|
||||||
|
return fmt.Errorf("running systemctl: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]string, 0)
|
||||||
|
cmd := ExecCommand("systemctl", "list-unit-files", "--state=enabled,generated,static")
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return ret, wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Running systemctl...")
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return ret, wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(stdout)
|
||||||
|
header := true // skip the first line
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if len(line) == 0 {
|
||||||
|
break // the rest of the output is footer
|
||||||
|
}
|
||||||
|
|
||||||
|
if !header {
|
||||||
|
spaceIdx := strings.IndexRune(line, ' ')
|
||||||
|
if spaceIdx == -1 {
|
||||||
|
return ret, fmt.Errorf("can't parse systemctl output")
|
||||||
|
}
|
||||||
|
|
||||||
|
line = line[:spaceIdx]
|
||||||
|
ret = append(ret, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
header = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
return ret, wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
32
pkg/setup/units_test.go
Normal file
32
pkg/setup/units_test.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package setup_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/setup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSystemdUnitList(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
setup.ExecCommand = fakeExecCommand
|
||||||
|
|
||||||
|
defer func() { setup.ExecCommand = exec.Command }()
|
||||||
|
|
||||||
|
units, err := setup.SystemdUnitList() //nolint:typecheck,nolintlint // exported only for tests
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
require.Equal([]string{
|
||||||
|
"crowdsec-setup-detect.service",
|
||||||
|
"apache2.service",
|
||||||
|
"apparmor.service",
|
||||||
|
"apport.service",
|
||||||
|
"atop.service",
|
||||||
|
"atopacct.service",
|
||||||
|
"finalrd.service",
|
||||||
|
"fwupd-refresh.service",
|
||||||
|
"fwupd.service",
|
||||||
|
}, units)
|
||||||
|
}
|
816
tests/bats/07_setup.bats
Normal file
816
tests/bats/07_setup.bats
Normal file
|
@ -0,0 +1,816 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
# vim: ft=bats:list:ts=8:sts=4:sw=4:et:ai:si:
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
setup_file() {
|
||||||
|
load "../lib/setup_file.sh"
|
||||||
|
./instance-data load
|
||||||
|
HUB_DIR=$(config_get '.config_paths.hub_dir')
|
||||||
|
export HUB_DIR
|
||||||
|
DETECT_YAML="${HUB_DIR}/detect.yaml"
|
||||||
|
export DETECT_YAML
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
TESTDATA="${BATS_TEST_DIRNAME}/testdata/07_setup"
|
||||||
|
export TESTDATA
|
||||||
|
|
||||||
|
export CROWDSEC_FEATURE_CSCLI_SETUP="true"
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file() {
|
||||||
|
load "../lib/teardown_file.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
load "../lib/setup.sh"
|
||||||
|
load "../lib/bats-file/load.bash"
|
||||||
|
load "../lib/bats-mock/load.bash"
|
||||||
|
./instance-data load
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
./instance-crowdsec stop
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------
|
||||||
|
|
||||||
|
#shellcheck disable=SC2154
|
||||||
|
@test "cscli setup" {
|
||||||
|
rune -0 cscli help
|
||||||
|
assert_line --regexp '^ +setup +Tools to configure crowdsec$'
|
||||||
|
|
||||||
|
rune -0 cscli setup --help
|
||||||
|
assert_line 'Usage:'
|
||||||
|
assert_line ' cscli setup [command]'
|
||||||
|
assert_line 'Manage hub configuration and service detection'
|
||||||
|
assert_line --partial "detect detect running services, generate a setup file"
|
||||||
|
assert_line --partial "datasources generate datasource (acquisition) configuration from a setup file"
|
||||||
|
assert_line --partial "install-hub install items from a setup file"
|
||||||
|
assert_line --partial "validate validate a setup file"
|
||||||
|
|
||||||
|
# cobra should return error for non-existing sub-subcommands, but doesn't
|
||||||
|
rune -0 cscli setup blahblah
|
||||||
|
assert_line 'Usage:'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect --help; --detect-config" {
|
||||||
|
rune -0 cscli setup detect --help
|
||||||
|
assert_line --regexp "detect running services, generate a setup file"
|
||||||
|
assert_line 'Usage:'
|
||||||
|
assert_line ' cscli setup detect [flags]'
|
||||||
|
assert_line --partial "--detect-config string path to service detection configuration (default \"${HUB_DIR}/detect.yaml\")"
|
||||||
|
assert_line --partial "--force-process strings force detection of a running process (can be repeated)"
|
||||||
|
assert_line --partial "--force-unit strings force detection of a systemd unit (can be repeated)"
|
||||||
|
assert_line --partial "--list-supported-services do not detect; only print supported services"
|
||||||
|
assert_line --partial "--force-os-family string override OS.Family: one of linux, freebsd, windows or darwin"
|
||||||
|
assert_line --partial "--force-os-id string override OS.ID=[debian | ubuntu | , redhat...]"
|
||||||
|
assert_line --partial "--force-os-version string override OS.RawVersion (of OS or Linux distribution)"
|
||||||
|
assert_line --partial "--skip-service strings ignore a service, don't recommend hub/datasources (can be repeated)"
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup detect --detect-config /path/does/not/exist
|
||||||
|
assert_stderr --partial "detecting services: while reading file: open /path/does/not/exist: no such file or directory"
|
||||||
|
|
||||||
|
# rm -f "${HUB_DIR}/detect.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (linux), --skip-service" {
|
||||||
|
[[ ${OSTYPE} =~ linux.* ]] || skip
|
||||||
|
tempfile=$(TMPDIR="$BATS_TEST_TMPDIR" mktemp)
|
||||||
|
cat <<-EOT >"${tempfile}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
linux:
|
||||||
|
when:
|
||||||
|
- OS.Family == "linux"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/linux
|
||||||
|
thewiz:
|
||||||
|
when:
|
||||||
|
- OS.Family != "linux"
|
||||||
|
foobarbaz:
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --detect-config "$tempfile"
|
||||||
|
assert_json '{setup:[{detected_service:"foobarbaz"},{detected_service:"linux",install:{collections:["crowdsecurity/linux"]}}]}'
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --detect-config "$tempfile" --skip-service linux
|
||||||
|
assert_json '{setup:[{detected_service:"foobarbaz"}]}'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect --force-os-*" {
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-os-family linux --detect-config "${TESTDATA}/detect.yaml"
|
||||||
|
rune -0 jq -cS '.setup[] | select(.detected_service=="linux")' <(output)
|
||||||
|
assert_json '{detected_service:"linux",install:{collections:["crowdsecurity/linux"]},datasource:{source:"file",labels:{type:"syslog"},filenames:["/var/log/syslog","/var/log/kern.log","/var/log/messages"]}}'
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-os-family freebsd --detect-config "${TESTDATA}/detect.yaml"
|
||||||
|
rune -0 jq -cS '.setup[] | select(.detected_service=="freebsd")' <(output)
|
||||||
|
assert_json '{detected_service:"freebsd",install:{collections:["crowdsecurity/freebsd"]}}'
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-os-family windows --detect-config "${TESTDATA}/detect.yaml"
|
||||||
|
rune -0 jq -cS '.setup[] | select(.detected_service=="windows")' <(output)
|
||||||
|
assert_json '{detected_service:"windows",install:{collections:["crowdsecurity/windows"]}}'
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-os-family darwin --detect-config "${TESTDATA}/detect.yaml"
|
||||||
|
|
||||||
|
# XXX do we want do disallow unknown family?
|
||||||
|
# assert_stderr --partial "detecting services: OS 'darwin' not supported"
|
||||||
|
|
||||||
|
# XXX TODO force-os-id, force-os-version
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect --list-supported-services" {
|
||||||
|
tempfile=$(TMPDIR="$BATS_TEST_TMPDIR" mktemp)
|
||||||
|
cat <<-EOT >"${tempfile}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
thewiz:
|
||||||
|
foobarbaz:
|
||||||
|
apache2:
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --list-supported-services --detect-config "$tempfile"
|
||||||
|
# the service list is sorted
|
||||||
|
assert_output - <<-EOT
|
||||||
|
apache2
|
||||||
|
foobarbaz
|
||||||
|
thewiz
|
||||||
|
EOT
|
||||||
|
|
||||||
|
cat <<-EOT >"${tempfile}"
|
||||||
|
thisisajoke
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup detect --list-supported-services --detect-config "$tempfile"
|
||||||
|
assert_stderr --partial "while parsing ${tempfile}: yaml: unmarshal errors:"
|
||||||
|
|
||||||
|
rm -f "$tempfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (systemctl)" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- UnitFound("mock-apache2.service")
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
filename: dummy.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
EOT
|
||||||
|
|
||||||
|
# transparently mock systemctl. It's easier if you can tell the application
|
||||||
|
# under test which executable to call (in which case just call $mock) but
|
||||||
|
# here we do the symlink and $PATH dance as an example
|
||||||
|
mocked_command="systemctl"
|
||||||
|
|
||||||
|
# mock setup
|
||||||
|
mock="$(mock_create)"
|
||||||
|
mock_path="${mock%/*}"
|
||||||
|
mock_file="${mock##*/}"
|
||||||
|
ln -sf "${mock_path}/${mock_file}" "${mock_path}/${mocked_command}"
|
||||||
|
|
||||||
|
#shellcheck disable=SC2030
|
||||||
|
PATH="${mock_path}:${PATH}"
|
||||||
|
|
||||||
|
mock_set_output "$mock" \
|
||||||
|
'UNIT FILE STATE VENDOR PRESET
|
||||||
|
snap-bare-5.mount enabled enabled
|
||||||
|
snap-core-13308.mount enabled enabled
|
||||||
|
snap-firefox-1635.mount enabled enabled
|
||||||
|
snap-fx-158.mount enabled enabled
|
||||||
|
snap-gimp-393.mount enabled enabled
|
||||||
|
snap-gtk\x2dcommon\x2dthemes-1535.mount enabled enabled
|
||||||
|
snap-kubectl-2537.mount enabled enabled
|
||||||
|
snap-rustup-1027.mount enabled enabled
|
||||||
|
cups.path enabled enabled
|
||||||
|
console-setup.service enabled enabled
|
||||||
|
dmesg.service enabled enabled
|
||||||
|
getty@.service enabled enabled
|
||||||
|
grub-initrd-fallback.service enabled enabled
|
||||||
|
irqbalance.service enabled enabled
|
||||||
|
keyboard-setup.service enabled enabled
|
||||||
|
mock-apache2.service enabled enabled
|
||||||
|
networkd-dispatcher.service enabled enabled
|
||||||
|
ua-timer.timer enabled enabled
|
||||||
|
update-notifier-download.timer enabled enabled
|
||||||
|
update-notifier-motd.timer enabled enabled
|
||||||
|
|
||||||
|
20 unit files listed.'
|
||||||
|
mock_set_status "$mock" 1 2
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect
|
||||||
|
rune -0 jq -c '.setup' <(output)
|
||||||
|
|
||||||
|
# If a call to UnitFoundwas part of the expression and it returned true,
|
||||||
|
# there is a default journalctl_filter derived from the unit's name.
|
||||||
|
assert_json '[{datasource:{source:"file",filename:"dummy.log",labels:{type:"apache2"}},detected_service:"apache2"}]'
|
||||||
|
|
||||||
|
# the command was called exactly once
|
||||||
|
[[ $(mock_get_call_num "$mock") -eq 1 ]]
|
||||||
|
|
||||||
|
# the command was called with the expected parameters
|
||||||
|
[[ $(mock_get_call_args "$mock" 1) == "list-unit-files --state=enabled,generated,static" ]]
|
||||||
|
|
||||||
|
rune -1 systemctl
|
||||||
|
|
||||||
|
# mock teardown
|
||||||
|
unlink "${mock_path}/${mocked_command}"
|
||||||
|
PATH="${PATH/${mock_path}:/}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# XXX this is the same boilerplate as the previous test, can be simplified
|
||||||
|
@test "cscli setup detect (snub systemd)" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- UnitFound("mock-apache2.service")
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
filename: dummy.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
EOT
|
||||||
|
|
||||||
|
# transparently mock systemctl. It's easier if you can tell the application
|
||||||
|
# under test which executable to call (in which case just call $mock) but
|
||||||
|
# here we do the symlink and $PATH dance as an example
|
||||||
|
mocked_command="systemctl"
|
||||||
|
|
||||||
|
# mock setup
|
||||||
|
mock="$(mock_create)"
|
||||||
|
mock_path="${mock%/*}"
|
||||||
|
mock_file="${mock##*/}"
|
||||||
|
ln -sf "${mock_path}/${mock_file}" "${mock_path}/${mocked_command}"
|
||||||
|
|
||||||
|
#shellcheck disable=SC2031
|
||||||
|
PATH="${mock_path}:${PATH}"
|
||||||
|
|
||||||
|
# we don't really care about the output, it's not used anyway
|
||||||
|
mock_set_output "$mock" ""
|
||||||
|
mock_set_status "$mock" 1 2
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --snub-systemd
|
||||||
|
|
||||||
|
# setup must not be 'null', but an empty list
|
||||||
|
assert_json '{setup:[]}'
|
||||||
|
|
||||||
|
# the command was never called
|
||||||
|
[[ $(mock_get_call_num "$mock") -eq 0 ]]
|
||||||
|
|
||||||
|
rune -0 systemctl
|
||||||
|
|
||||||
|
# mock teardown
|
||||||
|
unlink "${mock_path}/${mocked_command}"
|
||||||
|
PATH="${PATH/${mock_path}:/}"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect --force-unit" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- UnitFound("force-apache2")
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
filename: dummy.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
apache3:
|
||||||
|
when:
|
||||||
|
- UnitFound("force-apache3")
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
filename: dummy.log
|
||||||
|
labels:
|
||||||
|
type: apache3
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-unit force-apache2
|
||||||
|
rune -0 jq -cS '.setup' <(output)
|
||||||
|
assert_json '[{datasource:{source:"file",filename:"dummy.log",labels:{"type":"apache2"}},detected_service:"apache2"}]'
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-unit force-apache2,force-apache3
|
||||||
|
rune -0 jq -cS '.setup' <(output)
|
||||||
|
assert_json '[{datasource:{source:"file",filename:"dummy.log",labels:{type:"apache2"}},detected_service:"apache2"},{datasource:{source:"file",filename:"dummy.log",labels:{"type":"apache3"}},detected_service:"apache3"}]'
|
||||||
|
|
||||||
|
# force-unit can be specified multiple times, the order does not matter
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-unit force-apache3 --force-unit force-apache2
|
||||||
|
rune -0 jq -cS '.setup' <(output)
|
||||||
|
assert_json '[{datasource:{source:"file",filename:"dummy.log",labels:{type:"apache2"}},detected_service:"apache2"},{datasource:{source:"file",filename:"dummy.log",labels:{type:"apache3"}},detected_service:"apache3"}]'
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup detect --force-unit mock-doesnotexist
|
||||||
|
assert_stderr --partial "detecting services: unit(s) forced but not supported: [mock-doesnotexist]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (process)" {
|
||||||
|
# This is harder to mock, because gopsutil requires proc/ to be a mount
|
||||||
|
# point. So we pick a process that exists for sure.
|
||||||
|
expected_process=$(basename "$SHELL")
|
||||||
|
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("${expected_process}")
|
||||||
|
apache3:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("this-does-not-exist")
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect
|
||||||
|
rune -0 jq -cS '.setup' <(output)
|
||||||
|
assert_json '[{detected_service:"apache2"}]'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect --force-process" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("force-apache2")
|
||||||
|
apache3:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("this-does-not-exist")
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-process force-apache2
|
||||||
|
rune -0 jq -cS '.setup' <(output)
|
||||||
|
assert_json '[{detected_service:"apache2"}]'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (acquisition only, no hub items)" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- UnitFound("force-apache2")
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
filename: dummy.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-unit force-apache2
|
||||||
|
rune -0 jq -cS '.setup' <(output)
|
||||||
|
assert_json '[{datasource:{source:"file",filename:"dummy.log",labels:{type:"apache2"}},detected_service:"apache2"}]'
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-unit force-apache2 --yaml
|
||||||
|
assert_output - <<-EOT
|
||||||
|
setup:
|
||||||
|
- detected_service: apache2
|
||||||
|
datasource:
|
||||||
|
filename: dummy.log
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
source: file
|
||||||
|
EOT
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (full acquisition section)" {
|
||||||
|
skip "not supported yet"
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
foobar:
|
||||||
|
datasource:
|
||||||
|
filenames:
|
||||||
|
- /path/to/log/*.log
|
||||||
|
exclude_regexps:
|
||||||
|
- ^/path/to/log/excludeme\.log$
|
||||||
|
force_inotify: true
|
||||||
|
mode: tail
|
||||||
|
labels:
|
||||||
|
type: foolog
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 cscli setup detect --yaml
|
||||||
|
assert_output - <<-EOT
|
||||||
|
setup:
|
||||||
|
- detected_service: foobar
|
||||||
|
datasource:
|
||||||
|
filenames:
|
||||||
|
- /path/to/log/*.log
|
||||||
|
exclude_regexps:
|
||||||
|
- ^/path/to/log/excludeme.log$
|
||||||
|
force_inotify: true
|
||||||
|
mode: tail
|
||||||
|
labels:
|
||||||
|
type: foolog
|
||||||
|
EOT
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect + acquis + install (no acquisition, no hub items)" {
|
||||||
|
# no-op edge case, to make sure we don't crash
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
always:
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect
|
||||||
|
assert_json '{setup:[{detected_service:"always"}]}'
|
||||||
|
setup=$output
|
||||||
|
rune -0 cscli setup datasources /dev/stdin <<<"$setup"
|
||||||
|
rune -0 cscli setup install-hub /dev/stdin <<<"$setup"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (with collections)" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
foobar:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("force-foobar")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/foobar
|
||||||
|
qox:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("test-qox")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/foobar
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("force-apache2")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-process force-apache2,force-foobar
|
||||||
|
rune -0 jq -Sc '.setup | sort' <(output)
|
||||||
|
assert_json '[{install:{collections:["crowdsecurity/apache2"]},detected_service:"apache2"},{install:{collections:["crowdsecurity/foobar"]},detected_service:"foobar"}]'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (with acquisition)" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
foobar:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("force-foobar")
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: foobar
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --force-process force-foobar
|
||||||
|
rune -0 yq -op '.setup | sort_keys(..)' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
0.datasource.filenames.0 = /var/log/apache2/*.log
|
||||||
|
0.datasource.filenames.1 = /var/log/*http*/*.log
|
||||||
|
0.datasource.labels.type = foobar
|
||||||
|
0.datasource.source = file
|
||||||
|
0.detected_service = foobar
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup detect --force-process mock-doesnotexist
|
||||||
|
assert_stderr --partial "detecting services: process(es) forced but not supported: [mock-doesnotexist]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup detect (datasource validation)" {
|
||||||
|
cat <<-EOT >"${DETECT_YAML}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
foobar:
|
||||||
|
datasource:
|
||||||
|
labels:
|
||||||
|
type: something
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup detect
|
||||||
|
assert_stderr --partial "detecting services: invalid datasource for foobar: source is empty"
|
||||||
|
|
||||||
|
# more datasource-specific tests are in detect_test.go
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup install-hub (dry run)" {
|
||||||
|
# it's not installed
|
||||||
|
rune -0 --separate-stderr cscli collections list -o json
|
||||||
|
rune -0 jq -r '.collections[].name' <(output)
|
||||||
|
refute_line "crowdsecurity/apache2"
|
||||||
|
|
||||||
|
# we install it
|
||||||
|
rune -0 --separate-stderr cscli setup install-hub /dev/stdin --dry-run <<< '{"setup":[{"install":{"collections":["crowdsecurity/apache2"]}}]}'
|
||||||
|
assert_output 'dry-run: would install collection crowdsecurity/apache2'
|
||||||
|
|
||||||
|
# still not installed
|
||||||
|
rune -0 --separate-stderr cscli collections list -o json
|
||||||
|
rune -0 jq -r '.collections[].name' <(output)
|
||||||
|
refute_line "crowdsecurity/apache2"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup install-hub (dry run: install multiple collections)" {
|
||||||
|
# it's not installed
|
||||||
|
rune -0 --separate-stderr cscli collections list -o json
|
||||||
|
rune -0 jq -r '.collections[].name' <(output)
|
||||||
|
refute_line "crowdsecurity/apache2"
|
||||||
|
|
||||||
|
# we install it
|
||||||
|
rune -0 --separate-stderr cscli setup install-hub /dev/stdin --dry-run <<< '{"setup":[{"install":{"collections":["crowdsecurity/apache2"]}}]}'
|
||||||
|
assert_output 'dry-run: would install collection crowdsecurity/apache2'
|
||||||
|
|
||||||
|
# still not installed
|
||||||
|
rune -0 --separate-stderr cscli collections list -o json
|
||||||
|
rune -0 jq -r '.collections[].name' <(output)
|
||||||
|
refute_line "crowdsecurity/apache2"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup install-hub (dry run: install multiple collections, parsers, scenarios, postoverflows)" {
|
||||||
|
rune -0 --separate-stderr cscli setup install-hub /dev/stdin --dry-run <<< '{"setup":[{"install":{"collections":["crowdsecurity/foo","johndoe/bar"],"parsers":["crowdsecurity/fooparser","johndoe/barparser"],"scenarios":["crowdsecurity/fooscenario","johndoe/barscenario"],"postoverflows":["crowdsecurity/foopo","johndoe/barpo"]}}]}'
|
||||||
|
assert_line 'dry-run: would install collection crowdsecurity/foo'
|
||||||
|
assert_line 'dry-run: would install collection johndoe/bar'
|
||||||
|
assert_line 'dry-run: would install parser crowdsecurity/fooparser'
|
||||||
|
assert_line 'dry-run: would install parser johndoe/barparser'
|
||||||
|
assert_line 'dry-run: would install scenario crowdsecurity/fooscenario'
|
||||||
|
assert_line 'dry-run: would install scenario johndoe/barscenario'
|
||||||
|
assert_line 'dry-run: would install postoverflow crowdsecurity/foopo'
|
||||||
|
assert_line 'dry-run: would install postoverflow johndoe/barpo'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup datasources" {
|
||||||
|
rune -0 --separate-stderr cscli setup datasources --help
|
||||||
|
assert_line --partial "--to-dir string write the configuration to a directory, in multiple files"
|
||||||
|
|
||||||
|
# single item
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup datasources /dev/stdin <<-EOT
|
||||||
|
setup:
|
||||||
|
- datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
EOT
|
||||||
|
|
||||||
|
# remove diclaimer
|
||||||
|
rune -0 yq '. head_comment=""' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
source: file
|
||||||
|
EOT
|
||||||
|
|
||||||
|
# multiple items
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup datasources /dev/stdin <<-EOT
|
||||||
|
setup:
|
||||||
|
- datasource:
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
- datasource:
|
||||||
|
labels:
|
||||||
|
type: foobar
|
||||||
|
filenames:
|
||||||
|
- /var/log/foobar/*.log
|
||||||
|
- datasource:
|
||||||
|
labels:
|
||||||
|
type: barbaz
|
||||||
|
filenames:
|
||||||
|
- /path/to/barbaz.log
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 yq '. head_comment=""' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
---
|
||||||
|
filenames:
|
||||||
|
- /var/log/foobar/*.log
|
||||||
|
labels:
|
||||||
|
type: foobar
|
||||||
|
---
|
||||||
|
filenames:
|
||||||
|
- /path/to/barbaz.log
|
||||||
|
labels:
|
||||||
|
type: barbaz
|
||||||
|
EOT
|
||||||
|
|
||||||
|
# multiple items, to a directory
|
||||||
|
|
||||||
|
# avoid the BATS_TEST_TMPDIR variable, it can have a double //
|
||||||
|
acquisdir=$(TMPDIR="$BATS_FILE_TMPDIR" mktemp -u)
|
||||||
|
mkdir "$acquisdir"
|
||||||
|
|
||||||
|
rune -0 cscli setup datasources /dev/stdin --to-dir "$acquisdir" <<-EOT
|
||||||
|
setup:
|
||||||
|
- detected_service: apache2
|
||||||
|
datasource:
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
- detected_service: foobar
|
||||||
|
datasource:
|
||||||
|
labels:
|
||||||
|
type: foobar
|
||||||
|
filenames:
|
||||||
|
- /var/log/foobar/*.log
|
||||||
|
- detected_service: barbaz
|
||||||
|
datasource:
|
||||||
|
labels:
|
||||||
|
type: barbaz
|
||||||
|
filenames:
|
||||||
|
- /path/to/barbaz.log
|
||||||
|
EOT
|
||||||
|
|
||||||
|
# XXX what if detected_service is missing?
|
||||||
|
|
||||||
|
rune -0 cat "${acquisdir}/setup.apache2.yaml"
|
||||||
|
rune -0 yq '. head_comment=""' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 cat "${acquisdir}/setup.foobar.yaml"
|
||||||
|
rune -0 yq '. head_comment=""' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
filenames:
|
||||||
|
- /var/log/foobar/*.log
|
||||||
|
labels:
|
||||||
|
type: foobar
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 cat "${acquisdir}/setup.barbaz.yaml"
|
||||||
|
rune -0 yq '. head_comment=""' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
filenames:
|
||||||
|
- /path/to/barbaz.log
|
||||||
|
labels:
|
||||||
|
type: barbaz
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rm -rf -- "${acquisdir:?}"
|
||||||
|
mkdir "$acquisdir"
|
||||||
|
|
||||||
|
# having both filenames and journalctl does not generate two files: the datasource is copied as-is, even if incorrect
|
||||||
|
|
||||||
|
rune -0 cscli setup datasources /dev/stdin --to-dir "$acquisdir" <<-EOT
|
||||||
|
setup:
|
||||||
|
- detected_service: apache2
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
journalctl_filter:
|
||||||
|
- _SYSTEMD_UNIT=apache2.service
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 cat "${acquisdir}/setup.apache2.yaml"
|
||||||
|
rune -0 yq '. head_comment=""' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
journalctl_filter:
|
||||||
|
- _SYSTEMD_UNIT=apache2.service
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
EOT
|
||||||
|
|
||||||
|
# the directory must exist
|
||||||
|
rune -1 --separate-stderr cscli setup datasources /dev/stdin --to-dir /path/does/not/exist <<< '{}'
|
||||||
|
assert_stderr --partial "directory /path/does/not/exist does not exist"
|
||||||
|
|
||||||
|
# of course it must be a directory
|
||||||
|
|
||||||
|
touch "${acquisdir}/notadir"
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup datasources /dev/stdin --to-dir "${acquisdir}/notadir" <<-EOT
|
||||||
|
setup:
|
||||||
|
- detected_service: apache2
|
||||||
|
datasource:
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
EOT
|
||||||
|
assert_stderr --partial "open ${acquisdir}/notadir/setup.apache2.yaml: not a directory"
|
||||||
|
|
||||||
|
rm -rf -- "${acquisdir:?}"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup datasources (disclaimer)" {
|
||||||
|
disclaimer="This file was automatically generated"
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup datasources /dev/stdin <<<"setup:"
|
||||||
|
rune -0 yq 'head_comment' <(output)
|
||||||
|
assert_output --partial "$disclaimer"
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup datasources /dev/stdin <<-EOT
|
||||||
|
setup:
|
||||||
|
- detected_service: something
|
||||||
|
datasource:
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/something.log
|
||||||
|
EOT
|
||||||
|
rune -0 yq 'head_comment' <(output)
|
||||||
|
assert_output --partial "$disclaimer"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup (custom journalctl filter)" {
|
||||||
|
tempfile=$(TMPDIR="$BATS_TEST_TMPDIR" mktemp)
|
||||||
|
cat <<-EOT >"${tempfile}"
|
||||||
|
version: 1.0
|
||||||
|
detect:
|
||||||
|
thewiz:
|
||||||
|
when:
|
||||||
|
- UnitFound("thewiz.service")
|
||||||
|
datasource:
|
||||||
|
source: journalctl
|
||||||
|
labels:
|
||||||
|
type: thewiz
|
||||||
|
journalctl_filter:
|
||||||
|
- "SYSLOG_IDENTIFIER=TheWiz"
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 --separate-stderr cscli setup detect --detect-config "$tempfile" --force-unit thewiz.service
|
||||||
|
rune -0 jq -cS '.' <(output)
|
||||||
|
assert_json '{setup:[{datasource:{source:"journalctl",journalctl_filter:["SYSLOG_IDENTIFIER=TheWiz"],labels:{type:"thewiz"}},detected_service:"thewiz"}]}'
|
||||||
|
rune -0 --separate-stderr cscli setup datasources <(output)
|
||||||
|
rune -0 yq '. head_comment=""' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
journalctl_filter:
|
||||||
|
- SYSLOG_IDENTIFIER=TheWiz
|
||||||
|
labels:
|
||||||
|
type: thewiz
|
||||||
|
source: journalctl
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rm -f "$tempfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "cscli setup validate" {
|
||||||
|
# an empty file is not enough
|
||||||
|
rune -1 --separate-stderr cscli setup validate /dev/null
|
||||||
|
assert_output "EOF"
|
||||||
|
assert_stderr --partial "invalid setup file"
|
||||||
|
|
||||||
|
# this is ok; install nothing
|
||||||
|
rune -0 --separate-stderr cscli setup validate /dev/stdin <<-EOT
|
||||||
|
setup:
|
||||||
|
EOT
|
||||||
|
refute_output
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup validate /dev/stdin <<-EOT
|
||||||
|
se tup:
|
||||||
|
EOT
|
||||||
|
assert_output - <<-EOT
|
||||||
|
[1:1] unknown field "se tup"
|
||||||
|
> 1 | se tup:
|
||||||
|
^
|
||||||
|
EOT
|
||||||
|
assert_stderr --partial "invalid setup file"
|
||||||
|
|
||||||
|
rune -1 --separate-stderr cscli setup validate /dev/stdin <<-EOT
|
||||||
|
setup:
|
||||||
|
alsdk al; sdf
|
||||||
|
EOT
|
||||||
|
assert_output "while unmarshaling setup file: yaml: line 2: could not find expected ':'"
|
||||||
|
assert_stderr --partial "invalid setup file"
|
||||||
|
}
|
||||||
|
|
88
tests/bats/testdata/07_setup/detect.yaml
vendored
Normal file
88
tests/bats/testdata/07_setup/detect.yaml
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
# TODO: windows, use_time_machine, event support (see https://hub.crowdsec.net/author/crowdsecurity/collections/iis)
|
||||||
|
|
||||||
|
---
|
||||||
|
version: 1.0
|
||||||
|
|
||||||
|
detect:
|
||||||
|
apache2:
|
||||||
|
when:
|
||||||
|
- ProcessRunning("apache2")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
filenames:
|
||||||
|
- /var/log/apache2/*.log
|
||||||
|
- /var/log/*http*/*.log
|
||||||
|
- /var/log/httpd/*.log
|
||||||
|
|
||||||
|
apache2-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("apache2.service")
|
||||||
|
- OS.ID != "centos"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: journalctl
|
||||||
|
journalctl_filter:
|
||||||
|
- "_SYSTEMD_UNIT=mock-apache2.service"
|
||||||
|
labels:
|
||||||
|
type: apache2
|
||||||
|
|
||||||
|
apache2-systemd-centos:
|
||||||
|
when:
|
||||||
|
- UnitFound("httpd.service")
|
||||||
|
- OS.ID == "centos"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: journalctl
|
||||||
|
journalctl_filter:
|
||||||
|
- "_SYSTEMD_UNIT=httpd.service"
|
||||||
|
|
||||||
|
ssh-systemd:
|
||||||
|
when:
|
||||||
|
- UnitFound("ssh.service") or UnitFound("ssh.socket")
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/apache2
|
||||||
|
datasource:
|
||||||
|
source: journalctl
|
||||||
|
journalctl_filter:
|
||||||
|
- "_SYSTEMD_UNIT=ssh.service"
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
|
||||||
|
linux:
|
||||||
|
when:
|
||||||
|
- OS.Family == "linux"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/linux
|
||||||
|
datasource:
|
||||||
|
source: file
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
|
filenames:
|
||||||
|
- /var/log/syslog
|
||||||
|
- /var/log/kern.log
|
||||||
|
- /var/log/messages
|
||||||
|
|
||||||
|
freebsd:
|
||||||
|
when:
|
||||||
|
- OS.Family == "freebsd"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/freebsd
|
||||||
|
|
||||||
|
windows:
|
||||||
|
when:
|
||||||
|
- OS.Family == "windows"
|
||||||
|
install:
|
||||||
|
collections:
|
||||||
|
- crowdsecurity/windows
|
|
@ -63,6 +63,9 @@ config_generate() {
|
||||||
|
|
||||||
cp ../config/context.yaml "${CONFIG_DIR}/console/"
|
cp ../config/context.yaml "${CONFIG_DIR}/console/"
|
||||||
|
|
||||||
|
cp ../config/detect.yaml \
|
||||||
|
"${HUB_DIR}"
|
||||||
|
|
||||||
# the default acquis file contains files that are not readable by everyone
|
# the default acquis file contains files that are not readable by everyone
|
||||||
touch "$LOG_DIR/empty.log"
|
touch "$LOG_DIR/empty.log"
|
||||||
cat <<-EOT >"$CONFIG_DIR/acquis.yaml"
|
cat <<-EOT >"$CONFIG_DIR/acquis.yaml"
|
||||||
|
|
Loading…
Reference in a new issue