Compare commits

...

4 commits

Author SHA1 Message Date
Marco Mariani
734c3d11fa wip 2022-12-10 22:17:59 +01:00
Marco Mariani
978872c1e6 rename 01_base -> 01_cscli 2022-12-10 22:17:59 +01:00
Marco Mariani
7c3d4488f6 go mod tidy 2022-12-10 22:17:59 +01:00
Marco Mariani
79276bda95 cscli setup 2022-12-10 22:17:59 +01:00
83 changed files with 7279 additions and 580 deletions

View file

@ -182,6 +182,9 @@ linters:
issues:
# “Look, thats why theres rules, understand? So that you think before you
# break em.” ― Terry Pratchett
max-issues-per-linter: 0
max-same-issues: 10
exclude-rules:

View file

@ -226,7 +226,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
rootCmd.AddCommand(NewHubTestCmd())
rootCmd.AddCommand(NewNotificationsCmd())
rootCmd.AddCommand(NewSupportCmd())
rootCmd.AddCommand(NewSetupCmd())
if err := rootCmd.Execute(); err != nil {
if bincoverTesting != "" {
log.Debug("coverage report is enabled")

312
cmd/crowdsec-cli/setup.go Normal file
View 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"
kyaml "sigs.k8s.io/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 = kyaml.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
}

480
config/detect.yaml Normal file
View file

@ -0,0 +1,480 @@
---
version: 1.0
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

30
debian/postinst vendored
View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
COLLECTIONS=false
set -e
@ -7,11 +7,11 @@ set -e
. /usr/share/debconf/confmodule
if [ "$1" = configure ]; then
if [[ ! -d /var/lib/crowdsec/data ]]; then
if [ ! -d /var/lib/crowdsec/data ]; then
mkdir -p /var/lib/crowdsec/data
fi
if [[ -d /var/lib/crowdsec/backup ]]; then
if [ -d /var/lib/crowdsec/backup ]; then
cscli config restore /var/lib/crowdsec/backup/backup.config
rm -rf /var/lib/crowdsec/backup
/usr/bin/cscli hub update
@ -19,29 +19,27 @@ if [ "$1" = configure ]; then
systemctl start crowdsec
fi
. /usr/share/crowdsec/wizard.sh -n
if ! [[ -f /etc/crowdsec/acquis.yaml ]]; then
echo Creating /etc/crowdsec/acquis.yaml
set +e
SILENT=true detect_services
SILENT=true TMP_ACQUIS_FILE_SKIP=skip genacquisition
set -e
COLLECTIONS=true
if ! find /etc/crowdsec/acquis.d -maxdepth 1 -type f -name '*' 2>/dev/null | grep -q '.'; then
echo Creating /etc/crowdsec/acquis.d
mkdir -p /etc/crowdsec/acquis.d
cscli setup detect >/etc/crowdsec/.setup.yaml
cscli setup install-hub /etc/crowdsec/.setup.yaml
cscli setup datasources /etc/crowdsec/.setup.yaml --to-dir /etc/crowdsec/acquis.d
fi
if [[ -f /etc/crowdsec/local_api_credentials.yaml ]] ; then
if [ -f /etc/crowdsec/local_api_credentials.yaml ] ; then
chmod 600 /etc/crowdsec/local_api_credentials.yaml
fi
if [[ -f /etc/crowdsec/online_api_credentials.yaml ]]; then
if [ -f /etc/crowdsec/online_api_credentials.yaml ]; then
chmod 600 /etc/crowdsec/online_api_credentials.yaml
fi
if [[ ! -f /etc/crowdsec/local_api_credentials.yaml ]] || [[ ! -f /etc/crowdsec/online_api_credentials.yaml ]]; then
if [[ ! -f /etc/crowdsec/local_api_credentials.yaml ]] ; then
if [ ! -f /etc/crowdsec/local_api_credentials.yaml ] || [ ! -f /etc/crowdsec/online_api_credentials.yaml ]; then
if [ ! -f /etc/crowdsec/local_api_credentials.yaml ] ; then
install -m 600 /dev/null /etc/crowdsec/local_api_credentials.yaml
fi
if [[ ! -f /etc/crowdsec/online_api_credentials.yaml ]] ; then
if [ ! -f /etc/crowdsec/online_api_credentials.yaml ] ; then
install -m 600 /dev/null /etc/crowdsec/online_api_credentials.yaml
fi

20
go.mod
View file

@ -53,7 +53,7 @@ require (
github.com/r3labs/diff/v2 v2.14.1
github.com/sirupsen/logrus v1.8.1
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
google.golang.org/grpc v1.46.0
@ -65,18 +65,24 @@ require (
)
require (
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/sprig/v3 v3.2.2
github.com/aquasecurity/table v1.8.0
github.com/beevik/etree v1.1.0
github.com/blackfireio/osinfo v1.0.3
github.com/goccy/go-yaml v1.9.7
github.com/google/winops v0.0.0-20211216095627-f0e86eb1453b
github.com/ivanpirog/coloredcobra v1.0.1
github.com/lithammer/dedent v1.1.0
github.com/mattn/go-isatty v0.0.14
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
github.com/segmentio/kafka-go v0.4.34
github.com/shirou/gopsutil/v3 v3.22.11
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.2.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apiserver v0.22.5
sigs.k8s.io/yaml v1.2.0
)
require (
@ -97,6 +103,7 @@ require (
github.com/docker/go-units v0.4.0 // indirect
github.com/gin-contrib/sse v0.1.0 // 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/inflect v0.19.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
@ -112,7 +119,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // 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/gorilla/mux v1.7.3 // indirect
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
@ -133,6 +140,7 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.15.7 // 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/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
@ -151,6 +159,7 @@ require (
github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // 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/procfs v0.8.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
@ -160,20 +169,23 @@ require (
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/pflag v1.0.5 // 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/vjeantet/grok v1.0.1 // 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
go.mongodb.org/mongo-driver v1.9.0 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
gopkg.in/inf.v0 v0.9.1 // 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/apimachinery v0.25.2 // indirect
k8s.io/klog/v2 v2.70.1 // indirect

41
go.sum
View file

@ -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/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
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/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
@ -209,6 +211,7 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@ -246,6 +249,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.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
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.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@ -372,6 +377,8 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/goccy/go-yaml v1.9.7 h1:D/Vx+JITklB1ugSkncB4BNR67M3X6AKs9+rqVeo3ddw=
github.com/goccy/go-yaml v1.9.7/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
@ -431,8 +438,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.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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
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/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -634,8 +641,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.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
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.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/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@ -651,6 +662,7 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
@ -664,7 +676,6 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
@ -762,6 +773,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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/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=
@ -827,6 +840,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/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v3 v3.22.11 h1:kxsPKS+Eeo+VnEQ2XCaGJepeP6KY53QoRTETx3+1ndM=
github.com/shirou/gopsutil/v3 v3.22.11/go.mod h1:xl0EeL4vXJ+hQMAGN8B9VFpxukEMA0XdevQOe5MZ1oY=
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/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@ -864,8 +879,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.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.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 v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -875,8 +891,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.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.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.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/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=
@ -888,6 +905,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.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
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-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@ -918,6 +939,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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
@ -1126,6 +1149,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-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-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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1157,6 +1181,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-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-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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1173,9 +1198,10 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/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-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -1264,6 +1290,7 @@ golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=

View file

@ -134,6 +134,7 @@ func GetItemMap(itemType string) map[string]Item {
var m map[string]Item
var ok bool
log.Tracef("hubIdx: %v", hubIdx)
if m, ok = hubIdx[itemType]; !ok {
return nil
}
@ -178,6 +179,7 @@ func GetItemByPath(itemType string, itemPath string) (*Item, error) {
}
func GetItem(itemType string, itemName string) *Item {
log.Tracef("getting hub item %s: %s", itemType, itemName)
if m, ok := GetItemMap(itemType)[itemName]; ok {
return &m
}

338
pkg/setup/README.md Normal file
View file

@ -0,0 +1,338 @@
---
# 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
View 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
}

1012
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
View file

@ -0,0 +1,9 @@
package setup
var (
SystemdUnitList = systemdUnitList
FilterWithRules = filterWithRules
ApplyRules = applyRules
// NormalizeVersion = normalizeVersion
)

255
pkg/setup/install.go Normal file
View 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
View 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
View 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)
}

View file

@ -1,22 +1,66 @@
#!/bin/bash
#!/bin/sh
set -e
# XXX this can't be a good place to make the tree
BASE="./tests"
usage() {
echo "Usage:"
echo " ./wizard.sh -h Display this help message."
echo " ./test_env.sh -d ./tests Create test environment in './tests' folder"
echo " $0 -h Display this help message."
echo " $0 -d ./tests Create test environment in './tests' folder"
exit 0
}
set_colors() {
FG_BLACK=""
FG_RED=""
FG_GREEN=""
FG_YELLOW=""
FG_BLUE=""
FG_MAGENTA=""
FG_CYAN=""
FG_WHITE=""
BOLD=""
RESET=""
while [[ $# -gt 0 ]]
#shellcheck disable=SC2034
if tput sgr0 >/dev/null; then
FG_BLACK=$(tput setaf 0)
FG_RED=$(tput setaf 1)
FG_GREEN=$(tput setaf 2)
FG_YELLOW=$(tput setaf 3)
FG_BLUE=$(tput setaf 4)
FG_MAGENTA=$(tput setaf 5)
FG_CYAN=$(tput setaf 6)
FG_WHITE=$(tput setaf 7)
BOLD=$(tput bold)
RESET=$(tput sgr0)
fi
}
log_info() {
msg=$1
date=$(date +%x:%X)
echo "{FG_BLUE}INFO${RESET}[${date}] $msg"
}
log_err() {
msg=$1
date=$(date +%x:%X)
echo "${FG_RED}ERR${RESET}[${date}] $msg" >&2
}
set_colors()
while [ $# -gt 0 ]
do
key="${1}"
case ${key} in
-d|--directory)
BASE=${2}
shift #past argument
shift
BASE=$1
shift
;;
-h|--help)
@ -31,7 +75,7 @@ do
esac
done
BASE=$(realpath $BASE)
BASE=$(realpath "$BASE")
DATA_DIR="$BASE/data"
@ -51,13 +95,8 @@ PLUGINS="http slack splunk email"
PLUGINS_DIR="plugins"
NOTIF_DIR="notifications"
log_info() {
msg=$1
date=$(date +%x:%X)
echo -e "[$date][INFO] $msg"
}
create_arbo() {
create_tree() {
mkdir -p "$BASE"
mkdir -p "$DATA_DIR"
mkdir -p "$LOG_DIR"
@ -83,38 +122,37 @@ copy_files() {
cp "./config/acquis.yaml" "$CONFIG_DIR"
touch "$CONFIG_DIR"/local_api_credentials.yaml
touch "$CONFIG_DIR"/online_api_credentials.yaml
envsubst < "./config/dev.yaml" > $BASE/dev.yaml
for plugin in $PLUGINS
do
cp $PLUGINS_DIR/$NOTIF_DIR/$plugin/notification-$plugin $BASE/$PLUGINS_DIR/notification-$plugin
cp $PLUGINS_DIR/$NOTIF_DIR/$plugin/$plugin.yaml $CONFIG_DIR/$NOTIF_DIR/$plugin.yaml
envsubst < "./config/dev.yaml" > "$BASE/dev.yaml"
for plugin in $PLUGINS; do
cp "$PLUGINS_DIR/$NOTIF_DIR/$plugin/notification-$plugin" "$BASE/$PLUGINS_DIR/notification-$plugin"
cp "$PLUGINS_DIR/$NOTIF_DIR/$plugin/$plugin.yaml" "$CONFIG_DIR/$NOTIF_DIR/$plugin.yaml"
done
}
setup() {
$BASE/cscli -c "$CONFIG_FILE" hub update
$BASE/cscli -c "$CONFIG_FILE" collections install crowdsecurity/linux
"$BASE/cscli" -c "$CONFIG_FILE" hub update
"$BASE/cscli" -c "$CONFIG_FILE" collections install crowdsecurity/linux
}
setup_api() {
$BASE/cscli -c "$CONFIG_FILE" machines add test -p testpassword -f $CONFIG_DIR/local_api_credentials.yaml --force
"$BASE/cscli" -c "$CONFIG_FILE" machines add test -p testpassword -f "$CONFIG_DIR/local_api_credentials.yaml" --force
}
main() {
log_info "Creating test arboresence in $BASE"
create_arbo
log_info "Arboresence created"
log_info "Creating directory tree in $BASE"
create_tree
log_info "Directory tree created"
log_info "Copying needed files for tests environment"
copy_files
log_info "Files copied"
log_info "Setting up configurations"
CURRENT_PWD=$(pwd)
cd $BASE
cd "$BASE"
setup_api
setup
cd $CURRENT_PWD
cd "$CURRENT_PWD"
log_info "Environment is ready in $BASE"
}

16
tests/ansible/env/source-wizard.sh vendored Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
DB_BACKEND=sqlite
TEST_SUITE_GIT=https://github.com/crowdsecurity/crowdsec
TEST_SUITE_VERSION=mm-wizard
export DB_BACKEND
export PACKAGE_TESTING
export TEST_SUITE_GIT
export TEST_SUITE_VERSION
export TEST_SUITE_ZIP
export TEST_PACKAGE_VERSION_DEB
export TEST_PACKAGE_VERSION_RPM
export TEST_PACKAGE_FILE
export TEST_PACKAGE_DIR
export TEST_SKIP

View file

@ -6,6 +6,9 @@ roles:
- src: https://github.com/crowdsecurity/ansible-role-postgresql
version: crowdsec
name: geerlingguy.postgresql
# these should be included as dependencies of crowdsecurity.testing, but sometime are not
- src: geerlingguy.repo-epel
- src: gantsign.golang
collections:
- name: https://github.com/crowdsecurity/ansible-collection-crowdsecurity.testing.git

View file

@ -45,7 +45,7 @@
pattern: "*.bats"
register: testfiles
- name: "Run BATS tests for source build"
- name: "Run BATS: functional tests for source build"
become: false
block:
- name: "Run test scripts"
@ -74,7 +74,7 @@
when:
- (package_testing is not defined) or (package_testing in ['', 'false', 'False'])
- name: "Run BATS tests for binary package"
- name: "Run BATS: functional tests for binary package"
become: true
block:
- name: "Run test scripts"

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
Vagrant.configure('2') do |config|
config.vm.box = 'centos/stream8'
config.vm.define 'wizard'
config.vm.provision 'shell', path: 'bootstrap'
config.vm.provider :libvirt do |libvirt|
libvirt.cpus = 4
libvirt.memory = 4096
end
config.vm.synced_folder '.', '/vagrant', disabled: true
# config.vm.provision 'ansible' do |ansible|
# ansible.config_file = '../../ansible.cfg'
# ansible.playbook = '../../run_all.yml'
# end
# same as above, to run the steps separately
config.vm.provision 'ansible' do |provdep|
provdep.config_file = '../../../ansible.cfg'
provdep.playbook = '../../../provision_dependencies.yml'
end
config.vm.provision 'ansible' do |provtest|
provtest.config_file = '../../../ansible.cfg'
provtest.playbook = '../../../provision_test_suite.yml'
end
config.vm.provision 'ansible' do |preptest|
preptest.config_file = '../../../ansible.cfg'
preptest.playbook = '../../../prepare_tests.yml'
end
# config.vm.provision 'ansible' do |runtests|
# runtests.config_file = '../../../ansible.cfg'
# runtests.playbook = '../../../run_tests.yml'
# end
end

View file

@ -0,0 +1,5 @@
#!/bin/sh
unset IFS
set -euf
sudo dnf -y update

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
Vagrant.configure('2') do |config|
config.vm.box = 'debian/bullseye64'
config.vm.define 'wizard'
config.vm.provision 'shell', path: 'bootstrap'
config.vm.provider :libvirt do |libvirt|
libvirt.cpus = 4
libvirt.memory = 4096
end
config.vm.synced_folder '.', '/vagrant', disabled: true
# config.vm.provision 'ansible' do |ansible|
# ansible.config_file = '../../ansible.cfg'
# ansible.playbook = '../../run_all.yml'
# end
# same as above, to run the steps separately
config.vm.provision 'ansible' do |provdep|
provdep.config_file = '../../../ansible.cfg'
provdep.playbook = '../../../provision_dependencies.yml'
end
config.vm.provision 'ansible' do |provtest|
provtest.config_file = '../../../ansible.cfg'
provtest.playbook = '../../../provision_test_suite.yml'
end
config.vm.provision 'ansible' do |preptest|
preptest.config_file = '../../../ansible.cfg'
preptest.playbook = '../../../prepare_tests.yml'
end
# config.vm.provision 'ansible' do |runtests|
# runtests.config_file = '../../../ansible.cfg'
# runtests.playbook = '../../../run_tests.yml'
# end
end

View file

@ -0,0 +1,5 @@
#!/bin/sh
unset IFS
set -euf
sudo apt install -y aptitude

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
Vagrant.configure('2') do |config|
config.vm.box = 'debian/buster64'
config.vm.define 'wizard'
config.vm.provision 'shell', path: 'bootstrap'
config.vm.provider :libvirt do |libvirt|
libvirt.cpus = 4
libvirt.memory = 4096
end
config.vm.synced_folder '.', '/vagrant', disabled: true
# config.vm.provision 'ansible' do |ansible|
# ansible.config_file = '../../ansible.cfg'
# ansible.playbook = '../../run_all.yml'
# end
# same as above, to run the steps separately
config.vm.provision 'ansible' do |provdep|
provdep.config_file = '../../../ansible.cfg'
provdep.playbook = '../../../provision_dependencies.yml'
end
config.vm.provision 'ansible' do |provtest|
provtest.config_file = '../../../ansible.cfg'
provtest.playbook = '../../../provision_test_suite.yml'
end
config.vm.provision 'ansible' do |preptest|
preptest.config_file = '../../../ansible.cfg'
preptest.playbook = '../../../prepare_tests.yml'
end
# config.vm.provision 'ansible' do |runtests|
# runtests.config_file = '../../../ansible.cfg'
# runtests.playbook = '../../../run_tests.yml'
# end
end

View file

@ -0,0 +1,5 @@
#!/bin/sh
unset IFS
set -euf
sudo apt install -y aptitude

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
Vagrant.configure('2') do |config|
config.vm.box = 'fedora/36-cloud-base'
config.vm.define 'wizard'
config.vm.provision 'shell', path: 'bootstrap'
config.vm.provider :libvirt do |libvirt|
libvirt.cpus = 4
libvirt.memory = 4096
end
config.vm.synced_folder '.', '/vagrant', disabled: true
# config.vm.provision 'ansible' do |ansible|
# ansible.config_file = '../../ansible.cfg'
# ansible.playbook = '../../run_all.yml'
# end
# same as above, to run the steps separately
config.vm.provision 'ansible' do |provdep|
provdep.config_file = '../../../ansible.cfg'
provdep.playbook = '../../../provision_dependencies.yml'
end
config.vm.provision 'ansible' do |provtest|
provtest.config_file = '../../../ansible.cfg'
provtest.playbook = '../../../provision_test_suite.yml'
end
config.vm.provision 'ansible' do |preptest|
preptest.config_file = '../../../ansible.cfg'
preptest.playbook = '../../../prepare_tests.yml'
end
# config.vm.provision 'ansible' do |runtests|
# runtests.config_file = '../../../ansible.cfg'
# runtests.playbook = '../../../run_tests.yml'
# end
end

View file

@ -0,0 +1,5 @@
#!/bin/sh
unset IFS
set -euf
sudo dnf -y update

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
Vagrant.configure('2') do |config|
config.vm.box = 'generic/ubuntu2204'
config.vm.define 'wizard'
config.vm.provision 'shell', path: 'bootstrap'
config.vm.provider :libvirt do |libvirt|
libvirt.cpus = 4
libvirt.memory = 4096
end
config.vm.synced_folder '.', '/vagrant', disabled: true
# config.vm.provision 'ansible' do |ansible|
# ansible.config_file = '../../ansible.cfg'
# ansible.playbook = '../../run_all.yml'
# end
# same as above, to run the steps separately
config.vm.provision 'ansible' do |provdep|
provdep.config_file = '../../../ansible.cfg'
provdep.playbook = '../../../provision_dependencies.yml'
end
config.vm.provision 'ansible' do |provtest|
provtest.config_file = '../../../ansible.cfg'
provtest.playbook = '../../../provision_test_suite.yml'
end
config.vm.provision 'ansible' do |preptest|
preptest.config_file = '../../../ansible.cfg'
preptest.playbook = '../../../prepare_tests.yml'
end
# config.vm.provision 'ansible' do |runtests|
# runtests.config_file = '../../../ansible.cfg'
# runtests.playbook = '../../../run_tests.yml'
# end
end

View file

@ -0,0 +1,5 @@
#!/bin/sh
unset IFS
set -euf
sudo apt install -y aptitude

View file

@ -0,0 +1,8 @@
Running the tests in this directory WILL change the system configuration in
unpredictable ways, remove packages and data (with a peculiar appetite for
databases) and possibly bring the system to an unusable state.
They are meant to be run, as root, on temporary VMs. They are only intended to
ease the development of configurations for "cscli setup detect".

View file

@ -0,0 +1,49 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove apache2
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "apache2: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'apache2-systemd-deb'
refute_line 'apache2-systemd-rpm'
}
@test "apache2: install" {
run -0 deb-install apache2
run -0 sudo systemctl enable apache2.service
}
@test "apache2: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'apache2-systemd-deb'
refute_line 'apache2-systemd-rpm'
}
@test "apache2: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,49 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove httpd
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "apache2: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'apache2-systemd-rpm'
refute_line 'apache2-systemd-deb'
}
@test "apache2: install" {
run -0 rpm-install httpd
run -0 sudo systemctl enable httpd.service
}
@test "apache2: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'apache2-systemd-rpm'
refute_line 'apache2-systemd-deb'
}
@test "apache2: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove asterisk
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "asterisk: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'asterisk-systemd'
}
@test "asterisk: install" {
run -0 deb-install asterisk
run -0 sudo systemctl enable asterisk.service
}
@test "asterisk: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'asterisk-systemd'
}
@test "asterisk: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,50 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove asterisk
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
if ! dnf list | grep -q asterisk; then
skip 'asterisk package not available'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "asterisk: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'asterisk-systemd'
}
@test "asterisk: install" {
run -0 rpm-install asterisk
run -0 sudo systemctl enable asterisk.service
}
@test "asterisk: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'asterisk-systemd'
}
@test "asterisk: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,53 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove caddy
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "caddy: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'caddy-systemd'
}
@test "caddy: install" {
run -0 deb-install debian-keyring debian-archive-keyring apt-transport-https
run -0 curl -1sSLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key'
run -0 sudo gpg --yes --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg < <(output)
run -0 curl -1sSLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt'
run -0 sudo tee /etc/apt/sources.list.d/caddy-stable.list < <(output)
run -0 deb-update
run -0 deb-install caddy
run -0 sudo systemctl enable caddy.service
}
@test "caddy: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'caddy-systemd'
}
@test "caddy: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,49 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove caddy
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "caddy: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'caddy-systemd'
}
@test "caddy: install" {
run -0 rpm-install 'dnf-command(copr)'
run -0 sudo dnf -q -y copr enable @caddy/caddy
run -0 rpm-install caddy
run -0 sudo systemctl enable caddy.service
}
@test "caddy: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'caddy-systemd'
}
@test "caddy: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove dovecot-core
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "dovecot: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'dovecot-systemd'
}
@test "dovecot: install" {
run -0 deb-install dovecot-core
run -0 sudo systemctl enable dovecot.service
}
@test "dovecot: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'dovecot-systemd'
}
@test "dovecot: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove dovecot
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "dovecot: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'dovecot-systemd'
}
@test "dovecot: install" {
run -0 rpm-install dovecot
run -0 sudo systemctl enable dovecot.service
}
@test "dovecot: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'dovecot-systemd'
}
@test "dovecot: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,52 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove emby-server
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "emby: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'emby-systemd'
}
@test "emby: install" {
# https://emby.media/linux-server.html
version=4.7.6.0
filename="emby-server-deb_${version}_amd64.deb"
# don't download twice
run -0 curl -1sSLf "https://github.com/MediaBrowser/Emby.Releases/releases/download/${version}/${filename}" -o "${CACHEDIR}/${filename}"
run -0 sudo dpkg --install "${CACHEDIR}/${filename}"
run -0 sudo systemctl enable emby-server.service
}
@test "emby: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'emby-systemd'
}
@test "emby: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,52 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove emby-server
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "emby: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'emby-systemd'
}
@test "emby: install" {
# https://emby.media/linux-server.html
version=4.7.6.0
filename="emby-server-rpm_${version}_x86_64.rpm"
# don't download twice
run -0 curl -1sSLf "https://github.com/MediaBrowser/Emby.Releases/releases/download/${version}/${filename}" -o "${CACHEDIR}/${filename}"
run -0 rpm-install "${CACHEDIR}/${filename}"
run -0 sudo systemctl enable emby-server.service
}
@test "emby: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'emby-systemd'
}
@test "emby: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,48 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove endlessh
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "endlessh: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'endlessh-systemd'
}
@test "endlessh: install" {
# https://github.com/skeeto/endlessh
run -0 deb-install endlessh
run -0 sudo systemctl enable endlessh.service
}
@test "endlessh: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'endlessh-systemd'
}
@test "endlessh: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,48 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove endlessh
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "endlessh: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'endlessh-systemd'
}
@test "endlessh: install" {
# https://github.com/skeeto/endlessh
run -0 rpm-install endlessh
run -0 sudo systemctl enable endlessh.service
}
@test "endlessh: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'endlessh-systemd'
}
@test "endlessh: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,46 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
}
setup() {
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
skip 'WIP'
./instance-data load
}
#----------
@test "gitea: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'gitea-systemd'
}
@test "gitea: install" {
# https://docs.gitea.io/en-us/install-from-binary/#download
version=1.16.9
# don't download twice
run -0 wget -nc --directory-prefix "$CACHEDIR" "https://dl.gitea.io/gitea/${version}/gitea-${version}-linux-amd64"
}
@test "gitea: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'gitea-systemd'
}
@test "gitea: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove haproxy
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "haproxy: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'haproxy-systemd'
}
@test "haproxy: install" {
run -0 deb-install haproxy
run -0 sudo systemctl enable haproxy.service
}
@test "haproxy: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'haproxy-systemd'
}
@test "haproxy: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove haproxy
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "haproxy: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'haproxy-systemd'
}
@test "haproxy: install" {
run -0 rpm-install haproxy
run -0 sudo systemctl enable haproxy.service
}
@test "haproxy: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'haproxy-systemd'
}
@test "haproxy: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove lemonldap-ng
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "lemonldap: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'lemonldap-ng-systemd'
}
@test "lemonldap: install" {
run -0 deb-install lemonldap-ng
run -0 sudo systemctl enable lemonldap-ng-fastcgi-server.service
}
@test "lemonldap: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'lemonldap-ng-systemd'
}
@test "lemonldap: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,50 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove lemonldap-ng
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
skip 'WIP'
}
#----------
@test "lemonldap: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'lemonldap-ng-systemd'
}
@test "lemonldap: install" {
run -0 rpm-install 'dnf-command(copr)'
run -0 sudo dnf -q -y copr enable xavierb/lemonldap-ng
run -0 rpm-install lemonldap-ng
run -0 sudo systemctl enable lemonldap-ng-fastcgi-server.service
}
@test "lemonldap: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'lemonldap-ng-systemd'
}
@test "lemonldap: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,55 @@
#!/usr/bin/env bash
TESTDATA="${BATS_TEST_DIRNAME}/testdata"
export TESTDATA
CACHEDIR="${TESTDATA}/.cache"
export CACHEDIR
mkdir -p "${CACHEDIR}"
DEBIAN_FRONTEND=noninteractive
export DEBIAN_FRONTEND
# avoid warnings in stderr, especially from perl modules
LC_ALL=C
export LC_ALL
deb-install() {
# use aptitude to reliably purge dependencies too
sudo aptitude install "$@" -yq >/dev/null
# this does not work well enough
# sudo apt-get -qq -y -o Dpkg:Use-Pty=0 install "$@" >/dev/null
# sudo apt-mark auto "$@"
}
export -f deb-install
deb-update() {
sudo apt-get -qq -y -o Dpkg:Use-Pty=0 update
}
export -f deb-update
deb-remove() {
for pkg in "$@"; do
if dpkg -s "${pkg}" >/dev/null 2>&1; then
# use aptitude to reliably purge dependencies too
sudo aptitude purge "${pkg}" -yq >/dev/null
# this does not work well enough
# sudo apt-get -qq -y purge --auto-remove "${pkg}" >/dev/null
fi
done
}
export -f deb-remove
rpm-install() {
sudo dnf -q -y install "$@"
}
export -f rpm-install
rpm-remove() {
# don't fail if dnf does not exist (teardown is called on deb distros too)
if command -v dnf >/dev/null; then
sudo dnf -q -y remove "$@" >/dev/null
fi
}
export -f rpm-remove

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove openlitespeed
}
setup() {
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
skip 'WIP'
./instance-data load
}
#----------
@test "openlitespeed: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'litespeed-systemd'
}
@test "openlitespeed: install" {
run -0 sudo "${TESTDATA}/enable_lst_debian_repo.sh"
run -0 deb-update
run -0 deb-install openlitespeed
# run -0 sudo systemctl enable XXX TODO
}
@test "litespeed: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'litespeed-systemd'
}
@test "litespeed: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove mariadb-server
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "mariadb: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'mariadb-systemd'
}
@test "mariadb: install" {
run -0 deb-install mariadb-server
run -0 sudo systemctl enable mariadb.service
}
@test "mariadb: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'mariadb-systemd'
}
@test "mariadb: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove mariadb-server
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "mariadb: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'mariadb-systemd'
}
@test "mariadb: install" {
run -0 rpm-install mariadb-server
run -0 sudo systemctl enable mariadb.service
}
@test "mariadb: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'mariadb-systemd'
}
@test "mariadb: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,64 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
# debian: mysql-community-server
# ubuntu: mysql-server
deb-remove mysql-server mysql-community-server
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
if apt-cache search --names-only "^mysql-server$"; then
skip "mysql-server package not available"
fi
}
#----------
@test "mysql: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'mysql-systemd'
}
@test "mysql: install" {
# ubuntu comes with mysql, debian does not
if apt-cache search --names-only "^mysql-server$"; then
# package not available, install the repo
filename="mysql-apt-config_0.8.23-1_all.deb"
run -0 curl -1sSLf "https://dev.mysql.com/get/${filename}" -o "${CACHEDIR}/${filename}"
# XXX md5 c2b410031867dc7c966ca5b1aa0c72aa
run -0 sudo dpkg --install "${CACHEDIR}/${filename}"
run -0 deb-update
# XXX this hangs
run -0 deb-install mysql-community-server
else
run -0 deb-install mysql-server
fi
run -0 sudo systemctl enable mysql.service
}
@test "mysql: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'mysql-systemd'
}
@test "mysql: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,48 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove mysql-server
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
skip 'WIP'
./instance-data load
}
#----------
@test "mysql: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'mysql-systemd'
}
@test "mysql: install" {
run -0 rpm-install mysql-server
run -0 sudo systemctl enable mysql.service
}
@test "mysql: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'mysql-systemd'
}
@test "mysql: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove nginx
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "nginx: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'nginx-systemd'
}
@test "nginx: install" {
run -0 deb-install nginx
run -0 sudo systemctl enable nginx.service
}
@test "nginx: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'nginx-systemd'
}
@test "nginx: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove nginx
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "nginx: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'nginx-systemd'
}
@test "nginx: install" {
run -0 rpm-install nginx
run -0 sudo systemctl enable nginx.service
}
@test "nginx: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'nginx-systemd'
}
@test "nginx: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,52 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove odoo
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "odoo: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'odoo-systemd'
}
@test "odoo: install" {
run -0 deb-install debian-keyring debian-archive-keyring apt-transport-https
run -0 curl -1sSLf https://nightly.odoo.com/odoo.key
run -0 sudo gpg --yes --dearmor -o /usr/share/keyrings/odoo-keyring.gpg < <(output)
run -0 sudo tee <<< "deb [signed-by=/usr/share/keyrings/odoo-keyring.gpg] https://nightly.odoo.com/15.0/nightly/deb/ ./" /etc/apt/sources.list.d/odoo.list >/dev/null
run -0 deb-update
run -0 deb-install odoo
# run -0 sudo systemctl enable caddy.service
}
@test "odoo: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'odoo-systemd'
}
@test "odoo: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,49 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove odoo
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
skip 'WIP (https://bytemeta.vip/repo/odoo/odoo/issues/95168)'
./instance-data load
}
#----------
@test "odoo: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'odoo-systemd'
}
@test "odoo: install" {
run -0 sudo dnf config-manager --add-repo=https://nightly.odoo.com/15.0/nightly/rpm/odoo.repo
run -0 rpm-install odoo
run -0 sudo systemctl enable odoo
}
@test "odoo: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'odoo-systemd'
}
@test "odoo: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,52 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove ombi
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "ombi: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'ombi-systemd'
}
@test "ombi: install" {
run -0 deb-install debian-keyring debian-archive-keyring apt-transport-https
run -0 curl -1sSLf https://apt.ombi.app/pub.key
run -0 sudo gpg --yes --dearmor -o /usr/share/keyrings/ombi-keyring.gpg < <(output)
run -0 sudo tee <<< "deb [signed-by=/usr/share/keyrings/ombi-keyring.gpg] https://apt.ombi.app/develop jessie main" /etc/apt/sources.list.d/ombi.list >/dev/null
run -0 deb-update
run -0 deb-install ombi
run -0 sudo systemctl enable ombi.service
}
@test "ombi: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'ombi-systemd'
}
@test "ombi: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,57 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove openresty
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "openresty: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'openresty-systemd'
}
@test "openresty: install" {
run -0 deb-install debian-keyring debian-archive-keyring apt-transport-https
run -0 curl -1sSLf 'https://openresty.org/package/pubkey.gpg'
if [[ "$(lsb_release -is)" == "Ubuntu" ]]; then
run -0 sudo gpg --yes --dearmor -o /usr/share/keyrings/openresty.gpg < <(output)
run -0 sudo tee <<< "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" /etc/apt/sources.list.d/openresty.list
else
run -0 sudo apt-key add - < <(output)
run -0 sudo tee <<< "deb http://openresty.org/package/debian $(lsb_release -sc) openresty" /etc/apt/sources.list.d/openresty.list
fi
run -0 deb-update
run -0 deb-install openresty
run -0 sudo systemctl enable openresty.service
}
@test "openresty: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'openresty-systemd'
}
@test "openresty: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,54 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove openresty
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "openresty: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'openresty-systemd'
}
@test "openresty: install" {
run -0 rpm-install redhat-lsb-core
if [[ "$(lsb_release -is)" == "Fedora" ]]; then
run -0 sudo curl -1sSLf "https://openresty.org/package/fedora/openresty.repo" -o "/etc/yum.repos.d/openresty.repo"
elif [[ "$(lsb_release -is)" == "CentOS" ]]; then
run -0 sudo curl -1sSLf "https://openresty.org/package/centos/openresty.repo" -o "/etc/yum.repos.d/openresty.repo"
fi
run -0 sudo dnf check-update
run -0 rpm-install openresty
run -0 sudo systemctl enable openresty.service
}
@test "openresty: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'openresty-systemd'
}
@test "openresty: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,61 @@
#!/usr/bin/env bats
# vim: ft=bats:list:ts=8:sts=4:sw=4:et:ai:si:
set -u
cleanup() {
command -v dpkg >/dev/null || return 0
# sudo systemctl stop postgresql.service || :
# remove the DB to avoid a prompt from postrm
if [[ -d /var/lib/postgresql ]]; then
# shellcheck disable=SC2045
for cluster in $(ls /var/lib/postgresql 2>/dev/null); do
sudo pg_dropcluster --stop "${cluster}" main
done
fi
deb-remove postgresql $(dpkg -l | grep postgres | awk '{print $2}')
}
setup_file() {
load "../lib/setup_file.sh"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
cleanup
}
teardown_file() {
load "../lib/teardown_file.sh"
cleanup
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "pgsql: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'pgsql-systemd-deb'
}
@test "pgsql: install" {
run -0 deb-install postgresql
run -0 sudo systemctl enable postgresql.service
}
@test "pgsql: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'pgsql-systemd-deb'
}
@test "pgsql: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,51 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove postgresql-server
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "pgsql: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'pgsql-systemd-rpm'
}
@test "pgsql: install" {
run -0 rpm-install postgresql-server
# for centos 8, we need to create the cluster
if ! sudo bash -c 'stat /var/lib/pgsql/data/*'; then
sudo /usr/bin/postgresql-setup --initdb
fi
run -0 sudo systemctl enable postgresql.service
}
@test "pgsql: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'pgsql-systemd-rpm'
}
@test "pgsql: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,49 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove postfix
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "postfix: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'postfix-systemd'
}
@test "postfix: install" {
run -0 sudo debconf-set-selections <<< "postfix postfix/mailname string hostname.example.com"
run -0 sudo debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Internet Site'"
run -0 deb-install postfix
run -0 sudo systemctl enable postfix.service
}
@test "postfix: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'postfix-systemd'
}
@test "postfix: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove postfix
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "postfix: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'postfix-systemd'
}
@test "postfix: install" {
run -0 rpm-install postfix
run -0 sudo systemctl enable postfix.service
}
@test "postfix: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'postfix-systemd'
}
@test "postfix: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove proftpd
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "proftpd: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'proftpd-systemd'
}
@test "proftpd: install" {
run -0 deb-install proftpd
run -0 sudo systemctl enable proftpd.service
}
@test "proftpd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'proftpd-systemd'
}
@test "proftpd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove proftpd
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "proftpd: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'proftpd-systemd'
}
@test "proftpd: install" {
run -0 rpm-install proftpd
run -0 sudo systemctl enable proftpd.service
}
@test "proftpd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'proftpd-systemd'
}
@test "proftpd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,62 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove proxmox-ve
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
. /etc/os-release
case "$VERSION_CODENAME" in
bullseye | buster | jessie | squeeze | stretch | wheezy)
skip "the installation does not work"
;;
*)
skip "unsupported distribution"
;;
esac
export VERSION_CODENAME
}
#----------
@test "proxmox: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'proxmox-systemd'
}
@test "proxmox: install" {
run -0 deb-install debian-keyring debian-archive-keyring apt-transport-https
run -0 sudo curl -1sSLf http://download.proxmox.com/debian/proxmox-ve-release-6.x.gpg -o /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
run -0 sudo tee <<<"deb http://download.proxmox.com/debian/pve ${VERSION_CODENAME} pve-no-subscription" /etc/apt/sources.list.d/proxmox.list >/dev/null
run -0 deb-update
run -0 deb-install proxmox-ve
run -0 sudo systemctl enable proxmox.service
}
@test "proxmox: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'proxmox-systemd'
}
@test "proxmox: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove pure-ftpd
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "pureftpd: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'pureftpd-systemd'
}
@test "pureftpd: install" {
run -0 deb-install pure-ftpd
run -0 sudo systemctl enable pure-ftpd.service
}
@test "pureftpd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'pureftpd-systemd'
}
@test "pureftpd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove pure-ftpd
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "pureftpd: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'pureftpd-systemd'
}
@test "pureftpd: install" {
run -0 rpm-install pure-ftpd
run -0 sudo systemctl enable pure-ftpd.service
}
@test "pureftpd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'pureftpd-systemd'
}
@test "pureftpd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,50 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove samba
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "smb: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'smb-systemd'
}
@test "smb: install" {
run -0 sudo debconf-set-selections <<< "samba-common samba-common/workgroup string WORKGROUP"
run -0 sudo debconf-set-selections <<< "samba-common samba-common/dhcp boolean true"
run -0 sudo debconf-set-selections <<< "samba-common samba-common/do_debconf boolean true"
run -0 deb-install samba
run -0 sudo systemctl enable smbd.service
}
@test "smb: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'smb-systemd'
}
@test "smb: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove samba
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "smb: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'smb-systemd'
}
@test "smb: install" {
run -0 rpm-install samba
run -0 sudo systemctl enable smb.service
}
@test "smb: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'smb-systemd'
}
@test "smb: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,49 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
# don't remove ssh here, we assume it's needed
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "sshd: detect unit (fail)" {
run -0 sudo systemctl mask ssh.service
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'sshd-systemd'
}
@test "sshd: install" {
# run -0 deb-install openssh-server
run -0 sudo systemctl unmask ssh.service
run -0 sudo systemctl enable ssh.service
}
@test "sshd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'sshd-systemd'
}
@test "sshd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,49 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
# don't remove ssh here, we assume it's needed
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "sshd: detect unit (fail)" {
run -0 sudo systemctl mask sshd.service
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'sshd-systemd'
}
@test "sshd: install" {
# run -0 rpm-install openssh-server
run -0 sudo systemctl unmask sshd.service
run -0 sudo systemctl enable sshd.service
}
@test "sshd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'sshd-systemd'
}
@test "sshd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
deb-remove suricata
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "suricata: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'suricata-systemd'
}
@test "suricata: install" {
run -0 deb-install suricata
run -0 sudo systemctl enable suricata.service
}
@test "suricata: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'suricata-systemd'
}
@test "suricata: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove suricata
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "suricata: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'suricata-systemd'
}
@test "suricata: install" {
run -0 rpm-install suricata
run -0 sudo systemctl enable suricata.service
}
@test "suricata: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'suricata-systemd'
}
@test "suricata: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,65 @@
#!/bin/bash
if [ -r /etc/os-release ]; then
echo " detecting OS type : "
. /etc/os-release
if [ $ID == "debian" ]; then
echo "detected OS: $ID - $VERSION_ID"
echo " now enable the LiteSpeed Debian Repo "
if [ $VERSION_ID == "11" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ bullseye main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ bullseye main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ $VERSION_ID == "10" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ buster main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ buster main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ $VERSION_ID == "9" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ stretch main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ stretch main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ $VERSION_ID == "8" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ jessie main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ jessie main" >> /etc/apt/sources.list.d/lst_debian_repo.list
fi
elif [ $ID == "ubuntu" ]; then
echo "detected OS: $ID - $VERSION_ID"
echo " now enable the LiteSpeed Debian Repo "
if [ `echo "$VERSION_ID" | cut -b-2 ` == "14" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ trusty main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ trusty main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ `echo "$VERSION_ID" | cut -b-2 ` == "12" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ precise main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ precise main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ `echo "$VERSION_ID" | cut -b-2 ` == "16" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ xenial main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ xenial main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ `echo "$VERSION_ID" | cut -b-2 ` == "18" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ bionic main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ bionic main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ `echo "$VERSION_ID" | cut -b-2 ` == "20" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ focal main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ focal main" >> /etc/apt/sources.list.d/lst_debian_repo.list
elif [ `echo "$VERSION_ID" | cut -b-2 ` == "22" ]; then
echo "deb http://rpms.litespeedtech.com/debian/ focal main" > /etc/apt/sources.list.d/lst_debian_repo.list
echo "#deb http://rpms.litespeedtech.com/edge/debian/ focal main" >> /etc/apt/sources.list.d/lst_debian_repo.list
fi
else
echo " This distribution is not currently supported by LST repo "
echo " If you really have the needs please contact LiteSpeed for support "
fi
else
echo " The /etc/os-release file doesn't exist "
echo " This script couldn't determine which distribution of the repo should be enabled "
echo " Please consult LiteSpeed Customer Support for further assistance "
fi
echo " register LiteSpeed GPG key "
wget -O /etc/apt/trusted.gpg.d/lst_debian_repo.gpg http://rpms.litespeedtech.com/debian/lst_debian_repo.gpg
wget -O /etc/apt/trusted.gpg.d/lst_repo.gpg http://rpms.litespeedtech.com/debian/lst_repo.gpg
echo " update the repo "
apt-get update
echo " All done, congratulations and enjoy ! "

View file

@ -0,0 +1,48 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
sudo systemctl stop vsftpd.service 2>/dev/null || :
deb-remove vsftpd
}
setup() {
if ! command -v dpkg >/dev/null; then
skip 'not a debian-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "vsftpd: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'vsftpd-systemd'
}
@test "vsftpd: install" {
run -0 deb-install vsftpd
run -0 sudo systemctl enable vsftpd.service
}
@test "vsftpd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'vsftpd-systemd'
}
@test "vsftpd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

