github.com/crowdsecurity/crowdsec@v1.6.1/cmd/crowdsec-cli/config_backup.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	log "github.com/sirupsen/logrus"
    11  	"github.com/spf13/cobra"
    12  
    13  	"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
    14  	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
    15  )
    16  
    17  func (cli *cliConfig) backupHub(dirPath string) error {
    18  	hub, err := require.Hub(cli.cfg(), nil, nil)
    19  	if err != nil {
    20  		return err
    21  	}
    22  
    23  	for _, itemType := range cwhub.ItemTypes {
    24  		clog := log.WithFields(log.Fields{
    25  			"type": itemType,
    26  		})
    27  
    28  		itemMap := hub.GetItemMap(itemType)
    29  		if itemMap == nil {
    30  			clog.Infof("No %s to backup.", itemType)
    31  			continue
    32  		}
    33  
    34  		itemDirectory := fmt.Sprintf("%s/%s/", dirPath, itemType)
    35  		if err = os.MkdirAll(itemDirectory, os.ModePerm); err != nil {
    36  			return fmt.Errorf("error while creating %s: %w", itemDirectory, err)
    37  		}
    38  
    39  		upstreamParsers := []string{}
    40  
    41  		for k, v := range itemMap {
    42  			clog = clog.WithFields(log.Fields{
    43  				"file": v.Name,
    44  			})
    45  			if !v.State.Installed { // only backup installed ones
    46  				clog.Debugf("[%s]: not installed", k)
    47  				continue
    48  			}
    49  
    50  			// for the local/tainted ones, we back up the full file
    51  			if v.State.Tainted || v.State.IsLocal() || !v.State.UpToDate {
    52  				// we need to backup stages for parsers
    53  				if itemType == cwhub.PARSERS || itemType == cwhub.POSTOVERFLOWS {
    54  					fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
    55  					if err = os.MkdirAll(fstagedir, os.ModePerm); err != nil {
    56  						return fmt.Errorf("error while creating stage dir %s: %w", fstagedir, err)
    57  					}
    58  				}
    59  
    60  				clog.Debugf("[%s]: backing up file (tainted:%t local:%t up-to-date:%t)", k, v.State.Tainted, v.State.IsLocal(), v.State.UpToDate)
    61  
    62  				tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
    63  				if err = CopyFile(v.State.LocalPath, tfile); err != nil {
    64  					return fmt.Errorf("failed copy %s %s to %s: %w", itemType, v.State.LocalPath, tfile, err)
    65  				}
    66  
    67  				clog.Infof("local/tainted saved %s to %s", v.State.LocalPath, tfile)
    68  
    69  				continue
    70  			}
    71  
    72  			clog.Debugf("[%s]: from hub, just backup name (up-to-date:%t)", k, v.State.UpToDate)
    73  			clog.Infof("saving, version:%s, up-to-date:%t", v.Version, v.State.UpToDate)
    74  			upstreamParsers = append(upstreamParsers, v.Name)
    75  		}
    76  		// write the upstream items
    77  		upstreamParsersFname := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itemType)
    78  
    79  		upstreamParsersContent, err := json.MarshalIndent(upstreamParsers, "", " ")
    80  		if err != nil {
    81  			return fmt.Errorf("failed marshaling upstream parsers: %w", err)
    82  		}
    83  
    84  		err = os.WriteFile(upstreamParsersFname, upstreamParsersContent, 0o644)
    85  		if err != nil {
    86  			return fmt.Errorf("unable to write to %s %s: %w", itemType, upstreamParsersFname, err)
    87  		}
    88  
    89  		clog.Infof("Wrote %d entries for %s to %s", len(upstreamParsers), itemType, upstreamParsersFname)
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  /*
    96  	Backup crowdsec configurations to directory <dirPath>:
    97  
    98  - Main config (config.yaml)
    99  - Profiles config (profiles.yaml)
   100  - Simulation config (simulation.yaml)
   101  - Backup of API credentials (local API and online API)
   102  - List of scenarios, parsers, postoverflows and collections that are up-to-date
   103  - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
   104  - Acquisition files (acquis.yaml, acquis.d/*.yaml)
   105  */
   106  func (cli *cliConfig) backup(dirPath string) error {
   107  	var err error
   108  
   109  	cfg := cli.cfg()
   110  
   111  	if dirPath == "" {
   112  		return errors.New("directory path can't be empty")
   113  	}
   114  
   115  	log.Infof("Starting configuration backup")
   116  
   117  	/*if parent directory doesn't exist, bail out. create final dir with Mkdir*/
   118  	parentDir := filepath.Dir(dirPath)
   119  	if _, err = os.Stat(parentDir); err != nil {
   120  		return fmt.Errorf("while checking parent directory %s existence: %w", parentDir, err)
   121  	}
   122  
   123  	if err = os.Mkdir(dirPath, 0o700); err != nil {
   124  		return fmt.Errorf("while creating %s: %w", dirPath, err)
   125  	}
   126  
   127  	if cfg.ConfigPaths.SimulationFilePath != "" {
   128  		backupSimulation := filepath.Join(dirPath, "simulation.yaml")
   129  		if err = CopyFile(cfg.ConfigPaths.SimulationFilePath, backupSimulation); err != nil {
   130  			return fmt.Errorf("failed copy %s to %s: %w", cfg.ConfigPaths.SimulationFilePath, backupSimulation, err)
   131  		}
   132  
   133  		log.Infof("Saved simulation to %s", backupSimulation)
   134  	}
   135  
   136  	/*
   137  	   - backup AcquisitionFilePath
   138  	   - backup the other files of acquisition directory
   139  	*/
   140  	if cfg.Crowdsec != nil && cfg.Crowdsec.AcquisitionFilePath != "" {
   141  		backupAcquisition := filepath.Join(dirPath, "acquis.yaml")
   142  		if err = CopyFile(cfg.Crowdsec.AcquisitionFilePath, backupAcquisition); err != nil {
   143  			return fmt.Errorf("failed copy %s to %s: %w", cfg.Crowdsec.AcquisitionFilePath, backupAcquisition, err)
   144  		}
   145  	}
   146  
   147  	acquisBackupDir := filepath.Join(dirPath, "acquis")
   148  	if err = os.Mkdir(acquisBackupDir, 0o700); err != nil {
   149  		return fmt.Errorf("error while creating %s: %w", acquisBackupDir, err)
   150  	}
   151  
   152  	if cfg.Crowdsec != nil && len(cfg.Crowdsec.AcquisitionFiles) > 0 {
   153  		for _, acquisFile := range cfg.Crowdsec.AcquisitionFiles {
   154  			/*if it was the default one, it was already backup'ed*/
   155  			if cfg.Crowdsec.AcquisitionFilePath == acquisFile {
   156  				continue
   157  			}
   158  
   159  			targetFname, err := filepath.Abs(filepath.Join(acquisBackupDir, filepath.Base(acquisFile)))
   160  			if err != nil {
   161  				return fmt.Errorf("while saving %s to %s: %w", acquisFile, acquisBackupDir, err)
   162  			}
   163  
   164  			if err = CopyFile(acquisFile, targetFname); err != nil {
   165  				return fmt.Errorf("failed copy %s to %s: %w", acquisFile, targetFname, err)
   166  			}
   167  
   168  			log.Infof("Saved acquis %s to %s", acquisFile, targetFname)
   169  		}
   170  	}
   171  
   172  	if ConfigFilePath != "" {
   173  		backupMain := fmt.Sprintf("%s/config.yaml", dirPath)
   174  		if err = CopyFile(ConfigFilePath, backupMain); err != nil {
   175  			return fmt.Errorf("failed copy %s to %s: %w", ConfigFilePath, backupMain, err)
   176  		}
   177  
   178  		log.Infof("Saved default yaml to %s", backupMain)
   179  	}
   180  
   181  	if cfg.API != nil && cfg.API.Server != nil && cfg.API.Server.OnlineClient != nil && cfg.API.Server.OnlineClient.CredentialsFilePath != "" {
   182  		backupCAPICreds := fmt.Sprintf("%s/online_api_credentials.yaml", dirPath)
   183  		if err = CopyFile(cfg.API.Server.OnlineClient.CredentialsFilePath, backupCAPICreds); err != nil {
   184  			return fmt.Errorf("failed copy %s to %s: %w", cfg.API.Server.OnlineClient.CredentialsFilePath, backupCAPICreds, err)
   185  		}
   186  
   187  		log.Infof("Saved online API credentials to %s", backupCAPICreds)
   188  	}
   189  
   190  	if cfg.API != nil && cfg.API.Client != nil && cfg.API.Client.CredentialsFilePath != "" {
   191  		backupLAPICreds := fmt.Sprintf("%s/local_api_credentials.yaml", dirPath)
   192  		if err = CopyFile(cfg.API.Client.CredentialsFilePath, backupLAPICreds); err != nil {
   193  			return fmt.Errorf("failed copy %s to %s: %w", cfg.API.Client.CredentialsFilePath, backupLAPICreds, err)
   194  		}
   195  
   196  		log.Infof("Saved local API credentials to %s", backupLAPICreds)
   197  	}
   198  
   199  	if cfg.API != nil && cfg.API.Server != nil && cfg.API.Server.ProfilesPath != "" {
   200  		backupProfiles := fmt.Sprintf("%s/profiles.yaml", dirPath)
   201  		if err = CopyFile(cfg.API.Server.ProfilesPath, backupProfiles); err != nil {
   202  			return fmt.Errorf("failed copy %s to %s: %w", cfg.API.Server.ProfilesPath, backupProfiles, err)
   203  		}
   204  
   205  		log.Infof("Saved profiles to %s", backupProfiles)
   206  	}
   207  
   208  	if err = cli.backupHub(dirPath); err != nil {
   209  		return fmt.Errorf("failed to backup hub config: %w", err)
   210  	}
   211  
   212  	return nil
   213  }
   214  
   215  func (cli *cliConfig) newBackupCmd() *cobra.Command {
   216  	cmd := &cobra.Command{
   217  		Use:   `backup "directory"`,
   218  		Short: "Backup current config",
   219  		Long: `Backup the current crowdsec configuration including :
   220  
   221  - Main config (config.yaml)
   222  - Simulation config (simulation.yaml)
   223  - Profiles config (profiles.yaml)
   224  - List of scenarios, parsers, postoverflows and collections that are up-to-date
   225  - Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
   226  - Backup of API credentials (local API and online API)`,
   227  		Example:           `cscli config backup ./my-backup`,
   228  		Args:              cobra.ExactArgs(1),
   229  		DisableAutoGenTag: true,
   230  		RunE: func(_ *cobra.Command, args []string) error {
   231  			if err := cli.backup(args[0]); err != nil {
   232  				return fmt.Errorf("failed to backup config: %w", err)
   233  			}
   234  
   235  			return nil
   236  		},
   237  	}
   238  
   239  	return cmd
   240  }