123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- package main
- import (
- "encoding/json"
- "fmt"
- "io"
- "os"
- "path/filepath"
- log "github.com/sirupsen/logrus"
- "github.com/spf13/cobra"
- "gopkg.in/yaml.v2"
- "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
- "github.com/crowdsecurity/crowdsec/pkg/csconfig"
- "github.com/crowdsecurity/crowdsec/pkg/cwhub"
- )
- type OldAPICfg struct {
- MachineID string `json:"machine_id"`
- Password string `json:"password"`
- }
- // it's a rip of the cli version, but in silent-mode
- func silentInstallItem(name string, obtype string) (string, error) {
- hub, err := cwhub.GetHub()
- if err != nil {
- return "", err
- }
- var item = hub.GetItem(obtype, name)
- if item == nil {
- return "", fmt.Errorf("error retrieving item")
- }
- err = hub.DownloadLatest(item, false, false)
- if err != nil {
- return "", fmt.Errorf("error while downloading %s : %v", item.Name, err)
- }
- if err := hub.AddItem(*item); err != nil {
- return "", err
- }
- err = hub.EnableItem(item)
- if err != nil {
- return "", fmt.Errorf("error while enabling %s : %v", item.Name, err)
- }
- if err := hub.AddItem(*item); err != nil {
- return "", err
- }
- return fmt.Sprintf("Enabled %s", item.Name), nil
- }
- func restoreHub(dirPath string) error {
- var err error
- cwhub.SetHubBranch()
- for _, itype := range cwhub.ItemTypes {
- itemDirectory := fmt.Sprintf("%s/%s/", dirPath, itype)
- if _, err = os.Stat(itemDirectory); err != nil {
- log.Infof("no %s in backup", itype)
- continue
- }
- /*restore the upstream items*/
- upstreamListFN := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itype)
- file, err := os.ReadFile(upstreamListFN)
- if err != nil {
- return fmt.Errorf("error while opening %s : %s", upstreamListFN, err)
- }
- var upstreamList []string
- err = json.Unmarshal(file, &upstreamList)
- if err != nil {
- return fmt.Errorf("error unmarshaling %s : %s", upstreamListFN, err)
- }
- for _, toinstall := range upstreamList {
- label, err := silentInstallItem(toinstall, itype)
- if err != nil {
- log.Errorf("Error while installing %s : %s", toinstall, err)
- } else if label != "" {
- log.Infof("Installed %s : %s", toinstall, label)
- } else {
- log.Printf("Installed %s : ok", toinstall)
- }
- }
- /*restore the local and tainted items*/
- files, err := os.ReadDir(itemDirectory)
- if err != nil {
- return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory, err)
- }
- for _, file := range files {
- //this was the upstream data
- if file.Name() == fmt.Sprintf("upstream-%s.json", itype) {
- continue
- }
- if itype == cwhub.PARSERS || itype == cwhub.POSTOVERFLOWS {
- //we expect a stage here
- if !file.IsDir() {
- continue
- }
- stage := file.Name()
- stagedir := fmt.Sprintf("%s/%s/%s/", csConfig.ConfigPaths.ConfigDir, itype, stage)
- log.Debugf("Found stage %s in %s, target directory : %s", stage, itype, stagedir)
- if err = os.MkdirAll(stagedir, os.ModePerm); err != nil {
- return fmt.Errorf("error while creating stage directory %s : %s", stagedir, err)
- }
- /*find items*/
- ifiles, err := os.ReadDir(itemDirectory + "/" + stage + "/")
- if err != nil {
- return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory+"/"+stage, err)
- }
- //finally copy item
- for _, tfile := range ifiles {
- log.Infof("Going to restore local/tainted [%s]", tfile.Name())
- sourceFile := fmt.Sprintf("%s/%s/%s", itemDirectory, stage, tfile.Name())
- destinationFile := fmt.Sprintf("%s%s", stagedir, tfile.Name())
- if err = CopyFile(sourceFile, destinationFile); err != nil {
- return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
- }
- log.Infof("restored %s to %s", sourceFile, destinationFile)
- }
- } else {
- log.Infof("Going to restore local/tainted [%s]", file.Name())
- sourceFile := fmt.Sprintf("%s/%s", itemDirectory, file.Name())
- destinationFile := fmt.Sprintf("%s/%s/%s", csConfig.ConfigPaths.ConfigDir, itype, file.Name())
- if err = CopyFile(sourceFile, destinationFile); err != nil {
- return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
- }
- log.Infof("restored %s to %s", sourceFile, destinationFile)
- }
- }
- }
- return nil
- }
- /*
- Restore crowdsec configurations to directory <dirPath>:
- - Main config (config.yaml)
- - Profiles config (profiles.yaml)
- - Simulation config (simulation.yaml)
- - Backup of API credentials (local API and online API)
- - List of scenarios, parsers, postoverflows and collections that are up-to-date
- - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
- - Acquisition files (acquis.yaml, acquis.d/*.yaml)
- */
- func restoreConfigFromDirectory(dirPath string, oldBackup bool) error {
- var err error
- if !oldBackup {
- backupMain := fmt.Sprintf("%s/config.yaml", dirPath)
- if _, err = os.Stat(backupMain); err == nil {
- if csConfig.ConfigPaths != nil && csConfig.ConfigPaths.ConfigDir != "" {
- if err = CopyFile(backupMain, fmt.Sprintf("%s/config.yaml", csConfig.ConfigPaths.ConfigDir)); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", backupMain, csConfig.ConfigPaths.ConfigDir, err)
- }
- }
- }
- // Now we have config.yaml, we should regenerate config struct to have rights paths etc
- ConfigFilePath = fmt.Sprintf("%s/config.yaml", csConfig.ConfigPaths.ConfigDir)
- initConfig()
- backupCAPICreds := fmt.Sprintf("%s/online_api_credentials.yaml", dirPath)
- if _, err = os.Stat(backupCAPICreds); err == nil {
- if err = CopyFile(backupCAPICreds, csConfig.API.Server.OnlineClient.CredentialsFilePath); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", backupCAPICreds, csConfig.API.Server.OnlineClient.CredentialsFilePath, err)
- }
- }
- backupLAPICreds := fmt.Sprintf("%s/local_api_credentials.yaml", dirPath)
- if _, err = os.Stat(backupLAPICreds); err == nil {
- if err = CopyFile(backupLAPICreds, csConfig.API.Client.CredentialsFilePath); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", backupLAPICreds, csConfig.API.Client.CredentialsFilePath, err)
- }
- }
- backupProfiles := fmt.Sprintf("%s/profiles.yaml", dirPath)
- if _, err = os.Stat(backupProfiles); err == nil {
- if err = CopyFile(backupProfiles, csConfig.API.Server.ProfilesPath); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", backupProfiles, csConfig.API.Server.ProfilesPath, err)
- }
- }
- } else {
- var oldAPICfg OldAPICfg
- backupOldAPICfg := fmt.Sprintf("%s/api_creds.json", dirPath)
- jsonFile, err := os.Open(backupOldAPICfg)
- if err != nil {
- log.Warningf("failed to open %s : %s", backupOldAPICfg, err)
- } else {
- byteValue, _ := io.ReadAll(jsonFile)
- err = json.Unmarshal(byteValue, &oldAPICfg)
- if err != nil {
- return fmt.Errorf("failed to load json file %s : %s", backupOldAPICfg, err)
- }
- apiCfg := csconfig.ApiCredentialsCfg{
- Login: oldAPICfg.MachineID,
- Password: oldAPICfg.Password,
- URL: CAPIBaseURL,
- }
- apiConfigDump, err := yaml.Marshal(apiCfg)
- if err != nil {
- return fmt.Errorf("unable to dump api credentials: %s", err)
- }
- apiConfigDumpFile := fmt.Sprintf("%s/online_api_credentials.yaml", csConfig.ConfigPaths.ConfigDir)
- if csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.CredentialsFilePath != "" {
- apiConfigDumpFile = csConfig.API.Server.OnlineClient.CredentialsFilePath
- }
- err = os.WriteFile(apiConfigDumpFile, apiConfigDump, 0o644)
- if err != nil {
- return fmt.Errorf("write api credentials in '%s' failed: %s", apiConfigDumpFile, err)
- }
- log.Infof("Saved API credentials to %s", apiConfigDumpFile)
- }
- }
- backupSimulation := fmt.Sprintf("%s/simulation.yaml", dirPath)
- if _, err = os.Stat(backupSimulation); err == nil {
- if err = CopyFile(backupSimulation, csConfig.ConfigPaths.SimulationFilePath); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", backupSimulation, csConfig.ConfigPaths.SimulationFilePath, err)
- }
- }
- /*if there is a acquisition dir, restore its content*/
- if csConfig.Crowdsec.AcquisitionDirPath != "" {
- if err = os.MkdirAll(csConfig.Crowdsec.AcquisitionDirPath, 0o700); err != nil {
- return fmt.Errorf("error while creating %s : %s", csConfig.Crowdsec.AcquisitionDirPath, err)
- }
- }
- // if there was a single one
- backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
- if _, err = os.Stat(backupAcquisition); err == nil {
- log.Debugf("restoring backup'ed %s", backupAcquisition)
- if err = CopyFile(backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath, err)
- }
- }
- // if there is files in the acquis backup dir, restore them
- acquisBackupDir := filepath.Join(dirPath, "acquis", "*.yaml")
- if acquisFiles, err := filepath.Glob(acquisBackupDir); err == nil {
- for _, acquisFile := range acquisFiles {
- targetFname, err := filepath.Abs(csConfig.Crowdsec.AcquisitionDirPath + "/" + filepath.Base(acquisFile))
- if err != nil {
- return fmt.Errorf("while saving %s to %s: %w", acquisFile, targetFname, err)
- }
- log.Debugf("restoring %s to %s", acquisFile, targetFname)
- if err = CopyFile(acquisFile, targetFname); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
- }
- }
- }
- if csConfig.Crowdsec != nil && len(csConfig.Crowdsec.AcquisitionFiles) > 0 {
- for _, acquisFile := range csConfig.Crowdsec.AcquisitionFiles {
- log.Infof("backup filepath from dir -> %s", acquisFile)
- // if it was the default one, it has already been backed up
- if csConfig.Crowdsec.AcquisitionFilePath == acquisFile {
- log.Infof("skip this one")
- continue
- }
- targetFname, err := filepath.Abs(filepath.Join(acquisBackupDir, filepath.Base(acquisFile)))
- if err != nil {
- return fmt.Errorf("while saving %s to %s: %w", acquisFile, acquisBackupDir, err)
- }
- if err = CopyFile(acquisFile, targetFname); err != nil {
- return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
- }
- log.Infof("Saved acquis %s to %s", acquisFile, targetFname)
- }
- }
- if err = restoreHub(dirPath); err != nil {
- return fmt.Errorf("failed to restore hub config : %s", err)
- }
- return nil
- }
- func runConfigRestore(cmd *cobra.Command, args []string) error {
- flags := cmd.Flags()
- oldBackup, err := flags.GetBool("old-backup")
- if err != nil {
- return err
- }
- if _, err := require.Hub(csConfig); err != nil {
- return err
- }
- if err := restoreConfigFromDirectory(args[0], oldBackup); err != nil {
- return fmt.Errorf("failed to restore config from %s: %w", args[0], err)
- }
- return nil
- }
- func NewConfigRestoreCmd() *cobra.Command {
- cmdConfigRestore := &cobra.Command{
- Use: `restore "directory"`,
- Short: `Restore config in backup "directory"`,
- Long: `Restore the crowdsec configuration from specified backup "directory" including:
- - Main config (config.yaml)
- - Simulation config (simulation.yaml)
- - Profiles config (profiles.yaml)
- - List of scenarios, parsers, postoverflows and collections that are up-to-date
- - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
- - Backup of API credentials (local API and online API)`,
- Args: cobra.ExactArgs(1),
- DisableAutoGenTag: true,
- RunE: runConfigRestore,
- }
- flags := cmdConfigRestore.Flags()
- flags.BoolP("old-backup", "", false, "To use when you are upgrading crowdsec v0.X to v1.X and you need to restore backup from v0.X")
- return cmdConfigRestore
- }
|