add console enroll command to cscli (#828)

This commit is contained in:
blotus 2021-06-28 17:34:19 +02:00 committed by GitHub
parent b29730520f
commit 3994aec7fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 179 additions and 1 deletions

View file

@ -166,5 +166,6 @@ func NewCapiCmd() *cobra.Command {
},
}
cmdCapi.AddCommand(cmdCapiStatus)
return cmdCapi
}

View file

@ -0,0 +1,98 @@
package main
import (
"context"
"fmt"
"net/url"
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
"github.com/go-openapi/strfmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
func NewConsoleCmd() *cobra.Command {
var cmdConsole = &cobra.Command{
Use: "console [action]",
Short: "Manage interaction with Crowdsec console (https://app.crowdsec.net)",
Args: cobra.MinimumNArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
log.Fatal("Local API is disabled, please run this command on the local API machine")
}
if csConfig.API.Server.OnlineClient == nil {
log.Fatalf("no configuration for crowdsec API in '%s'", *csConfig.FilePath)
}
return nil
},
}
cmdEnroll := &cobra.Command{
Use: "enroll [enroll-key]",
Short: "Enroll this instance to https://app.crowdsec.net [requires local API]",
Long: `
Enroll this instance to https://app.crowdsec.net
You can get your enrollment key by creating an account on https://app.crowdsec.net.
After running this command your will need to validate the enrollment in the webapp.`,
Example: "cscli console enroll YOUR-ENROLL-KEY",
Args: cobra.ExactArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
log.Fatal("Local API is disabled, please run this command on the local API machine")
}
if csConfig.API.Server.OnlineClient == nil {
log.Fatalf("no configuration for crowdsec API in '%s'", *csConfig.FilePath)
}
if csConfig.API.Server.OnlineClient.Credentials == nil {
log.Fatal("You must configure CAPI with `cscli capi register` before enrolling your instance")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
apiURL, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
if err != nil {
log.Fatalf("Could not parse CAPI URL : %s", err)
}
if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to load hub index : %s", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
scenarios, err := cwhub.GetUpstreamInstalledScenariosAsString()
if err != nil {
log.Fatalf("failed to get scenarios : %s", err.Error())
}
if len(scenarios) == 0 {
scenarios = make([]string, 0)
}
c, _ := apiclient.NewClient(&apiclient.Config{
MachineID: csConfig.API.Server.OnlineClient.Credentials.Login,
Password: password,
Scenarios: scenarios,
UserAgent: fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
URL: apiURL,
VersionPrefix: "v2",
})
_, err = c.Auth.EnrollWatcher(context.Background(), args[0])
if err != nil {
log.Fatalf("Could not enroll instance: %s", err)
}
log.Infof("Watcher successfully enrolled. Visit https://app.crowdsec.net to accept it.")
},
}
cmdConsole.AddCommand(cmdEnroll)
return cmdConsole
}

View file

@ -78,7 +78,9 @@ func initConfig() {
}
var validArgs = []string{
"scenarios", "parsers", "collections", "capi", "lapi", "postoverflows", "machines", "metrics", "bouncers", "alerts", "decisions", "simulation", "hub", "dashboard", "config", "completion", "version",
"scenarios", "parsers", "collections", "capi", "lapi", "postoverflows", "machines",
"metrics", "bouncers", "alerts", "decisions", "simulation", "hub", "dashboard",
"config", "completion", "version", "console",
}
func main() {
@ -148,6 +150,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
rootCmd.AddCommand(NewCapiCmd())
rootCmd.AddCommand(NewLapiCmd())
rootCmd.AddCommand(NewCompletionCmd())
rootCmd.AddCommand(NewConsoleCmd())
if err := rootCmd.Execute(); err != nil {
log.Fatalf("While executing root command : %s", err)
}

View file

@ -11,6 +11,11 @@ import (
type AuthService service
// Don't add it to the models, as they are used with LAPI, but the enroll endpoint is specific to CAPI
type enrollRequest struct {
EnrollKey string `json:"attachment_key"`
}
func (s *AuthService) UnregisterWatcher(ctx context.Context) (*Response, error) {
u := fmt.Sprintf("%s/watchers", s.client.URLPrefix)
@ -55,3 +60,17 @@ func (s *AuthService) AuthenticateWatcher(ctx context.Context, auth models.Watch
}
return resp, nil
}
func (s *AuthService) EnrollWatcher(ctx context.Context, enrollKey string) (*Response, error) {
u := fmt.Sprintf("%s/watchers/enroll", s.client.URLPrefix)
req, err := s.client.NewRequest("POST", u, &enrollRequest{EnrollKey: enrollKey})
if err != nil {
return nil, err
}
resp, err := s.client.Do(ctx, req, nil)
if err != nil {
return resp, err
}
return resp, nil
}

View file

@ -179,3 +179,60 @@ func TestWatcherUnregister(t *testing.T) {
}
log.Printf("->%T", client)
}
func TestWatcherEnroll(t *testing.T) {
log.SetLevel(log.DebugLevel)
mux, urlx, teardown := setup()
defer teardown()
mux.HandleFunc("/watchers/enroll", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
buf := new(bytes.Buffer)
_, _ = buf.ReadFrom(r.Body)
newStr := buf.String()
log.Debugf("body -> %s", newStr)
if newStr == `{"attachment_key":"goodkey"}
` {
log.Print("good key")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"statusCode": 200, "message": "OK"}`)
} else {
log.Print("bad key")
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, `{"message":"the attachment key provided is not valid"}`)
}
})
mux.HandleFunc("/watchers/login", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"code":200,"expire":"2029-11-30T14:14:24+01:00","token":"toto"}`)
})
log.Printf("URL is %s", urlx)
apiURL, err := url.Parse(urlx + "/")
if err != nil {
log.Fatalf("parsing api url: %s", apiURL)
}
mycfg := &Config{
MachineID: "test_login",
Password: "test_password",
UserAgent: fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
URL: apiURL,
VersionPrefix: "v1",
Scenarios: []string{"crowdsecurity/test"},
}
client, err := NewClient(mycfg)
if err != nil {
log.Fatalf("new api client: %s", err.Error())
}
_, err = client.Auth.EnrollWatcher(context.Background(), "goodkey")
if err != nil {
t.Fatalf("unexpect auth err: %s", err)
}
_, err = client.Auth.EnrollWatcher(context.Background(), "badkey")
assert.Contains(t, err.Error(), "the attachment key provided is not valid")
}