add structure
This commit is contained in:
parent
2c6a279b9b
commit
03dbce1e50
|
@ -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:
|
||||||
|
|
|
@ -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...)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue