Compare commits

...

1 commit

Author SHA1 Message Date
alteredCoder
c982588bca check if machine is enrolled in cscli console status 2022-09-13 13:01:48 +02:00
5 changed files with 108 additions and 43 deletions

View file

@ -1,16 +1,13 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
"github.com/crowdsecurity/crowdsec/pkg/apiclient" "github.com/crowdsecurity/crowdsec/pkg/apiclient"
"github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
"github.com/crowdsecurity/crowdsec/pkg/cwversion" "github.com/crowdsecurity/crowdsec/pkg/cwversion"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/go-openapi/strfmt" "github.com/go-openapi/strfmt"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -126,42 +123,12 @@ func NewCapiCmd() *cobra.Command {
log.Fatalf("no credentials for Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath) log.Fatalf("no credentials for Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath)
} }
password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
apiurl, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
if err != nil {
log.Fatalf("parsing api url ('%s'): %s", csConfig.API.Server.OnlineClient.Credentials.URL, err)
}
if err := csConfig.LoadHub(); err != nil {
log.Fatal(err)
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Info("Run 'sudo cscli hub update' to get the hub index")
log.Fatalf("Failed to load hub index : %s", err)
}
scenarios, err := cwhub.GetInstalledScenariosAsString()
if err != nil {
log.Fatalf("failed to get scenarios : %s", err)
}
if len(scenarios) == 0 {
log.Fatalf("no scenarios installed, abort")
}
Client, err = apiclient.NewDefaultClient(apiurl, CAPIURLPrefix, fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()), nil)
if err != nil {
log.Fatalf("init default client: %s", err)
}
t := models.WatcherAuthRequest{
MachineID: &csConfig.API.Server.OnlineClient.Credentials.Login,
Password: &password,
Scenarios: scenarios,
}
log.Infof("Loaded credentials from %s", csConfig.API.Server.OnlineClient.CredentialsFilePath) log.Infof("Loaded credentials from %s", csConfig.API.Server.OnlineClient.CredentialsFilePath)
log.Infof("Trying to authenticate with username %s on %s", csConfig.API.Server.OnlineClient.Credentials.Login, apiurl) log.Infof("Trying to authenticate with username %s on %s", csConfig.API.Server.OnlineClient.Credentials.Login, csConfig.API.Server.OnlineClient.Credentials.URL)
_, err = Client.Auth.AuthenticateWatcher(context.Background(), t)
_, err = CapiAuth(csConfig.API.Server.OnlineClient)
if err != nil { if err != nil {
log.Fatalf("Failed to authenticate to Central API (CAPI) : %s", err) log.Fatalf("unable to connect to CrowdSec Central API: %s", err)
} }
log.Infof("You can successfully interact with Central API (CAPI)") log.Infof("You can successfully interact with Central API (CAPI)")
}, },

View file

