Compare commits
1 commit
master
...
cscli_wiza
Author | SHA1 | Date | |
---|---|---|---|
|
6c27e1216c |
2 changed files with 280 additions and 0 deletions
|
@ -176,6 +176,8 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
|||
rootCmd.AddCommand(NewConsoleCmd())
|
||||
rootCmd.AddCommand(NewExplainCmd())
|
||||
rootCmd.AddCommand(NewHubTestCmd())
|
||||
rootCmd.AddCommand(NewWizardCmd())
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatalf("While executing root command : %s", err)
|
||||
}
|
||||
|
|
278
cmd/crowdsec-cli/wizard.go
Normal file
278
cmd/crowdsec-cli/wizard.go
Normal file
|
@ -0,0 +1,278 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type detect struct {
|
||||
LogFiles []string `json:"log_files"`
|
||||
JournalCTL []string `json:"journalctl"`
|
||||
ServiceName []string `json:"services_name"`
|
||||
Collections []string `json:"collections"`
|
||||
Parsers []string `json:"parsers"`
|
||||
Scenarios []string `json:"scenarios"`
|
||||
PostOverflows []string `json:"postoverflows"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type State struct {
|
||||
Service string
|
||||
Collections []string
|
||||
Parsers []string
|
||||
Scenarios []string
|
||||
PostOverflows []string
|
||||
Type string
|
||||
LogsPath []string
|
||||
JournalCTLFilters []string
|
||||
ServicesName []string // for windows
|
||||
}
|
||||
|
||||
type detectionManager struct {
|
||||
Filepath string
|
||||
OS map[string]*detect `json:"os"`
|
||||
Services map[string]*detect `json:"services"`
|
||||
State map[string]*State
|
||||
}
|
||||
|
||||
// 1 month ago
|
||||
var fileOldLimit = time.Now().AddDate(0, -1, 0)
|
||||
|
||||
const journalCTlOldLimit = "1 month ago"
|
||||
const journalctlCmd = "journalctl"
|
||||
|
||||
func NewServiceDetect(filepath string) (*detectionManager, error) {
|
||||
sd := &detectionManager{Filepath: filepath, State: make(map[string]*State)}
|
||||
jsonFile, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return sd, err
|
||||
}
|
||||
err = json.Unmarshal(jsonFile, sd)
|
||||
return sd, err
|
||||
}
|
||||
|
||||
func isFileOlder(t time.Time, limit time.Time) bool {
|
||||
return t.Before(limit)
|
||||
}
|
||||
|
||||
func detectService(serviceName string, detection *detect) (*State, bool, error) {
|
||||
detected := false
|
||||
state := &State{
|
||||
Service: serviceName,
|
||||
Type: detection.Type,
|
||||
LogsPath: make([]string, 0),
|
||||
JournalCTLFilters: make([]string, 0),
|
||||
Collections: detection.Collections,
|
||||
Parsers: detection.Parsers,
|
||||
Scenarios: detection.Scenarios,
|
||||
PostOverflows: detection.PostOverflows,
|
||||
}
|
||||
|
||||
for _, filePath := range detection.LogFiles {
|
||||
files, err := filepath.Glob(filePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(files) == 0 {
|
||||
log.Debugf("No matching files for pattern %s", filePath)
|
||||
continue
|
||||
}
|
||||
|
||||
// check if at least one file is younger than 1 month
|
||||
ok := false
|
||||
for _, file := range files {
|
||||
fileInfo, err := os.Stat(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if isFileOlder(fileInfo.ModTime(), fileOldLimit) {
|
||||
continue
|
||||
}
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
if ok {
|
||||
detected = true
|
||||
state.LogsPath = append(state.LogsPath, filePath)
|
||||
}
|
||||
}
|
||||
|
||||
for _, journalCTLFilter := range detection.JournalCTL {
|
||||
args := []string{journalCTLFilter, "--since", journalCTlOldLimit}
|
||||
cmd := exec.CommandContext(context.Background(), journalctlCmd, args...)
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Errorf("checking journalctl filter '%s' return error: %s", journalCTLFilter, err)
|
||||
continue
|
||||
}
|
||||
scanner := bufio.NewScanner(bytes.NewReader(stdout))
|
||||
ok := true
|
||||
for scanner.Scan() {
|
||||
if strings.Contains(scanner.Text(), "No entries") {
|
||||
log.Debugf("no entries since '%s' in journalctl '%s' filter, continue", journalCTlOldLimit, journalCTLFilter)
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
detected = true
|
||||
state.JournalCTLFilters = append(state.JournalCTLFilters, journalCTLFilter)
|
||||
}
|
||||
}
|
||||
|
||||
return state, detected, nil
|
||||
}
|
||||
|
||||
func (d *detectionManager) Detect() error {
|
||||
var err error
|
||||
|
||||
log.Debugf("Detected OS: %s", runtime.GOOS)
|
||||
err = d.DetectOS(runtime.GOOS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for serviceName := range d.Services {
|
||||
err = d.DetectService(serviceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *detectionManager) Apply(config *csconfig.CrowdsecServiceCfg) error {
|
||||
for serviceName, service := range d.State {
|
||||
log.Printf("service: %s|%+v", serviceName, service)
|
||||
// check if service already configured
|
||||
dataSourceExist := false
|
||||
for _, dataSource := range dataSources {
|
||||
for logFile := range service.LogsPath {
|
||||
for filenames := range dataSource.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *detectionManager) DetectOS(osName string) error {
|
||||
var ok bool
|
||||
var err error
|
||||
var detectedOS *detect
|
||||
|
||||
if detectedOS, ok = d.OS[osName]; !ok {
|
||||
return fmt.Errorf("os '%s' not supported", osName)
|
||||
}
|
||||
state, detected, err := detectService(osName, detectedOS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if detected {
|
||||
d.State[osName] = state
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *detectionManager) Print() {
|
||||
for serviceName, service := range d.State {
|
||||
fmt.Printf("%s detected\n", strings.Title(serviceName))
|
||||
if len(service.LogsPath) > 0 {
|
||||
fmt.Printf(" * logs files found:\n")
|
||||
for _, logPath := range service.LogsPath {
|
||||
fmt.Printf(" - %s\n", logPath)
|
||||
}
|
||||
}
|
||||
if len(service.JournalCTLFilters) > 0 {
|
||||
fmt.Printf(" * journalctl found:\n")
|
||||
for _, journalctl := range service.JournalCTLFilters {
|
||||
fmt.Printf(" - %s\n", journalctl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *detectionManager) DetectService(serviceName string) error {
|
||||
var ok bool
|
||||
var err error
|
||||
var service *detect
|
||||
|
||||
if service, ok = d.Services[serviceName]; !ok {
|
||||
return fmt.Errorf("service '%s' not found or can't be detected", service)
|
||||
}
|
||||
|
||||
state, detected, err := detectService(serviceName, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if detected {
|
||||
d.State[serviceName] = state
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewWizardCmd() *cobra.Command {
|
||||
var cmdWizard = &cobra.Command{
|
||||
Use: "wizard [action]",
|
||||
Short: "Help to configure crowdsec",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := csConfig.LoadCrowdsec(); err != nil || csConfig.DisableAgent {
|
||||
log.Fatal("CrowdSec agent is not configured or disabled, can't run the wizard")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var apply bool
|
||||
var reconfigure bool
|
||||
var serviceDetectionFile string
|
||||
var service string
|
||||
|
||||
var cmdWizardDetect = &cobra.Command{
|
||||
Use: "detect",
|
||||
Short: "Detect running services, generate acquisitions and install collections",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
serviceDetector, err := NewServiceDetect(serviceDetectionFile)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to load detection services: %s", err)
|
||||
}
|
||||
|
||||
if err := serviceDetector.Detect(); err != nil {
|
||||
log.Fatalf("unable to detect services: %s", err)
|
||||
}
|
||||
serviceDetector.Print()
|
||||
if apply {
|
||||
if err := serviceDetector.Apply(csConfig.Crowdsec); err != nil {
|
||||
log.Fatalf("unable to apply configuration: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
cmdWizardDetect.Flags().StringVarP(&serviceDetectionFile, "file", "f", "/etc/crowdsec/service_detection.json", "File to detect services")
|
||||
cmdWizardDetect.Flags().BoolVarP(&apply, "apply", "a", false, "Apply the detection")
|
||||
cmdWizardDetect.Flags().StringVarP(&service, "service", "s", "", "Service to detect")
|
||||
cmdWizardDetect.Flags().BoolVarP(&reconfigure, "reconfigure", "r", false, "Reconfigure all the acquisitions/collections")
|
||||
cmdWizard.AddCommand(cmdWizardDetect)
|
||||
|
||||
return cmdWizard
|
||||
}
|
Loading…
Reference in a new issue