add structure

This commit is contained in:
alteredCoder 2021-10-07 11:03:23 +02:00
parent 2c6a279b9b
commit 03dbce1e50
9 changed files with 104 additions and 112 deletions

View file

@ -41,6 +41,7 @@ api:
log_level: info log_level: info
listen_uri: 127.0.0.1:8080 listen_uri: 127.0.0.1:8080
profiles_path: /etc/crowdsec/profiles.yaml profiles_path: /etc/crowdsec/profiles.yaml
console_path: /etc/crowdsec/console_config.yaml
online_client: # Central API credentials (to push signals and receive bad IPs) online_client: # Central API credentials (to push signals and receive bad IPs)
credentials_path: /etc/crowdsec/online_api_credentials.yaml credentials_path: /etc/crowdsec/online_api_credentials.yaml
# tls: # tls:

View file

@ -31,19 +31,21 @@ const (
) )
type apic struct { type apic struct {
pullInterval time.Duration pullInterval time.Duration
pushInterval time.Duration pushInterval time.Duration
metricsInterval time.Duration metricsInterval time.Duration
dbClient *database.Client dbClient *database.Client
apiClient *apiclient.ApiClient apiClient *apiclient.ApiClient
alertToPush chan []*models.Alert alertToPush chan []*models.Alert
mu sync.Mutex mu sync.Mutex
pushTomb tomb.Tomb pushTomb tomb.Tomb
pullTomb tomb.Tomb pullTomb tomb.Tomb
metricsTomb tomb.Tomb metricsTomb tomb.Tomb
startup bool startup bool
credentials *csconfig.ApiCredentialsCfg credentials *csconfig.ApiCredentialsCfg
scenarioList []string scenarioList []string
consoleConfig *csconfig.ConsoleConfig
decisionsToDelete chan models.Decision
} }
func IsInSlice(a string, b []string) bool { func IsInSlice(a string, b []string) bool {
@ -75,7 +77,7 @@ func (a *apic) FetchScenariosListFromDB() ([]string, error) {
return scenarios, nil return scenarios, nil
} }
func AlertToSignal(alert *models.Alert) *models.AddSignalsRequestItem { func AlertToSignal(alert *models.Alert, scenarioTrust string, keepDecisions bool) *models.AddSignalsRequestItem {
return &models.AddSignalsRequestItem{ return &models.AddSignalsRequestItem{
Message: alert.Message, Message: alert.Message,
Scenario: alert.Scenario, Scenario: alert.Scenario,
@ -89,18 +91,20 @@ func AlertToSignal(alert *models.Alert) *models.AddSignalsRequestItem {
} }
} }
func NewAPIC(config *csconfig.OnlineApiClientCfg, dbClient *database.Client) (*apic, error) { func NewAPIC(config *csconfig.OnlineApiClientCfg, dbClient *database.Client, consoleConfig *csconfig.ConsoleConfig) (*apic, error) {
var err error var err error
ret := &apic{ ret := &apic{
alertToPush: make(chan []*models.Alert), alertToPush: make(chan []*models.Alert),
dbClient: dbClient, dbClient: dbClient,
mu: sync.Mutex{}, mu: sync.Mutex{},
startup: true, startup: true,
credentials: config.Credentials, credentials: config.Credentials,
pullTomb: tomb.Tomb{}, pullTomb: tomb.Tomb{},
pushTomb: tomb.Tomb{}, pushTomb: tomb.Tomb{},
metricsTomb: tomb.Tomb{}, metricsTomb: tomb.Tomb{},
scenarioList: make([]string, 0), scenarioList: make([]string, 0),
decisionsToDelete: make(chan models.Decision),
consoleConfig: consoleConfig,
} }
ret.pullInterval, err = time.ParseDuration(PullInterval) ret.pullInterval, err = time.ParseDuration(PullInterval)
@ -167,20 +171,42 @@ func (a *apic) Push() error {
case alerts := <-a.alertToPush: case alerts := <-a.alertToPush:
var signals []*models.AddSignalsRequestItem var signals []*models.AddSignalsRequestItem
for _, alert := range alerts { for _, alert := range alerts {
/*we're only interested into decisions coming from scenarios of the hub*/
if alert.ScenarioHash == nil || *alert.ScenarioHash == "" {
continue
}
/*and we're not interested into tainted scenarios neither*/
if alert.ScenarioVersion == nil || *alert.ScenarioVersion == "" || *alert.ScenarioVersion == "?" {
continue
}
/*we also ignore alerts in simulated mode*/
if *alert.Simulated { if *alert.Simulated {
log.Debugf("simulation enabled for alert (id:%d), will not be sent to CAPI", alert.ID) log.Debugf("simulation enabled for alert (id:%d), will not be sent to CAPI", alert.ID)
continue continue
} }
signals = append(signals, AlertToSignal(alert)) scenarioTrust := "certified"
if alert.ScenarioHash == nil || *alert.ScenarioHash == "" {
scenarioTrust = "custom"
}
if alert.ScenarioVersion == nil || *alert.ScenarioVersion == "" || *alert.ScenarioVersion == "?" {
scenarioTrust = "tainted"
}
if len(alert.Decisions) > 0 {
if *alert.Decisions[0].Origin == "cscli" {
scenarioTrust = "manual"
}
}
switch scenarioTrust {
case "manual":
if !*a.consoleConfig.ShareManualDecisions {
log.Debugf("manual decision generated an alert, doesn't send it to CAPI because options is disabled")
continue
}
case "tainted":
if !*a.consoleConfig.ShareTaintedScenarios {
log.Debugf("tainted scenario generated an alert, doesn't send it to CAPI because options is disabled")
continue
}
case "custom":
if !*a.consoleConfig.ShareCustomScenarios {
log.Debugf("custom scenario generated an alert, doesn't send it to CAPI because options is disabled")
continue
}
}
log.Infof("Add signals for '%s' alert", scenarioTrust)
signals = append(signals, AlertToSignal(alert, scenarioTrust, *a.consoleConfig.ShareDecisions))
} }
a.mu.Lock() a.mu.Lock()
cache = append(cache, signals...) cache = append(cache, signals...)

View file

@ -166,18 +166,19 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
}) })
router.Use(CustomRecoveryWithWriter()) router.Use(CustomRecoveryWithWriter())
controller := &controllers.Controller{ controller := &controllers.Controller{
DBClient: dbClient, DBClient: dbClient,
Ectx: context.Background(), Ectx: context.Background(),
Router: router, Router: router,
Profiles: config.Profiles, Profiles: config.Profiles,
Log: clog, Log: clog,
ConsoleConfig: config.ConsoleConfig,
} }
var apiClient *apic var apiClient *apic
if config.OnlineClient != nil && config.OnlineClient.Credentials != nil { if config.OnlineClient != nil && config.OnlineClient.Credentials != nil {
log.Printf("Loading CAPI pusher") log.Printf("Loading CAPI pusher")
apiClient, err = NewAPIC(config.OnlineClient, dbClient) apiClient, err = NewAPIC(config.OnlineClient, dbClient, config.ConsoleConfig)
if err != nil { if err != nil {
return &APIServer{}, err return &APIServer{}, err
} }

View file

@ -2,6 +2,8 @@ package controllers
import ( import (
"context" "context"
"net/http"
"github.com/alexliesenfeld/health" "github.com/alexliesenfeld/health"
v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1" v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1"
"github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/csconfig"
@ -10,17 +12,18 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/models" "github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http"
) )
type Controller struct { type Controller struct {
Ectx context.Context Ectx context.Context
DBClient *database.Client DBClient *database.Client
Router *gin.Engine Router *gin.Engine
Profiles []*csconfig.ProfileCfg Profiles []*csconfig.ProfileCfg
CAPIChan chan []*models.Alert CAPIChan chan []*models.Alert
PluginChannel chan csplugin.ProfileAlert PluginChannel chan csplugin.ProfileAlert
Log *log.Logger Log *log.Logger
ConsoleConfig *csconfig.ConsoleConfig
DeleteDecisionChannel chan models.Decision
} }
func (c *Controller) Init() error { func (c *Controller) Init() error {

View file

@ -128,34 +128,20 @@ func (c *Controller) CreateAlert(gctx *gin.Context) {
for _, alert := range input { for _, alert := range input {
alert.MachineID = machineID alert.MachineID = machineID
if len(alert.Decisions) != 0 {
for pIdx, profile := range c.Profiles {
_, matched, err := csprofiles.EvaluateProfile(profile, alert)
if err != nil {
gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}
if !matched {
continue
}
c.sendAlertToPluginChannel(alert, uint(pIdx))
if profile.OnSuccess == "break" {
break
}
}
continue
}
for pIdx, profile := range c.Profiles { for pIdx, profile := range c.Profiles {
profileDecisions, matched, err := csprofiles.EvaluateProfile(profile, alert) profileDecisions, matched, err := csprofiles.EvaluateProfile(profile, alert)
if err != nil { if err != nil {
gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return return
} }
if !matched { if !matched {
continue continue
} }
alert.Decisions = append(alert.Decisions, profileDecisions...)
if len(alert.Decisions) == 0 { // non manual decision
alert.Decisions = append(alert.Decisions, profileDecisions...)
}
profileAlert := *alert profileAlert := *alert
c.sendAlertToPluginChannel(&profileAlert, uint(pIdx)) c.sendAlertToPluginChannel(&profileAlert, uint(pIdx))
if profile.OnSuccess == "break" { if profile.OnSuccess == "break" {

View file

@ -18,6 +18,7 @@ type Controller struct {
Profiles []*csconfig.ProfileCfg Profiles []*csconfig.ProfileCfg
CAPIChan chan []*models.Alert CAPIChan chan []*models.Alert
PluginChannel chan csplugin.ProfileAlert PluginChannel chan csplugin.ProfileAlert
ConsoleConfig map[string]interface{}
} }
func New(dbClient *database.Client, ctx context.Context, profiles []*csconfig.ProfileCfg, capiChan chan []*models.Alert, pluginChannel chan csplugin.ProfileAlert) (*Controller, error) { func New(dbClient *database.Client, ctx context.Context, profiles []*csconfig.ProfileCfg, capiChan chan []*models.Alert, pluginChannel chan csplugin.ProfileAlert) (*Controller, error) {

View file

@ -3,7 +3,6 @@ package csconfig
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"strings" "strings"
"github.com/crowdsecurity/crowdsec/pkg/apiclient" "github.com/crowdsecurity/crowdsec/pkg/apiclient"
@ -79,18 +78,18 @@ func (l *LocalApiClientCfg) Load() error {
/*local api service configuration*/ /*local api service configuration*/
type LocalApiServerCfg struct { type LocalApiServerCfg struct {
ListenURI string `yaml:"listen_uri,omitempty"` //127.0.0.1:8080 ListenURI string `yaml:"listen_uri,omitempty"` //127.0.0.1:8080
TLS *TLSCfg `yaml:"tls"` TLS *TLSCfg `yaml:"tls"`
DbConfig *DatabaseCfg `yaml:"-"` DbConfig *DatabaseCfg `yaml:"-"`
LogDir string `yaml:"-"` LogDir string `yaml:"-"`
LogMedia string `yaml:"-"` LogMedia string `yaml:"-"`
OnlineClient *OnlineApiClientCfg `yaml:"online_client"` OnlineClient *OnlineApiClientCfg `yaml:"online_client"`
ProfilesPath string `yaml:"profiles_path,omitempty"` ProfilesPath string `yaml:"profiles_path,omitempty"`
ConsoleConfigPath string `yaml:"console_path,omitempty"` ConsoleConfigPath string `yaml:"console_path,omitempty"`
Profiles []*ProfileCfg `yaml:"-"` Profiles []*ProfileCfg `yaml:"-"`
LogLevel *log.Level `yaml:"log_level"` LogLevel *log.Level `yaml:"log_level"`
UseForwardedForHeaders bool `yaml:"use_forwarded_for_headers,omitempty"` UseForwardedForHeaders bool `yaml:"use_forwarded_for_headers,omitempty"`
ConsoleConfig map[string]interface{} `yaml:"-"` ConsoleConfig *ConsoleConfig `yaml:"-"`
} }
type TLSCfg struct { type TLSCfg struct {
@ -109,6 +108,10 @@ func (c *Config) LoadAPIServer() error {
return errors.Wrap(err, "while loading profiles for LAPI") return errors.Wrap(err, "while loading profiles for LAPI")
} }
if err := c.API.Server.LoadConsoleConfig(); err != nil {
return errors.Wrap(err, "while loading console options")
}
if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" { if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
if err := c.API.Server.OnlineClient.Load(); err != nil { if err := c.API.Server.OnlineClient.Load(); err != nil {
return errors.Wrap(err, "loading online client credentials") return errors.Wrap(err, "loading online client credentials")
@ -128,24 +131,6 @@ func (c *Config) LoadAPIServer() error {
return nil return nil
} }
func (c *LocalApiServerCfg) LoadConsoleConfig() error {
c.ConsoleConfig = make(map[string]interface{})
if _, err := os.Stat(c.ConsoleConfigPath); err != nil && os.IsNotExist(err) {
return nil
}
yamlFile, err := ioutil.ReadFile(c.ConsoleConfigPath)
if err != nil {
return fmt.Errorf("reading console config file '%s': %s", c.ConsoleConfigPath, err)
}
err = yaml.Unmarshal(yamlFile, c.ConsoleConfig)
if err != nil {
return fmt.Errorf("unmarshaling console config file '%s': %s", c.ConsoleConfigPath, err)
}
return nil
}
func (c *Config) LoadAPIClient() error { func (c *Config) LoadAPIClient() error {
if c.API != nil && c.API.Client != nil && c.API.Client.CredentialsFilePath != "" && !c.DisableAgent { if c.API != nil && c.API.Client != nil && c.API.Client.CredentialsFilePath != "" && !c.DisableAgent {
if err := c.API.Client.Load(); err != nil { if err := c.API.Client.Load(); err != nil {

View file

@ -1,14 +0,0 @@
package types
const (
SEND_CUSTOM_SCENARIOS = "custom"
SEND_TAINTED_SCENARIOS = "tainted"
SEND_MANUAL_SCENARIOS = "manual"
SEND_LIVE_DECISIONS = "live_decisions"
)
var CONSOLE_CONFIGS = []string{SEND_CUSTOM_SCENARIOS, SEND_LIVE_DECISIONS, SEND_MANUAL_SCENARIOS, SEND_TAINTED_SCENARIOS}
type ConsoleConfig struct {
ActivatedSharing []string
}

View file

@ -31,6 +31,8 @@ CSCLI_BIN="./cmd/crowdsec-cli/cscli"
CLIENT_SECRETS="local_api_credentials.yaml" CLIENT_SECRETS="local_api_credentials.yaml"
LAPI_SECRETS="online_api_credentials.yaml" LAPI_SECRETS="online_api_credentials.yaml"
CONSOLE_FILE="console_config.yaml"
BIN_INSTALL_PATH="/usr/local/bin" BIN_INSTALL_PATH="/usr/local/bin"
CROWDSEC_BIN_INSTALLED="${BIN_INSTALL_PATH}/crowdsec" CROWDSEC_BIN_INSTALLED="${BIN_INSTALL_PATH}/crowdsec"
@ -405,6 +407,7 @@ install_crowdsec() {
install -v -m 644 -D ./config/acquis.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit install -v -m 644 -D ./config/acquis.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/profiles.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit install -v -m 644 -D ./config/profiles.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/simulation.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit install -v -m 644 -D ./config/simulation.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/"${CONSOLE_FILE}" "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
mkdir -p ${PID_DIR} || exit mkdir -p ${PID_DIR} || exit
PID=${PID_DIR} DATA=${CROWDSEC_DATA_DIR} CFG=${CROWDSEC_CONFIG_PATH} envsubst '$CFG $PID $DATA' < ./config/user.yaml > ${CROWDSEC_CONFIG_PATH}"/user.yaml" || log_fatal "unable to generate user configuration file" PID=${PID_DIR} DATA=${CROWDSEC_DATA_DIR} CFG=${CROWDSEC_CONFIG_PATH} envsubst '$CFG $PID $DATA' < ./config/user.yaml > ${CROWDSEC_CONFIG_PATH}"/user.yaml" || log_fatal "unable to generate user configuration file"