@ -17,11 +17,27 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/types" "github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/enescakir/emoji" "github.com/enescakir/emoji"
"github.com/go-openapi/strfmt" "github.com/go-openapi/strfmt"
"github.com/golang-jwt/jwt/v4"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func isEnrolled(client *apiclient.ApiClient) bool {
apiHTTPClient := client.GetClient()
jwtTransport := apiHTTPClient.Transport.(*apiclient.JWTTransport)
tokenStr := jwtTransport.Token
token, _ := jwt.Parse(tokenStr, nil)
if token == nil {
return false
}
claims := token.Claims.(jwt.MapClaims)
_, ok := claims["organization_id"]
return ok
}
func NewConsoleCmd() *cobra.Command { func NewConsoleCmd() *cobra.Command {
var cmdConsole = &cobra.Command{ var cmdConsole = &cobra.Command{
Use: "console [action]", Use: "console [action]",
@ -192,6 +208,32 @@ Disable given information push to the central API.`,
Example: "status tainted", Example: "status tainted",
DisableAutoGenTag: true, DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var err error
if csConfig.API.Server == nil {
log.Fatalln("There is no configuration on 'api.server:'")
}
if csConfig.API.Server.OnlineClient == nil {
log.Fatalf("Please provide credentials for the Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath)
}
if csConfig.API.Server.OnlineClient.Credentials == nil {
log.Fatalf("no credentials for Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath)
}
log.Debugf("Loaded credentials from %s", csConfig.API.Server.OnlineClient.CredentialsFilePath)
log.Debugf("Trying to authenticate with username %s on %s", csConfig.API.Server.OnlineClient.Credentials.Login, csConfig.API.Server.OnlineClient.Credentials.URL)
client, err := CapiAuth(csConfig.API.Server.OnlineClient)
if err != nil {
log.Fatalf("unable to connect to CrowdSec Central API: %s", err)
}
if isEnrolled(client) {
fmt.Printf("Machine '%s' is enrolled in the console", csConfig.API.Server.OnlineClient.Credentials.Login)
} else {
fmt.Printf("Machine '%s' is not enrolled in the console", csConfig.API.Server.OnlineClient.Credentials.Login)
}
switch csConfig.Cscli.Output { switch csConfig.Cscli.Output {
case "human": case "human":
table := tablewriter.NewWriter(os.Stdout) table := tablewriter.NewWriter(os.Stdout)

View file

@ -2,17 +2,25 @@ package main
import ( import (
"bytes" "bytes"
"context"
"encoding/csv" "encoding/csv"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math" "math"
"net" "net"
"net/http" "net/http"
"net/url"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/go-openapi/strfmt"
"github.com/crowdsecurity/crowdsec/pkg/cwhub" "github.com/crowdsecurity/crowdsec/pkg/cwhub"
"github.com/crowdsecurity/crowdsec/pkg/types" "github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/enescakir/emoji" "github.com/enescakir/emoji"
@ -776,3 +784,47 @@ func formatNumber(num int) string {
res := math.Round(float64(num)/float64(goodUnit.value)*100) / 100 res := math.Round(float64(num)/float64(goodUnit.value)*100) / 100
return fmt.Sprintf("%.2f%s", res, goodUnit.symbol) return fmt.Sprintf("%.2f%s", res, goodUnit.symbol)
} }
func CapiAuth(capiConfig *csconfig.OnlineApiClientCfg) (*apiclient.ApiClient, error) {
var err error
var client *apiclient.ApiClient
password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
apiurl, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
if err != nil {
return client, fmt.Errorf("parsing api url ('%s'): %s", csConfig.API.Server.OnlineClient.Credentials.URL, err)
}
if err := csConfig.LoadHub(); err != nil {
log.Fatal(err)
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Info("Run 'sudo cscli hub update' to get the hub index")
return client, fmt.Errorf("Failed to load hub index : %s", err)
}
scenarios, err := cwhub.GetInstalledScenariosAsString()
if err != nil {
return client, fmt.Errorf("failed to get scenarios : %s", err)
}
if len(scenarios) == 0 {
return client, fmt.Errorf("no scenarios installed, abort")
}
client, err = apiclient.NewDefaultClient(apiurl, CAPIURLPrefix, fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()), nil)
if err != nil {
return client, fmt.Errorf("init default client: %s", err)
}
t := models.WatcherAuthRequest{
MachineID: &csConfig.API.Server.OnlineClient.Credentials.Login,
Password: &password,
Scenarios: scenarios,
}
_, err = client.Auth.AuthenticateWatcher(context.Background(), t)
if err != nil {
return client, fmt.Errorf("Failed to authenticate to Central API (CAPI) : %s", err)
}
return client, nil
}

View file

@ -78,7 +78,7 @@ func (t *APIKeyTransport) transport() http.RoundTripper {
type JWTTransport struct { type JWTTransport struct {
MachineID *string MachineID *string
Password *strfmt.Password Password *strfmt.Password
token string Token string
Expiration time.Time Expiration time.Time
Scenarios []string Scenarios []string
URL *url.URL URL *url.URL
@ -161,15 +161,15 @@ func (t *JWTTransport) refreshJwtToken() error {
if err := t.Expiration.UnmarshalText([]byte(response.Expire)); err != nil { if err := t.Expiration.UnmarshalText([]byte(response.Expire)); err != nil {
return errors.Wrap(err, "unable to parse jwt expiration") return errors.Wrap(err, "unable to parse jwt expiration")
} }
t.token = response.Token t.Token = response.Token
log.Debugf("token %s will expire on %s", t.token, t.Expiration.String()) log.Debugf("token %s will expire on %s", t.Token, t.Expiration.String())
return nil return nil
} }
// RoundTrip implements the RoundTripper interface. // RoundTrip implements the RoundTripper interface.
func (t *JWTTransport) RoundTrip(req *http.Request) (*http.Response, error) { func (t *JWTTransport) RoundTrip(req *http.Request) (*http.Response, error) {
if t.token == "" || t.Expiration.Add(-time.Minute).Before(time.Now().UTC()) { if t.Token == "" || t.Expiration.Add(-time.Minute).Before(time.Now().UTC()) {
if err := t.refreshJwtToken(); err != nil { if err := t.refreshJwtToken(); err != nil {
return nil, err return nil, err
} }
@ -179,7 +179,7 @@ func (t *JWTTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// that we don't modify the Request we were given. This is required by the // that we don't modify the Request we were given. This is required by the
// specification of http.RoundTripper. // specification of http.RoundTripper.
req = cloneRequest(req) req = cloneRequest(req)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.token)) req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.Token))
log.Debugf("req-jwt: %s %s", req.Method, req.URL.String()) log.Debugf("req-jwt: %s %s", req.Method, req.URL.String())
if log.GetLevel() >= log.TraceLevel { if log.GetLevel() >= log.TraceLevel {
dump, _ := httputil.DumpRequest(req, true) dump, _ := httputil.DumpRequest(req, true)
@ -196,7 +196,7 @@ func (t *JWTTransport) RoundTrip(req *http.Request) (*http.Response, error) {
} }
if err != nil || resp.StatusCode == 401 { if err != nil || resp.StatusCode == 401 {
/*we had an error (network error for example, or 401 because token is refused), reset the token ?*/ /*we had an error (network error for example, or 401 because token is refused), reset the token ?*/
t.token = "" t.Token = ""
return resp, errors.Wrapf(err, "performing jwt auth") return resp, errors.Wrapf(err, "performing jwt auth")
} }
log.Debugf("resp-jwt: %d", resp.StatusCode) log.Debugf("resp-jwt: %d", resp.StatusCode)

View file

@ -38,6 +38,10 @@ type ApiClient struct {
HeartBeat *HeartBeatService HeartBeat *HeartBeatService
} }
func (a *ApiClient) GetClient() *http.Client {
return a.client
}
type service struct { type service struct {
client *ApiClient client *ApiClient
} }