config.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "github.com/pkg/errors"
  9. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  10. "github.com/crowdsecurity/crowdsec/pkg/cwhub"
  11. "github.com/crowdsecurity/crowdsec/pkg/types"
  12. log "github.com/sirupsen/logrus"
  13. "github.com/spf13/cobra"
  14. "gopkg.in/yaml.v2"
  15. )
  16. type OldAPICfg struct {
  17. MachineID string `json:"machine_id"`
  18. Password string `json:"password"`
  19. }
  20. /* Backup crowdsec configurations to directory <dirPath> :
  21. - Main config (config.yaml)
  22. - Profiles config (profiles.yaml)
  23. - Simulation config (simulation.yaml)
  24. - Backup of API credentials (local API and online API)
  25. - List of scenarios, parsers, postoverflows and collections that are up-to-date
  26. - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
  27. */
  28. func backupConfigToDirectory(dirPath string) error {
  29. var err error
  30. if dirPath == "" {
  31. return fmt.Errorf("directory path can't be empty")
  32. }
  33. log.Infof("Starting configuration backup")
  34. /*if parent directory doesn't exist, bail out. create final dir with Mkdir*/
  35. parentDir := filepath.Dir(dirPath)
  36. if _, err := os.Stat(parentDir); err != nil {
  37. return errors.Wrapf(err, "while checking parent directory %s existence", parentDir)
  38. }
  39. if err = os.Mkdir(dirPath, 0600); err != nil {
  40. return fmt.Errorf("error while creating %s : %s", dirPath, err)
  41. }
  42. if csConfig.ConfigPaths.SimulationFilePath != "" {
  43. backupSimulation := fmt.Sprintf("%s/simulation.yaml", dirPath)
  44. if err = types.CopyFile(csConfig.ConfigPaths.SimulationFilePath, backupSimulation); err != nil {
  45. return fmt.Errorf("failed copy %s to %s : %s", csConfig.ConfigPaths.SimulationFilePath, backupSimulation, err)
  46. }
  47. log.Infof("Saved simulation to %s", backupSimulation)
  48. }
  49. /*
  50. - backup AcquisitionFilePath
  51. - backup the other files of acquisition directory
  52. */
  53. if csConfig.Crowdsec != nil && csConfig.Crowdsec.AcquisitionFilePath != "" {
  54. backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
  55. if err = types.CopyFile(csConfig.Crowdsec.AcquisitionFilePath, backupAcquisition); err != nil {
  56. return fmt.Errorf("failed copy %s to %s : %s", csConfig.Crowdsec.AcquisitionFilePath, backupAcquisition, err)
  57. }
  58. }
  59. acquisBackupDir := dirPath + "/acquis/"
  60. if err = os.Mkdir(acquisBackupDir, 0600); err != nil {
  61. return fmt.Errorf("error while creating %s : %s", acquisBackupDir, err)
  62. }
  63. if csConfig.Crowdsec != nil && len(csConfig.Crowdsec.AcquisitionFiles) > 0 {
  64. for _, acquisFile := range csConfig.Crowdsec.AcquisitionFiles {
  65. /*if it was the default one, it was already backup'ed*/
  66. if csConfig.Crowdsec.AcquisitionFilePath == acquisFile {
  67. continue
  68. }
  69. targetFname, err := filepath.Abs(acquisBackupDir + filepath.Base(acquisFile))
  70. if err != nil {
  71. return errors.Wrapf(err, "while saving %s to %s", acquisFile, acquisBackupDir)
  72. }
  73. if err = types.CopyFile(acquisFile, targetFname); err != nil {
  74. return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
  75. }
  76. log.Infof("Saved acquis %s to %s", acquisFile, targetFname)
  77. }
  78. }
  79. if ConfigFilePath != "" {
  80. backupMain := fmt.Sprintf("%s/config.yaml", dirPath)
  81. if err = types.CopyFile(ConfigFilePath, backupMain); err != nil {
  82. return fmt.Errorf("failed copy %s to %s : %s", ConfigFilePath, backupMain, err)
  83. }
  84. log.Infof("Saved default yaml to %s", backupMain)
  85. }
  86. if csConfig.API != nil && csConfig.API.Server != nil && csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.CredentialsFilePath != "" {
  87. backupCAPICreds := fmt.Sprintf("%s/online_api_credentials.yaml", dirPath)
  88. if err = types.CopyFile(csConfig.API.Server.OnlineClient.CredentialsFilePath, backupCAPICreds); err != nil {
  89. return fmt.Errorf("failed copy %s to %s : %s", csConfig.API.Server.OnlineClient.CredentialsFilePath, backupCAPICreds, err)
  90. }
  91. log.Infof("Saved online API credentials to %s", backupCAPICreds)
  92. }
  93. if csConfig.API != nil && csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" {
  94. backupLAPICreds := fmt.Sprintf("%s/local_api_credentials.yaml", dirPath)
  95. if err = types.CopyFile(csConfig.API.Client.CredentialsFilePath, backupLAPICreds); err != nil {
  96. return fmt.Errorf("failed copy %s to %s : %s", csConfig.API.Client.CredentialsFilePath, backupLAPICreds, err)
  97. }
  98. log.Infof("Saved local API credentials to %s", backupLAPICreds)
  99. }
  100. if csConfig.API != nil && csConfig.API.Server != nil && csConfig.API.Server.ProfilesPath != "" {
  101. backupProfiles := fmt.Sprintf("%s/profiles.yaml", dirPath)
  102. if err = types.CopyFile(csConfig.API.Server.ProfilesPath, backupProfiles); err != nil {
  103. return fmt.Errorf("failed copy %s to %s : %s", csConfig.API.Server.ProfilesPath, backupProfiles, err)
  104. }
  105. log.Infof("Saved profiles to %s", backupProfiles)
  106. }
  107. if err = BackupHub(dirPath); err != nil {
  108. return fmt.Errorf("failed to backup hub config : %s", err)
  109. }
  110. return nil
  111. }
  112. /* Restore crowdsec configurations to directory <dirPath> :
  113. - Main config (config.yaml)
  114. - Profiles config (profiles.yaml)
  115. - Simulation config (simulation.yaml)
  116. - Backup of API credentials (local API and online API)
  117. - List of scenarios, parsers, postoverflows and collections that are up-to-date
  118. - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
  119. */
  120. func restoreConfigFromDirectory(dirPath string) error {
  121. var err error
  122. if !restoreOldBackup {
  123. backupMain := fmt.Sprintf("%s/config.yaml", dirPath)
  124. if _, err = os.Stat(backupMain); err == nil {
  125. if csConfig.ConfigPaths != nil && csConfig.ConfigPaths.ConfigDir != "" {
  126. if err = types.CopyFile(backupMain, fmt.Sprintf("%s/config.yaml", csConfig.ConfigPaths.ConfigDir)); err != nil {
  127. return fmt.Errorf("failed copy %s to %s : %s", backupMain, csConfig.ConfigPaths.ConfigDir, err)
  128. }
  129. }
  130. }
  131. // Now we have config.yaml, we should regenerate config struct to have rights paths etc
  132. ConfigFilePath = fmt.Sprintf("%s/config.yaml", csConfig.ConfigPaths.ConfigDir)
  133. initConfig()
  134. backupCAPICreds := fmt.Sprintf("%s/online_api_credentials.yaml", dirPath)
  135. if _, err = os.Stat(backupCAPICreds); err == nil {
  136. if err = types.CopyFile(backupCAPICreds, csConfig.API.Server.OnlineClient.CredentialsFilePath); err != nil {
  137. return fmt.Errorf("failed copy %s to %s : %s", backupCAPICreds, csConfig.API.Server.OnlineClient.CredentialsFilePath, err)
  138. }
  139. }
  140. backupLAPICreds := fmt.Sprintf("%s/local_api_credentials.yaml", dirPath)
  141. if _, err = os.Stat(backupLAPICreds); err == nil {
  142. if err = types.CopyFile(backupLAPICreds, csConfig.API.Client.CredentialsFilePath); err != nil {
  143. return fmt.Errorf("failed copy %s to %s : %s", backupLAPICreds, csConfig.API.Client.CredentialsFilePath, err)
  144. }
  145. }
  146. backupProfiles := fmt.Sprintf("%s/profiles.yaml", dirPath)
  147. if _, err = os.Stat(backupProfiles); err == nil {
  148. if err = types.CopyFile(backupProfiles, csConfig.API.Server.ProfilesPath); err != nil {
  149. return fmt.Errorf("failed copy %s to %s : %s", backupProfiles, csConfig.API.Server.ProfilesPath, err)
  150. }
  151. }
  152. } else {
  153. var oldAPICfg OldAPICfg
  154. backupOldAPICfg := fmt.Sprintf("%s/api_creds.json", dirPath)
  155. jsonFile, err := os.Open(backupOldAPICfg)
  156. if err != nil {
  157. log.Warningf("failed to open %s : %s", backupOldAPICfg, err)
  158. } else {
  159. byteValue, _ := ioutil.ReadAll(jsonFile)
  160. err = json.Unmarshal(byteValue, &oldAPICfg)
  161. if err != nil {
  162. return fmt.Errorf("failed to load json file %s : %s", backupOldAPICfg, err)
  163. }
  164. apiCfg := csconfig.ApiCredentialsCfg{
  165. Login: oldAPICfg.MachineID,
  166. Password: oldAPICfg.Password,
  167. URL: CAPIBaseURL,
  168. }
  169. apiConfigDump, err := yaml.Marshal(apiCfg)
  170. if err != nil {
  171. return fmt.Errorf("unable to dump api credentials: %s", err)
  172. }
  173. apiConfigDumpFile := fmt.Sprintf("%s/online_api_credentials.yaml", csConfig.ConfigPaths.ConfigDir)
  174. if csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.CredentialsFilePath != "" {
  175. apiConfigDumpFile = csConfig.API.Server.OnlineClient.CredentialsFilePath
  176. }
  177. err = ioutil.WriteFile(apiConfigDumpFile, apiConfigDump, 0644)
  178. if err != nil {
  179. return fmt.Errorf("write api credentials in '%s' failed: %s", apiConfigDumpFile, err)
  180. }
  181. log.Infof("Saved API credentials to %s", apiConfigDumpFile)
  182. }
  183. }
  184. backupSimulation := fmt.Sprintf("%s/simulation.yaml", dirPath)
  185. if _, err = os.Stat(backupSimulation); err == nil {
  186. if err = types.CopyFile(backupSimulation, csConfig.ConfigPaths.SimulationFilePath); err != nil {
  187. return fmt.Errorf("failed copy %s to %s : %s", backupSimulation, csConfig.ConfigPaths.SimulationFilePath, err)
  188. }
  189. }
  190. /*if there is a acquisition dir, restore its content*/
  191. if csConfig.Crowdsec.AcquisitionDirPath != "" {
  192. if err = os.Mkdir(csConfig.Crowdsec.AcquisitionDirPath, 0600); err != nil {
  193. return fmt.Errorf("error while creating %s : %s", csConfig.Crowdsec.AcquisitionDirPath, err)
  194. }
  195. }
  196. //if there was a single one
  197. backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
  198. if _, err = os.Stat(backupAcquisition); err == nil {
  199. log.Debugf("restoring backup'ed %s", backupAcquisition)
  200. if err = types.CopyFile(backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath); err != nil {
  201. return fmt.Errorf("failed copy %s to %s : %s", backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath, err)
  202. }
  203. }
  204. //if there is files in the acquis backup dir, restore them
  205. acquisBackupDir := dirPath + "/acquis/*.yaml"
  206. if acquisFiles, err := filepath.Glob(acquisBackupDir); err == nil {
  207. for _, acquisFile := range acquisFiles {
  208. targetFname, err := filepath.Abs(csConfig.Crowdsec.AcquisitionDirPath + "/" + filepath.Base(acquisFile))
  209. if err != nil {
  210. return errors.Wrapf(err, "while saving %s to %s", acquisFile, targetFname)
  211. }
  212. log.Debugf("restoring %s to %s", acquisFile, targetFname)
  213. if err = types.CopyFile(acquisFile, targetFname); err != nil {
  214. return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
  215. }
  216. }
  217. }
  218. if csConfig.Crowdsec != nil && len(csConfig.Crowdsec.AcquisitionFiles) > 0 {
  219. for _, acquisFile := range csConfig.Crowdsec.AcquisitionFiles {
  220. log.Infof("backup filepath from dir -> %s", acquisFile)
  221. /*if it was the default one, it was already backup'ed*/
  222. if csConfig.Crowdsec.AcquisitionFilePath == acquisFile {
  223. log.Infof("skip this one")
  224. continue
  225. }
  226. targetFname, err := filepath.Abs(acquisBackupDir + filepath.Base(acquisFile))
  227. if err != nil {
  228. return errors.Wrapf(err, "while saving %s to %s", acquisFile, acquisBackupDir)
  229. }
  230. if err = types.CopyFile(acquisFile, targetFname); err != nil {
  231. return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
  232. }
  233. log.Infof("Saved acquis %s to %s", acquisFile, targetFname)
  234. }
  235. }
  236. if err = RestoreHub(dirPath); err != nil {
  237. return fmt.Errorf("failed to restore hub config : %s", err)
  238. }
  239. return nil
  240. }
  241. func NewConfigCmd() *cobra.Command {
  242. var cmdConfig = &cobra.Command{
  243. Use: "config [command]",
  244. Short: "Allows to view current config",
  245. Args: cobra.ExactArgs(0),
  246. }
  247. var cmdConfigShow = &cobra.Command{
  248. Use: "show",
  249. Short: "Displays current config",
  250. Long: `Displays the current cli configuration.`,
  251. Args: cobra.ExactArgs(0),
  252. Run: func(cmd *cobra.Command, args []string) {
  253. switch csConfig.Cscli.Output {
  254. case "human":
  255. fmt.Printf("Global:\n")
  256. if csConfig.ConfigPaths != nil {
  257. fmt.Printf(" - Configuration Folder : %s\n", csConfig.ConfigPaths.ConfigDir)
  258. fmt.Printf(" - Data Folder : %s\n", csConfig.ConfigPaths.DataDir)
  259. fmt.Printf(" - Hub Folder : %s\n", csConfig.ConfigPaths.HubDir)
  260. fmt.Printf(" - Simulation File : %s\n", csConfig.ConfigPaths.SimulationFilePath)
  261. }
  262. if csConfig.Common != nil {
  263. fmt.Printf(" - Log Folder : %s\n", csConfig.Common.LogDir)
  264. fmt.Printf(" - Log level : %s\n", csConfig.Common.LogLevel)
  265. fmt.Printf(" - Log Media : %s\n", csConfig.Common.LogMedia)
  266. }
  267. if csConfig.Crowdsec != nil {
  268. fmt.Printf("Crowdsec:\n")
  269. fmt.Printf(" - Acquisition File : %s\n", csConfig.Crowdsec.AcquisitionFilePath)
  270. fmt.Printf(" - Parsers routines : %d\n", csConfig.Crowdsec.ParserRoutinesCount)
  271. }
  272. if csConfig.Cscli != nil {
  273. fmt.Printf("cscli:\n")
  274. fmt.Printf(" - Output : %s\n", csConfig.Cscli.Output)
  275. fmt.Printf(" - Hub Branch : %s\n", csConfig.Cscli.HubBranch)
  276. fmt.Printf(" - Hub Folder : %s\n", csConfig.Cscli.HubDir)
  277. }
  278. if csConfig.API != nil {
  279. if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil {
  280. fmt.Printf("API Client:\n")
  281. fmt.Printf(" - URL : %s\n", csConfig.API.Client.Credentials.URL)
  282. fmt.Printf(" - Login : %s\n", csConfig.API.Client.Credentials.Login)
  283. fmt.Printf(" - Credentials File : %s\n", csConfig.API.Client.CredentialsFilePath)
  284. }
  285. if csConfig.API.Server != nil {
  286. fmt.Printf("Local API Server:\n")
  287. fmt.Printf(" - Listen URL : %s\n", csConfig.API.Server.ListenURI)
  288. fmt.Printf(" - Profile File : %s\n", csConfig.API.Server.ProfilesPath)
  289. if csConfig.API.Server.TLS != nil {
  290. if csConfig.API.Server.TLS.CertFilePath != "" {
  291. fmt.Printf(" - Cert File : %s\n", csConfig.API.Server.TLS.CertFilePath)
  292. }
  293. if csConfig.API.Server.TLS.KeyFilePath != "" {
  294. fmt.Printf(" - Key File : %s\n", csConfig.API.Server.TLS.KeyFilePath)
  295. }
  296. }
  297. if csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.Credentials != nil {
  298. fmt.Printf("Central API:\n")
  299. fmt.Printf(" - URL : %s\n", csConfig.API.Server.OnlineClient.Credentials.URL)
  300. fmt.Printf(" - Login : %s\n", csConfig.API.Server.OnlineClient.Credentials.Login)
  301. fmt.Printf(" - Credentials File : %s\n", csConfig.API.Server.OnlineClient.CredentialsFilePath)
  302. }
  303. }
  304. }
  305. if csConfig.DbConfig != nil {
  306. fmt.Printf(" - Database:\n")
  307. fmt.Printf(" - Type : %s\n", csConfig.DbConfig.Type)
  308. switch csConfig.DbConfig.Type {
  309. case "sqlite":
  310. fmt.Printf(" - Path : %s\n", csConfig.DbConfig.DbPath)
  311. case "mysql", "postgresql", "postgres":
  312. fmt.Printf(" - Host : %s\n", csConfig.DbConfig.Host)
  313. fmt.Printf(" - Port : %d\n", csConfig.DbConfig.Port)
  314. fmt.Printf(" - User : %s\n", csConfig.DbConfig.User)
  315. fmt.Printf(" - DB Name : %s\n", csConfig.DbConfig.DbName)
  316. }
  317. if csConfig.DbConfig.Flush != nil {
  318. if *csConfig.DbConfig.Flush.MaxAge != "" {
  319. fmt.Printf(" - Flush age : %s\n", *csConfig.DbConfig.Flush.MaxAge)
  320. }
  321. if *csConfig.DbConfig.Flush.MaxItems != 0 {
  322. fmt.Printf(" - Flush size : %d\n", *csConfig.DbConfig.Flush.MaxItems)
  323. }
  324. }
  325. }
  326. case "json":
  327. data, err := json.MarshalIndent(csConfig, "", " ")
  328. if err != nil {
  329. log.Fatalf("failed to marshal configuration: %s", err)
  330. }
  331. fmt.Printf("%s\n", string(data))
  332. case "raw":
  333. data, err := yaml.Marshal(csConfig)
  334. if err != nil {
  335. log.Fatalf("failed to marshal configuration: %s", err)
  336. }
  337. fmt.Printf("%s\n", string(data))
  338. }
  339. },
  340. }
  341. cmdConfig.AddCommand(cmdConfigShow)
  342. var cmdConfigBackup = &cobra.Command{
  343. Use: "backup <directory>",
  344. Short: "Backup current config",
  345. Long: `Backup the current crowdsec configuration including :
  346. - Main config (config.yaml)
  347. - Simulation config (simulation.yaml)
  348. - Profiles config (profiles.yaml)
  349. - List of scenarios, parsers, postoverflows and collections that are up-to-date
  350. - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
  351. - Backup of API credentials (local API and online API)`,
  352. Example: `cscli config backup ./my-backup`,
  353. Args: cobra.ExactArgs(1),
  354. Run: func(cmd *cobra.Command, args []string) {
  355. var err error
  356. if err = cwhub.GetHubIdx(csConfig.Cscli); err != nil {
  357. log.Fatalf("Failed to get Hub index : %v", err)
  358. log.Infoln("Run 'sudo cscli hub update' to get the hub index")
  359. }
  360. if err = backupConfigToDirectory(args[0]); err != nil {
  361. log.Fatalf("Failed to backup configurations: %s", err)
  362. }
  363. },
  364. }
  365. cmdConfig.AddCommand(cmdConfigBackup)
  366. var cmdConfigRestore = &cobra.Command{
  367. Use: "restore <directory>",
  368. Short: "Restore config in backup <directory>",
  369. Long: `Restore the crowdsec configuration from specified backup <directory> including:
  370. - Main config (config.yaml)
  371. - Simulation config (simulation.yaml)
  372. - Profiles config (profiles.yaml)
  373. - List of scenarios, parsers, postoverflows and collections that are up-to-date
  374. - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
  375. - Backup of API credentials (local API and online API)`,
  376. Args: cobra.ExactArgs(1),
  377. Run: func(cmd *cobra.Command, args []string) {
  378. var err error
  379. if err = cwhub.GetHubIdx(csConfig.Cscli); err != nil {
  380. log.Fatalf("Failed to get Hub index : %v", err)
  381. log.Infoln("Run 'sudo cscli hub update' to get the hub index")
  382. }
  383. if err := restoreConfigFromDirectory(args[0]); err != nil {
  384. log.Fatalf("failed restoring configurations from %s : %s", args[0], err)
  385. }
  386. },
  387. }
  388. cmdConfigRestore.PersistentFlags().BoolVar(&restoreOldBackup, "old-backup", false, "To use when you are upgrading crowdsec v0.X to v1.X and you need to restore backup from v0.X")
  389. cmdConfig.AddCommand(cmdConfigRestore)
  390. return cmdConfig
  391. }