main.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. _ "net/http/pprof"
  6. "os"
  7. "runtime"
  8. "strings"
  9. "time"
  10. "github.com/pkg/errors"
  11. log "github.com/sirupsen/logrus"
  12. "gopkg.in/tomb.v2"
  13. "github.com/crowdsecurity/crowdsec/pkg/acquisition"
  14. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  15. "github.com/crowdsecurity/crowdsec/pkg/csplugin"
  16. "github.com/crowdsecurity/crowdsec/pkg/cwhub"
  17. "github.com/crowdsecurity/crowdsec/pkg/cwversion"
  18. "github.com/crowdsecurity/crowdsec/pkg/fflag"
  19. "github.com/crowdsecurity/crowdsec/pkg/leakybucket"
  20. "github.com/crowdsecurity/crowdsec/pkg/parser"
  21. "github.com/crowdsecurity/crowdsec/pkg/types"
  22. )
  23. var (
  24. /*tombs for the parser, buckets and outputs.*/
  25. acquisTomb tomb.Tomb
  26. parsersTomb tomb.Tomb
  27. bucketsTomb tomb.Tomb
  28. outputsTomb tomb.Tomb
  29. apiTomb tomb.Tomb
  30. crowdsecTomb tomb.Tomb
  31. pluginTomb tomb.Tomb
  32. flags *Flags
  33. /*the state of acquisition*/
  34. dataSources []acquisition.DataSource
  35. /*the state of the buckets*/
  36. holders []leakybucket.BucketFactory
  37. buckets *leakybucket.Buckets
  38. inputLineChan chan types.Event
  39. inputEventChan chan types.Event
  40. outputEventChan chan types.Event // the buckets init returns its own chan that is used for multiplexing
  41. /*settings*/
  42. lastProcessedItem time.Time /*keep track of last item timestamp in time-machine. it is used to GC buckets when we dump them.*/
  43. pluginBroker csplugin.PluginBroker
  44. )
  45. type Flags struct {
  46. ConfigFile string
  47. LogLevelTrace bool
  48. LogLevelDebug bool
  49. LogLevelInfo bool
  50. LogLevelWarn bool
  51. LogLevelError bool
  52. LogLevelFatal bool
  53. PrintVersion bool
  54. SingleFileType string
  55. Labels map[string]string
  56. OneShotDSN string
  57. TestMode bool
  58. DisableAgent bool
  59. DisableAPI bool
  60. WinSvc string
  61. DisableCAPI bool
  62. Transform string
  63. OrderEvent bool
  64. }
  65. type labelsMap map[string]string
  66. func LoadBuckets(cConfig *csconfig.Config) error {
  67. var (
  68. err error
  69. files []string
  70. )
  71. for _, hubScenarioItem := range cwhub.GetItemMap(cwhub.SCENARIOS) {
  72. if hubScenarioItem.Installed {
  73. files = append(files, hubScenarioItem.LocalPath)
  74. }
  75. }
  76. buckets = leakybucket.NewBuckets()
  77. log.Infof("Loading %d scenario files", len(files))
  78. holders, outputEventChan, err = leakybucket.LoadBuckets(cConfig.Crowdsec, files, &bucketsTomb, buckets, flags.OrderEvent)
  79. if err != nil {
  80. return fmt.Errorf("scenario loading failed: %v", err)
  81. }
  82. if cConfig.Prometheus != nil && cConfig.Prometheus.Enabled {
  83. for holderIndex := range holders {
  84. holders[holderIndex].Profiling = true
  85. }
  86. }
  87. return nil
  88. }
  89. func LoadAcquisition(cConfig *csconfig.Config) error {
  90. var err error
  91. if flags.SingleFileType != "" && flags.OneShotDSN != "" {
  92. flags.Labels = labels
  93. flags.Labels["type"] = flags.SingleFileType
  94. dataSources, err = acquisition.LoadAcquisitionFromDSN(flags.OneShotDSN, flags.Labels, flags.Transform)
  95. if err != nil {
  96. return errors.Wrapf(err, "failed to configure datasource for %s", flags.OneShotDSN)
  97. }
  98. } else {
  99. dataSources, err = acquisition.LoadAcquisitionFromFile(cConfig.Crowdsec)
  100. if err != nil {
  101. return err
  102. }
  103. }
  104. if len(dataSources) == 0 {
  105. return fmt.Errorf("no datasource enabled")
  106. }
  107. return nil
  108. }
  109. var (
  110. dumpFolder string
  111. dumpStates bool
  112. labels = make(labelsMap)
  113. )
  114. func (l *labelsMap) String() string {
  115. return "labels"
  116. }
  117. func (l labelsMap) Set(label string) error {
  118. for _, pair := range strings.Split(label, ",") {
  119. split := strings.Split(pair, ":")
  120. if len(split) != 2 {
  121. return fmt.Errorf("invalid format for label '%s', must be key:value", pair)
  122. }
  123. l[split[0]] = split[1]
  124. }
  125. return nil
  126. }
  127. func (f *Flags) Parse() {
  128. flag.StringVar(&f.ConfigFile, "c", csconfig.DefaultConfigPath("config.yaml"), "configuration file")
  129. flag.BoolVar(&f.LogLevelTrace, "trace", false, "set log level to 'trace' (VERY verbose)")
  130. flag.BoolVar(&f.LogLevelDebug, "debug", false, "set log level to 'debug'")
  131. flag.BoolVar(&f.LogLevelInfo, "info", false, "set log level to 'info'")
  132. flag.BoolVar(&f.LogLevelWarn, "warning", false, "set log level to 'warning'")
  133. flag.BoolVar(&f.LogLevelError, "error", false, "set log level to 'error'")
  134. flag.BoolVar(&f.LogLevelFatal, "fatal", false, "set log level to 'fatal'")
  135. flag.BoolVar(&f.PrintVersion, "version", false, "display version")
  136. flag.StringVar(&f.OneShotDSN, "dsn", "", "Process a single data source in time-machine")
  137. flag.StringVar(&f.Transform, "transform", "", "expr to apply on the event after acquisition")
  138. flag.StringVar(&f.SingleFileType, "type", "", "Labels.type for file in time-machine")
  139. flag.Var(&labels, "label", "Additional Labels for file in time-machine")
  140. flag.BoolVar(&f.TestMode, "t", false, "only test configs")
  141. flag.BoolVar(&f.DisableAgent, "no-cs", false, "disable crowdsec agent")
  142. flag.BoolVar(&f.DisableAPI, "no-api", false, "disable local API")
  143. flag.BoolVar(&f.DisableCAPI, "no-capi", false, "disable communication with Central API")
  144. flag.BoolVar(&f.OrderEvent, "order-event", false, "enforce event ordering with significant performance cost")
  145. if runtime.GOOS == "windows" {
  146. flag.StringVar(&f.WinSvc, "winsvc", "", "Windows service Action: Install, Remove etc..")
  147. }
  148. flag.StringVar(&dumpFolder, "dump-data", "", "dump parsers/buckets raw outputs")
  149. flag.Parse()
  150. }
  151. func newLogLevel(curLevelPtr *log.Level, f *Flags) *log.Level {
  152. // mother of all defaults
  153. ret := log.InfoLevel
  154. // keep if already set
  155. if curLevelPtr != nil {
  156. ret = *curLevelPtr
  157. }
  158. // override from flags
  159. switch {
  160. case f.LogLevelTrace:
  161. ret = log.TraceLevel
  162. case f.LogLevelDebug:
  163. ret = log.DebugLevel
  164. case f.LogLevelInfo:
  165. ret = log.InfoLevel
  166. case f.LogLevelWarn:
  167. ret = log.WarnLevel
  168. case f.LogLevelError:
  169. ret = log.ErrorLevel
  170. case f.LogLevelFatal:
  171. ret = log.FatalLevel
  172. default:
  173. }
  174. if curLevelPtr != nil && ret == *curLevelPtr {
  175. // avoid returning a new ptr to the same value
  176. return curLevelPtr
  177. }
  178. return &ret
  179. }
  180. // LoadConfig returns a configuration parsed from configuration file
  181. func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet bool) (*csconfig.Config, error) {
  182. cConfig, _, err := csconfig.NewConfig(configFile, disableAgent, disableAPI, quiet)
  183. if err != nil {
  184. return nil, fmt.Errorf("while loading configuration file: %w", err)
  185. }
  186. cConfig.Common.LogLevel = newLogLevel(cConfig.Common.LogLevel, flags)
  187. if dumpFolder != "" {
  188. parser.ParseDump = true
  189. parser.DumpFolder = dumpFolder
  190. leakybucket.BucketPourTrack = true
  191. dumpStates = true
  192. }
  193. if flags.SingleFileType != "" && flags.OneShotDSN != "" {
  194. // if we're in time-machine mode, we don't want to log to file
  195. cConfig.Common.LogMedia = "stdout"
  196. }
  197. // Configure logging
  198. if err := types.SetDefaultLoggerConfig(cConfig.Common.LogMedia,
  199. cConfig.Common.LogDir, *cConfig.Common.LogLevel,
  200. cConfig.Common.LogMaxSize, cConfig.Common.LogMaxFiles,
  201. cConfig.Common.LogMaxAge, cConfig.Common.CompressLogs,
  202. cConfig.Common.ForceColorLogs); err != nil {
  203. return nil, err
  204. }
  205. if err := csconfig.LoadFeatureFlagsFile(configFile, log.StandardLogger()); err != nil {
  206. return nil, err
  207. }
  208. if !cConfig.DisableAgent {
  209. if err := cConfig.LoadCrowdsec(); err != nil {
  210. return nil, err
  211. }
  212. }
  213. if !cConfig.DisableAPI {
  214. if err := cConfig.LoadAPIServer(); err != nil {
  215. return nil, err
  216. }
  217. }
  218. if !cConfig.DisableAgent && (cConfig.API == nil || cConfig.API.Client == nil || cConfig.API.Client.Credentials == nil) {
  219. return nil, errors.New("missing local API credentials for crowdsec agent, abort")
  220. }
  221. if cConfig.DisableAPI && cConfig.DisableAgent {
  222. return nil, errors.New("You must run at least the API Server or crowdsec")
  223. }
  224. if flags.TestMode && !cConfig.DisableAgent {
  225. cConfig.Crowdsec.LintOnly = true
  226. }
  227. if flags.OneShotDSN != "" && flags.SingleFileType == "" {
  228. return nil, errors.New("-dsn requires a -type argument")
  229. }
  230. if flags.Transform != "" && flags.OneShotDSN == "" {
  231. return nil, errors.New("-transform requires a -dsn argument")
  232. }
  233. if flags.SingleFileType != "" && flags.OneShotDSN == "" {
  234. return nil, errors.New("-type requires a -dsn argument")
  235. }
  236. if flags.SingleFileType != "" && flags.OneShotDSN != "" {
  237. if cConfig.API != nil && cConfig.API.Server != nil {
  238. cConfig.API.Server.OnlineClient = nil
  239. }
  240. /*if the api is disabled as well, just read file and exit, don't daemonize*/
  241. if cConfig.DisableAPI {
  242. cConfig.Common.Daemonize = false
  243. }
  244. log.Infof("single file mode : log_media=%s daemonize=%t", cConfig.Common.LogMedia, cConfig.Common.Daemonize)
  245. }
  246. if cConfig.Common.PidDir != "" {
  247. log.Warn("Deprecation warning: the pid_dir config can be safely removed and is not required")
  248. }
  249. if cConfig.Common.Daemonize && runtime.GOOS == "windows" {
  250. log.Debug("Daemonization is not supported on Windows, disabling")
  251. cConfig.Common.Daemonize = false
  252. }
  253. // recap of the enabled feature flags, because logging
  254. // was not enabled when we set them from envvars
  255. if fflist := csconfig.ListFeatureFlags(); fflist != "" {
  256. log.Infof("Enabled feature flags: %s", fflist)
  257. }
  258. return cConfig, nil
  259. }
  260. // crowdsecT0 can be used to measure start time of services,
  261. // or uptime of the application
  262. var crowdsecT0 time.Time
  263. func main() {
  264. if err := fflag.RegisterAllFeatures(); err != nil {
  265. log.Fatalf("failed to register features: %s", err)
  266. }
  267. // some features can require configuration or command-line options,
  268. // so we need to parse them asap. we'll load from feature.yaml later.
  269. if err := csconfig.LoadFeatureFlagsEnv(log.StandardLogger()); err != nil {
  270. log.Fatalf("failed to set feature flags from environment: %s", err)
  271. }
  272. crowdsecT0 = time.Now()
  273. log.Debugf("os.Args: %v", os.Args)
  274. // Handle command line arguments
  275. flags = &Flags{}
  276. flags.Parse()
  277. if len(flag.Args()) > 0 {
  278. fmt.Fprintf(os.Stderr, "argument provided but not defined: %s\n", flag.Args()[0])
  279. flag.Usage()
  280. // the flag package exits with 2 in case of unknown flag
  281. os.Exit(2)
  282. }
  283. if flags.PrintVersion {
  284. cwversion.Show()
  285. os.Exit(0)
  286. }
  287. err := StartRunSvc()
  288. if err != nil {
  289. log.Fatal(err)
  290. }
  291. os.Exit(0)
  292. }