github.com/crowdsecurity/crowdsec@v1.6.1/cmd/crowdsec-cli/capi.go (about) 1 package main 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/url" 8 "os" 9 10 "github.com/go-openapi/strfmt" 11 log "github.com/sirupsen/logrus" 12 "github.com/spf13/cobra" 13 "gopkg.in/yaml.v2" 14 15 "github.com/crowdsecurity/go-cs-lib/version" 16 17 "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" 18 "github.com/crowdsecurity/crowdsec/pkg/apiclient" 19 "github.com/crowdsecurity/crowdsec/pkg/csconfig" 20 "github.com/crowdsecurity/crowdsec/pkg/cwhub" 21 "github.com/crowdsecurity/crowdsec/pkg/models" 22 "github.com/crowdsecurity/crowdsec/pkg/types" 23 ) 24 25 const ( 26 CAPIBaseURL = "https://api.crowdsec.net/" 27 CAPIURLPrefix = "v3" 28 ) 29 30 type cliCapi struct { 31 cfg configGetter 32 } 33 34 func NewCLICapi(cfg configGetter) *cliCapi { 35 return &cliCapi{ 36 cfg: cfg, 37 } 38 } 39 40 func (cli *cliCapi) NewCommand() *cobra.Command { 41 cmd := &cobra.Command{ 42 Use: "capi [action]", 43 Short: "Manage interaction with Central API (CAPI)", 44 Args: cobra.MinimumNArgs(1), 45 DisableAutoGenTag: true, 46 PersistentPreRunE: func(_ *cobra.Command, _ []string) error { 47 cfg := cli.cfg() 48 if err := require.LAPI(cfg); err != nil { 49 return err 50 } 51 52 if err := require.CAPI(cfg); err != nil { 53 return err 54 } 55 56 return nil 57 }, 58 } 59 60 cmd.AddCommand(cli.newRegisterCmd()) 61 cmd.AddCommand(cli.newStatusCmd()) 62 63 return cmd 64 } 65 66 func (cli *cliCapi) register(capiUserPrefix string, outputFile string) error { 67 cfg := cli.cfg() 68 69 capiUser, err := generateID(capiUserPrefix) 70 if err != nil { 71 return fmt.Errorf("unable to generate machine id: %w", err) 72 } 73 74 password := strfmt.Password(generatePassword(passwordLength)) 75 76 apiurl, err := url.Parse(types.CAPIBaseURL) 77 if err != nil { 78 return fmt.Errorf("unable to parse api url %s: %w", types.CAPIBaseURL, err) 79 } 80 81 _, err = apiclient.RegisterClient(&apiclient.Config{ 82 MachineID: capiUser, 83 Password: password, 84 UserAgent: fmt.Sprintf("crowdsec/%s", version.String()), 85 URL: apiurl, 86 VersionPrefix: CAPIURLPrefix, 87 }, nil) 88 89 if err != nil { 90 return fmt.Errorf("api client register ('%s'): %w", types.CAPIBaseURL, err) 91 } 92 93 log.Infof("Successfully registered to Central API (CAPI)") 94 95 var dumpFile string 96 97 switch { 98 case outputFile != "": 99 dumpFile = outputFile 100 case cfg.API.Server.OnlineClient.CredentialsFilePath != "": 101 dumpFile = cfg.API.Server.OnlineClient.CredentialsFilePath 102 default: 103 dumpFile = "" 104 } 105 106 apiCfg := csconfig.ApiCredentialsCfg{ 107 Login: capiUser, 108 Password: password.String(), 109 URL: types.CAPIBaseURL, 110 } 111 112 apiConfigDump, err := yaml.Marshal(apiCfg) 113 if err != nil { 114 return fmt.Errorf("unable to marshal api credentials: %w", err) 115 } 116 117 if dumpFile != "" { 118 err = os.WriteFile(dumpFile, apiConfigDump, 0o600) 119 if err != nil { 120 return fmt.Errorf("write api credentials in '%s' failed: %w", dumpFile, err) 121 } 122 123 log.Infof("Central API credentials written to '%s'", dumpFile) 124 } else { 125 fmt.Println(string(apiConfigDump)) 126 } 127 128 log.Warning(ReloadMessage()) 129 130 return nil 131 } 132 133 func (cli *cliCapi) newRegisterCmd() *cobra.Command { 134 var ( 135 capiUserPrefix string 136 outputFile string 137 ) 138 139 cmd := &cobra.Command{ 140 Use: "register", 141 Short: "Register to Central API (CAPI)", 142 Args: cobra.MinimumNArgs(0), 143 DisableAutoGenTag: true, 144 RunE: func(_ *cobra.Command, _ []string) error { 145 return cli.register(capiUserPrefix, outputFile) 146 }, 147 } 148 149 cmd.Flags().StringVarP(&outputFile, "file", "f", "", "output file destination") 150 cmd.Flags().StringVar(&capiUserPrefix, "schmilblick", "", "set a schmilblick (use in tests only)") 151 152 if err := cmd.Flags().MarkHidden("schmilblick"); err != nil { 153 log.Fatalf("failed to hide flag: %s", err) 154 } 155 156 return cmd 157 } 158 159 func (cli *cliCapi) status() error { 160 cfg := cli.cfg() 161 162 if err := require.CAPIRegistered(cfg); err != nil { 163 return err 164 } 165 166 password := strfmt.Password(cfg.API.Server.OnlineClient.Credentials.Password) 167 168 apiurl, err := url.Parse(cfg.API.Server.OnlineClient.Credentials.URL) 169 if err != nil { 170 return fmt.Errorf("parsing api url ('%s'): %w", cfg.API.Server.OnlineClient.Credentials.URL, err) 171 } 172 173 hub, err := require.Hub(cfg, nil, nil) 174 if err != nil { 175 return err 176 } 177 178 scenarios, err := hub.GetInstalledItemNames(cwhub.SCENARIOS) 179 if err != nil { 180 return fmt.Errorf("failed to get scenarios: %w", err) 181 } 182 183 if len(scenarios) == 0 { 184 return errors.New("no scenarios installed, abort") 185 } 186 187 Client, err = apiclient.NewDefaultClient(apiurl, CAPIURLPrefix, fmt.Sprintf("crowdsec/%s", version.String()), nil) 188 if err != nil { 189 return fmt.Errorf("init default client: %w", err) 190 } 191 192 t := models.WatcherAuthRequest{ 193 MachineID: &cfg.API.Server.OnlineClient.Credentials.Login, 194 Password: &password, 195 Scenarios: scenarios, 196 } 197 198 log.Infof("Loaded credentials from %s", cfg.API.Server.OnlineClient.CredentialsFilePath) 199 log.Infof("Trying to authenticate with username %s on %s", cfg.API.Server.OnlineClient.Credentials.Login, apiurl) 200 201 _, _, err = Client.Auth.AuthenticateWatcher(context.Background(), t) 202 if err != nil { 203 return fmt.Errorf("failed to authenticate to Central API (CAPI): %w", err) 204 } 205 206 log.Info("You can successfully interact with Central API (CAPI)") 207 208 return nil 209 } 210 211 func (cli *cliCapi) newStatusCmd() *cobra.Command { 212 cmd := &cobra.Command{ 213 Use: "status", 214 Short: "Check status with the Central API (CAPI)", 215 Args: cobra.MinimumNArgs(0), 216 DisableAutoGenTag: true, 217 RunE: func(_ *cobra.Command, _ []string) error { 218 return cli.status() 219 }, 220 } 221 222 return cmd 223 }