View file

@ -0,0 +1,47 @@
#!/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"
load "${BATS_TEST_DIRNAME}/lib/setup_file_detect.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
rpm-remove vsftpd
}
setup() {
if ! command -v dnf >/dev/null; then
skip 'not a redhat-like system'
fi
load "../lib/setup.sh"
load "../lib/bats-file/load.bash"
./instance-data load
}
#----------
@test "vsftpd: detect unit (fail)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
refute_line 'vsftpd-systemd'
}
@test "vsftpd: install" {
run -0 rpm-install vsftpd
run -0 sudo systemctl enable vsftpd.service
}
@test "vsftpd: detect unit (succeed)" {
run -0 cscli setup detect
run -0 jq -r '.setup | .[].detected_service' <(output)
assert_line 'vsftpd-systemd'
}
@test "vsftpd: install detected collection" {
run -0 cscli setup detect
run -0 cscli setup install-hub <(output)
}

815
tests/bats/07_setup.bats Normal file
View file

@ -0,0 +1,815 @@
#!/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
}
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" {
run -0 cscli help
assert_line --regexp '^ +setup +Tools to configure crowdsec$'
run -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
run -0 cscli setup blahblah
assert_line 'Usage:'
}
@test "cscli setup detect --help; --detect-config" {
run -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)"
run -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
run -0 cscli setup detect --detect-config "$tempfile"
assert_json '{setup:[{detected_service:"foobarbaz"},{detected_service:"linux",install:{collections:["crowdsecurity/linux"]}}]}'
run -0 cscli setup detect --detect-config "$tempfile" --skip-service linux
assert_json '{setup:[{detected_service:"foobarbaz"}]}'
}
@test "cscli setup detect --force-os-*" {
run -0 cscli setup detect --force-os-family linux --detect-config "${TESTDATA}/detect.yaml"
run -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"]}}'
run -0 cscli setup detect --force-os-family freebsd --detect-config "${TESTDATA}/detect.yaml"
run -0 jq -cS '.setup[] | select(.detected_service=="freebsd")' <(output)
assert_json '{detected_service:"freebsd",install:{collections:["crowdsecurity/freebsd"]}}'
run -0 cscli setup detect --force-os-family windows --detect-config "${TESTDATA}/detect.yaml"
run -0 jq -cS '.setup[] | select(.detected_service=="windows")' <(output)
assert_json '{detected_service:"windows",install:{collections:["crowdsecurity/windows"]}}'
run -0 --separate-stderr cscli setup detect --force-os-family darwin --detect-config "${TESTDATA}/detect.yaml"
refute_stderr
# 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
run -0 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
run -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
run -0 cscli setup detect
run -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" ]]
run -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
run -0 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 ]]
run -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
run -0 cscli setup detect --force-unit force-apache2
run -0 jq -cS '.setup' <(output)
assert_json '[{datasource:{source:"file",filename:"dummy.log",labels:{"type":"apache2"}},detected_service:"apache2"}]'
run -0 cscli setup detect --force-unit force-apache2,force-apache3
run -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
run -0 cscli setup detect --force-unit force-apache3 --force-unit force-apache2
run -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"}]'
run -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
run -0 cscli setup detect
run -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
run -0 cscli setup detect --force-process force-apache2
run -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
run -0 cscli setup detect --force-unit force-apache2
run -0 jq -cS '.setup' <(output)
assert_json '[{datasource:{source:"file",filename:"dummy.log",labels:{type:"apache2"}},detected_service:"apache2"}]'
run -0 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
run -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
run -0 cscli setup detect
assert_json '{setup:[{detected_service:"always"}]}'
setup=$output
run -0 cscli setup datasources /dev/stdin <<<"$setup"
run -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
run -0 cscli setup detect --force-process force-apache2,force-foobar
run -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
run -0 cscli setup detect --force-process force-foobar
run -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
run -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
run -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
run -0 cscli collections list -o json
run -0 jq -r '.collections[].name' <(output)
refute_line "crowdsecurity/apache2"
# we install it
run -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
run -0 cscli collections list -o json
run -0 jq -r '.collections[].name' <(output)
refute_line "crowdsecurity/apache2"
}
@test "cscli setup install-hub (dry run: install multiple collections)" {
# it's not installed
run -0 cscli collections list -o json
run -0 jq -r '.collections[].name' <(output)
refute_line "crowdsecurity/apache2"
# we install it
run -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
run -0 cscli collections list -o json
run -0 jq -r '.collections[].name' <(output)
refute_line "crowdsecurity/apache2"
}
@test "cscli setup install-hub (dry run: install multiple collections, parsers, scenarios, postoverflows)" {
run -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" {
run -0 cscli setup datasources --help
assert_line --partial "--to-dir string write the configuration to a directory, in multiple files"
# single item
run -0 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
run -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
run -0 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
run -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"
run -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?
run -0 cat "${acquisdir}/setup.apache2.yaml"
run -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
run -0 cat "${acquisdir}/setup.foobar.yaml"
run -0 yq '. head_comment=""' <(output)
assert_output - <<-EOT
filenames:
- /var/log/foobar/*.log
labels:
type: foobar
EOT
run -0 cat "${acquisdir}/setup.barbaz.yaml"
run -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
run -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
run -0 cat "${acquisdir}/setup.apache2.yaml"
run -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
run -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"
run -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"
run -0 cscli setup datasources /dev/stdin <<<"setup:"
run -0 yq 'head_comment' <(output)
assert_output --partial "$disclaimer"
run -0 cscli setup datasources /dev/stdin <<-EOT
setup:
- detected_service: something
datasource:
labels:
type: syslog
filenames:
- /var/log/something.log
EOT
run -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
run -0 cscli setup detect --detect-config "$tempfile" --force-unit thewiz.service
run -0 jq -cS '.' <(output)
assert_json '{setup:[{datasource:{source:"journalctl",journalctl_filter:["SYSLOG_IDENTIFIER=TheWiz"],labels:{type:"thewiz"}},detected_service:"thewiz"}]}'
run -0 cscli setup datasources <(output)
run -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
run -1 --separate-stderr cscli setup validate /dev/null
assert_output "EOF"
assert_stderr --partial "invalid setup file"
# this is ok; install nothing
run -0 --separate-stderr cscli setup validate /dev/stdin <<-EOT
setup:
EOT
refute_output
refute_stderr
run -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"
run -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"
}

View 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

View file

@ -61,6 +61,9 @@ config_generate() {
../config/online_api_credentials.yaml \
"${CONFIG_DIR}/"
cp ../config/detect.yaml \
"${HUB_DIR}"
# the default acquis file contains files that are not readable by everyone
touch "$LOG_DIR/empty.log"
cat <<-EOT >"$CONFIG_DIR/acquis.yaml"

1020
wizard.sh

File diff suppressed because it is too large Load diff