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  }