Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
|
c982588bca |
5 changed files with 108 additions and 43 deletions
|
@ -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)")
|
||||||
},
|
},
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue