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 }