Merge branch 'master' into http_plugin_unix_socket
This commit is contained in:
commit
0c06438fb6
33 changed files with 982 additions and 1006 deletions
4
.github/workflows/bats-hub.yml
vendored
4
.github/workflows/bats-hub.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Hub tests
|
||||
name: (sub) Bats / Hub
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
@ -17,7 +17,7 @@ jobs:
|
|||
matrix:
|
||||
test-file: ["hub-1.bats", "hub-2.bats", "hub-3.bats"]
|
||||
|
||||
name: "Build + tests"
|
||||
name: "Functional tests"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
|
|
4
.github/workflows/bats-mysql.yml
vendored
4
.github/workflows/bats-mysql.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Functional tests (MySQL)
|
||||
name: (sub) Bats / MySQL
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
@ -12,7 +12,7 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build + tests"
|
||||
name: "Functional tests"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
services:
|
||||
|
|
4
.github/workflows/bats-postgres.yml
vendored
4
.github/workflows/bats-postgres.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Functional tests (Postgres)
|
||||
name: (sub) Bats / Postgres
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
@ -8,7 +8,7 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build + tests"
|
||||
name: "Functional tests"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
services:
|
||||
|
|
4
.github/workflows/bats-sqlite-coverage.yml
vendored
4
.github/workflows/bats-sqlite-coverage.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Functional tests (sqlite)
|
||||
name: (sub) Bats / sqlite + coverage
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
@ -9,7 +9,7 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build + tests"
|
||||
name: "Functional tests"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
|
|
46
.github/workflows/docker-tests.yml
vendored
46
.github/workflows/docker-tests.yml
vendored
|
@ -15,7 +15,14 @@ on:
|
|||
- 'README.md'
|
||||
|
||||
jobs:
|
||||
test_docker_image:
|
||||
test_flavor:
|
||||
strategy:
|
||||
# we could test all the flavors in a single pytest job,
|
||||
# but let's split them (and the image build) in multiple runners for performance
|
||||
matrix:
|
||||
# can be slim, full or debian (no debian slim).
|
||||
flavor: ["slim", "debian"]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
|
@ -30,37 +37,13 @@ jobs:
|
|||
with:
|
||||
config: .github/buildkit.toml
|
||||
|
||||
- name: "Build flavor: slim"
|
||||
- name: "Build image"
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
tags: crowdsecurity/crowdsec:test-slim
|
||||
target: slim
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
|
||||
- name: "Build flavor: full"
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
tags: crowdsecurity/crowdsec:test
|
||||
target: full
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
|
||||
- name: "Build flavor: full (debian)"
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.debian
|
||||
tags: crowdsecurity/crowdsec:test-debian
|
||||
target: full
|
||||
file: ./Dockerfile${{ matrix.flavor == 'debian' && '.debian' || '' }}
|
||||
tags: crowdsecurity/crowdsec:test${{ matrix.flavor == 'full' && '' || '-' }}${{ matrix.flavor == 'full' && '' || matrix.flavor }}
|
||||
target: ${{ matrix.flavor == 'debian' && 'full' || matrix.flavor }}
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
cache-from: type=gha
|
||||
|
@ -95,9 +78,10 @@ jobs:
|
|||
- name: "Run tests"
|
||||
env:
|
||||
CROWDSEC_TEST_VERSION: test
|
||||
CROWDSEC_TEST_FLAVORS: slim,debian
|
||||
CROWDSEC_TEST_FLAVORS: ${{ matrix.flavor }}
|
||||
CROWDSEC_TEST_NETWORK: net-test
|
||||
CROWDSEC_TEST_TIMEOUT: 90
|
||||
# running serially to reduce test flakiness
|
||||
run: |
|
||||
cd docker/test
|
||||
pipenv run pytest -n 2 --durations=0 --color=yes
|
||||
pipenv run pytest -n 1 --durations=0 --color=yes
|
||||
|
|
6
.github/workflows/publish-docker-master.yml
vendored
6
.github/workflows/publish-docker-master.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Publish Docker image on Push to Master
|
||||
name: (push-master) Publish latest Docker images
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -6,10 +6,10 @@ on:
|
|||
paths:
|
||||
- 'pkg/**'
|
||||
- 'cmd/**'
|
||||
- 'plugins/**'
|
||||
- 'mk/**'
|
||||
- 'docker/docker_start.sh'
|
||||
- 'docker/config.yaml'
|
||||
- '.github/workflows/publish_docker-master.yml'
|
||||
- '.github/workflows/publish-docker-master.yml'
|
||||
- '.github/workflows/publish-docker.yml'
|
||||
- 'Dockerfile'
|
||||
- 'Dockerfile.debian'
|
||||
|
|
14
.github/workflows/publish-docker-release.yml
vendored
14
.github/workflows/publish-docker-release.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Publish Docker images
|
||||
name: (manual) Publish Docker images
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
@ -20,37 +20,29 @@ on:
|
|||
|
||||
jobs:
|
||||
alpine:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: ["linux/amd64", "linux/386", "linux/arm64", "linux/arm/v7", "linux/arm/v6"]
|
||||
|
||||
uses: ./.github/workflows/publish-docker.yml
|
||||
secrets:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
with:
|
||||
platform: ${{ matrix.platform }}
|
||||
image_version: ${{ github.event.inputs.image_version }}
|
||||
crowdsec_version: ${{ github.event.inputs.crowdsec_version }}
|
||||
latest: ${{ github.event.inputs.latest == 'true' }}
|
||||
push: ${{ github.event.inputs.push == 'true' }}
|
||||
slim: true
|
||||
debian: false
|
||||
platform: "linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6"
|
||||
|
||||
debian:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: ["linux/amd64", "linux/386", "linux/arm64"]
|
||||
|
||||
uses: ./.github/workflows/publish-docker.yml
|
||||
secrets:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
with:
|
||||
platform: ${{ matrix.platform }}
|
||||
image_version: ${{ github.event.inputs.image_version }}
|
||||
crowdsec_version: ${{ github.event.inputs.crowdsec_version }}
|
||||
latest: ${{ github.event.inputs.latest == 'true' }}
|
||||
push: ${{ github.event.inputs.push == 'true' }}
|
||||
slim: false
|
||||
debian: true
|
||||
platform: "linux/amd64,linux/386,linux/arm64"
|
||||
|
|
2
.github/workflows/publish-docker.yml
vendored
2
.github/workflows/publish-docker.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Publish Docker image / platform
|
||||
name: (sub) Publish Docker images
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
|
2
.github/workflows/update_docker_hub_doc.yml
vendored
2
.github/workflows/update_docker_hub_doc.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Update Docker Hub README
|
||||
name: (push-master) Update Docker Hub README
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
17
Dockerfile
17
Dockerfile
|
@ -39,10 +39,8 @@ RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/co
|
|||
mkdir -p /staging/var/lib/crowdsec && \
|
||||
mkdir -p /var/lib/crowdsec/data
|
||||
|
||||
COPY --from=build /go/bin/yq /usr/local/bin/yq
|
||||
COPY --from=build /go/bin/yq /usr/local/bin/crowdsec /usr/local/bin/cscli /usr/local/bin/
|
||||
COPY --from=build /etc/crowdsec /staging/etc/crowdsec
|
||||
COPY --from=build /usr/local/bin/crowdsec /usr/local/bin/crowdsec
|
||||
COPY --from=build /usr/local/bin/cscli /usr/local/bin/cscli
|
||||
COPY --from=build /go/src/crowdsec/docker/docker_start.sh /
|
||||
COPY --from=build /go/src/crowdsec/docker/config.yaml /staging/etc/crowdsec/config.yaml
|
||||
RUN yq -n '.url="http://0.0.0.0:8080"' | install -m 0600 /dev/stdin /staging/etc/crowdsec/local_api_credentials.yaml
|
||||
|
@ -53,11 +51,14 @@ FROM slim as plugins
|
|||
|
||||
# Due to the wizard using cp -n, we have to copy the config files directly from the source as -n does not exist in busybox cp
|
||||
# The files are here for reference, as users will need to mount a new version to be actually able to use notifications
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-email/email.yaml /staging/etc/crowdsec/notifications/email.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-http/http.yaml /staging/etc/crowdsec/notifications/http.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-slack/slack.yaml /staging/etc/crowdsec/notifications/slack.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-splunk/splunk.yaml /staging/etc/crowdsec/notifications/splunk.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-sentinel/sentinel.yaml /staging/etc/crowdsec/notifications/sentinel.yaml
|
||||
COPY --from=build \
|
||||
/go/src/crowdsec/cmd/notification-email/email.yaml \
|
||||
/go/src/crowdsec/cmd/notification-http/http.yaml \
|
||||
/go/src/crowdsec/cmd/notification-slack/slack.yaml \
|
||||
/go/src/crowdsec/cmd/notification-splunk/splunk.yaml \
|
||||
/go/src/crowdsec/cmd/notification-sentinel/sentinel.yaml \
|
||||
/staging/etc/crowdsec/notifications/
|
||||
|
||||
COPY --from=build /usr/local/lib/crowdsec/plugins /usr/local/lib/crowdsec/plugins
|
||||
|
||||
FROM slim as geoip
|
||||
|
|
|
@ -55,10 +55,8 @@ RUN apt-get update && \
|
|||
mkdir -p /staging/var/lib/crowdsec && \
|
||||
mkdir -p /var/lib/crowdsec/data
|
||||
|
||||
COPY --from=build /go/bin/yq /usr/local/bin/yq
|
||||
COPY --from=build /go/bin/yq /usr/local/bin/crowdsec /usr/local/bin/cscli /usr/local/bin/
|
||||
COPY --from=build /etc/crowdsec /staging/etc/crowdsec
|
||||
COPY --from=build /usr/local/bin/crowdsec /usr/local/bin/crowdsec
|
||||
COPY --from=build /usr/local/bin/cscli /usr/local/bin/cscli
|
||||
COPY --from=build /go/src/crowdsec/docker/docker_start.sh /
|
||||
COPY --from=build /go/src/crowdsec/docker/config.yaml /staging/etc/crowdsec/config.yaml
|
||||
RUN yq -n '.url="http://0.0.0.0:8080"' | install -m 0600 /dev/stdin /staging/etc/crowdsec/local_api_credentials.yaml && \
|
||||
|
@ -70,11 +68,14 @@ FROM slim as plugins
|
|||
|
||||
# Due to the wizard using cp -n, we have to copy the config files directly from the source as -n does not exist in busybox cp
|
||||
# The files are here for reference, as users will need to mount a new version to be actually able to use notifications
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-email/email.yaml /staging/etc/crowdsec/notifications/email.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-http/http.yaml /staging/etc/crowdsec/notifications/http.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-slack/slack.yaml /staging/etc/crowdsec/notifications/slack.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-splunk/splunk.yaml /staging/etc/crowdsec/notifications/splunk.yaml
|
||||
COPY --from=build /go/src/crowdsec/cmd/notification-sentinel/sentinel.yaml /staging/etc/crowdsec/notifications/sentinel.yaml
|
||||
COPY --from=build \
|
||||
/go/src/crowdsec/cmd/notification-email/email.yaml \
|
||||
/go/src/crowdsec/cmd/notification-http/http.yaml \
|
||||
/go/src/crowdsec/cmd/notification-slack/slack.yaml \
|
||||
/go/src/crowdsec/cmd/notification-splunk/splunk.yaml \
|
||||
/go/src/crowdsec/cmd/notification-sentinel/sentinel.yaml \
|
||||
/staging/etc/crowdsec/notifications/
|
||||
|
||||
COPY --from=build /usr/local/lib/crowdsec/plugins /usr/local/lib/crowdsec/plugins
|
||||
|
||||
FROM slim as geoip
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
@ -48,52 +47,9 @@ func DecisionsFromAlert(alert *models.Alert) string {
|
|||
return ret
|
||||
}
|
||||
|
||||
func DateFromAlert(alert *models.Alert) string {
|
||||
ts, err := time.Parse(time.RFC3339, alert.CreatedAt)
|
||||
if err != nil {
|
||||
log.Infof("while parsing %s with %s : %s", alert.CreatedAt, time.RFC3339, err)
|
||||
return alert.CreatedAt
|
||||
}
|
||||
return ts.Format(time.RFC822)
|
||||
}
|
||||
|
||||
func SourceFromAlert(alert *models.Alert) string {
|
||||
|
||||
//more than one item, just number and scope
|
||||
if len(alert.Decisions) > 1 {
|
||||
return fmt.Sprintf("%d %ss (%s)", len(alert.Decisions), *alert.Decisions[0].Scope, *alert.Decisions[0].Origin)
|
||||
}
|
||||
|
||||
//fallback on single decision information
|
||||
if len(alert.Decisions) == 1 {
|
||||
return fmt.Sprintf("%s:%s", *alert.Decisions[0].Scope, *alert.Decisions[0].Value)
|
||||
}
|
||||
|
||||
//try to compose a human friendly version
|
||||
if *alert.Source.Value != "" && *alert.Source.Scope != "" {
|
||||
scope := fmt.Sprintf("%s:%s", *alert.Source.Scope, *alert.Source.Value)
|
||||
extra := ""
|
||||
if alert.Source.Cn != "" {
|
||||
extra = alert.Source.Cn
|
||||
}
|
||||
if alert.Source.AsNumber != "" {
|
||||
extra += fmt.Sprintf("/%s", alert.Source.AsNumber)
|
||||
}
|
||||
if alert.Source.AsName != "" {
|
||||
extra += fmt.Sprintf("/%s", alert.Source.AsName)
|
||||
}
|
||||
|
||||
if extra != "" {
|
||||
scope += " (" + extra + ")"
|
||||
}
|
||||
return scope
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func AlertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
|
||||
|
||||
if csConfig.Cscli.Output == "raw" {
|
||||
func alertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
|
||||
switch csConfig.Cscli.Output {
|
||||
case "raw":
|
||||
csvwriter := csv.NewWriter(os.Stdout)
|
||||
header := []string{"id", "scope", "value", "reason", "country", "as", "decisions", "created_at"}
|
||||
if printMachine {
|
||||
|
@ -123,7 +79,7 @@ func AlertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
|
|||
}
|
||||
}
|
||||
csvwriter.Flush()
|
||||
} else if csConfig.Cscli.Output == "json" {
|
||||
case "json":
|
||||
if *alerts == nil {
|
||||
// avoid returning "null" in json
|
||||
// could be cleaner if we used slice of alerts directly
|
||||
|
@ -131,8 +87,8 @@ func AlertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
|
|||
return nil
|
||||
}
|
||||
x, _ := json.MarshalIndent(alerts, "", " ")
|
||||
fmt.Printf("%s", string(x))
|
||||
} else if csConfig.Cscli.Output == "human" {
|
||||
fmt.Print(string(x))
|
||||
case "human":
|
||||
if len(*alerts) == 0 {
|
||||
fmt.Println("No active alerts")
|
||||
return nil
|
||||
|
@ -160,59 +116,60 @@ var alertTemplate = `
|
|||
|
||||
`
|
||||
|
||||
func DisplayOneAlert(alert *models.Alert, withDetail bool) error {
|
||||
if csConfig.Cscli.Output == "human" {
|
||||
tmpl, err := template.New("alert").Parse(alertTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tmpl.Execute(os.Stdout, alert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func displayOneAlert(alert *models.Alert, withDetail bool) error {
|
||||
tmpl, err := template.New("alert").Parse(alertTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tmpl.Execute(os.Stdout, alert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
alertDecisionsTable(color.Output, alert)
|
||||
alertDecisionsTable(color.Output, alert)
|
||||
|
||||
if len(alert.Meta) > 0 {
|
||||
fmt.Printf("\n - Context :\n")
|
||||
sort.Slice(alert.Meta, func(i, j int) bool {
|
||||
return alert.Meta[i].Key < alert.Meta[j].Key
|
||||
})
|
||||
table := newTable(color.Output)
|
||||
table.SetRowLines(false)
|
||||
table.SetHeaders("Key", "Value")
|
||||
for _, meta := range alert.Meta {
|
||||
var valSlice []string
|
||||
if err := json.Unmarshal([]byte(meta.Value), &valSlice); err != nil {
|
||||
return fmt.Errorf("unknown context value type '%s' : %s", meta.Value, err)
|
||||
}
|
||||
for _, value := range valSlice {
|
||||
table.AddRow(
|
||||
meta.Key,
|
||||
value,
|
||||
)
|
||||
}
|
||||
if len(alert.Meta) > 0 {
|
||||
fmt.Printf("\n - Context :\n")
|
||||
sort.Slice(alert.Meta, func(i, j int) bool {
|
||||
return alert.Meta[i].Key < alert.Meta[j].Key
|
||||
})
|
||||
table := newTable(color.Output)
|
||||
table.SetRowLines(false)
|
||||
table.SetHeaders("Key", "Value")
|
||||
for _, meta := range alert.Meta {
|
||||
var valSlice []string
|
||||
if err := json.Unmarshal([]byte(meta.Value), &valSlice); err != nil {
|
||||
return fmt.Errorf("unknown context value type '%s' : %s", meta.Value, err)
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
|
||||
if withDetail {
|
||||
fmt.Printf("\n - Events :\n")
|
||||
for _, event := range alert.Events {
|
||||
alertEventTable(color.Output, event)
|
||||
for _, value := range valSlice {
|
||||
table.AddRow(
|
||||
meta.Key,
|
||||
value,
|
||||
)
|
||||
}
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
|
||||
if withDetail {
|
||||
fmt.Printf("\n - Events :\n")
|
||||
for _, event := range alert.Events {
|
||||
alertEventTable(color.Output, event)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type cliAlerts struct{}
|
||||
type cliAlerts struct{
|
||||
client *apiclient.ApiClient
|
||||
}
|
||||
|
||||
func NewCLIAlerts() *cliAlerts {
|
||||
return &cliAlerts{}
|
||||
}
|
||||
|
||||
func (cli cliAlerts) NewCommand() *cobra.Command {
|
||||
func (cli *cliAlerts) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "alerts [action]",
|
||||
Short: "Manage alerts",
|
||||
|
@ -228,7 +185,7 @@ func (cli cliAlerts) NewCommand() *cobra.Command {
|
|||
if err != nil {
|
||||
return fmt.Errorf("parsing api url %s: %w", apiURL, err)
|
||||
}
|
||||
Client, err = apiclient.NewClient(&apiclient.Config{
|
||||
cli.client, err = apiclient.NewClient(&apiclient.Config{
|
||||
MachineID: csConfig.API.Client.Credentials.Login,
|
||||
Password: strfmt.Password(csConfig.API.Client.Credentials.Password),
|
||||
UserAgent: fmt.Sprintf("crowdsec/%s", version.String()),
|
||||
|
@ -251,7 +208,7 @@ func (cli cliAlerts) NewCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliAlerts) NewListCmd() *cobra.Command {
|
||||
func (cli *cliAlerts) NewListCmd() *cobra.Command {
|
||||
var alertListFilter = apiclient.AlertsListOpts{
|
||||
ScopeEquals: new(string),
|
||||
ValueEquals: new(string),
|
||||
|
@ -345,12 +302,12 @@ cscli alerts list --type ban`,
|
|||
alertListFilter.Contains = new(bool)
|
||||
}
|
||||
|
||||
alerts, _, err := Client.Alerts.List(context.Background(), alertListFilter)
|
||||
alerts, _, err := cli.client.Alerts.List(context.Background(), alertListFilter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list alerts: %v", err)
|
||||
}
|
||||
|
||||
err = AlertsToTable(alerts, printMachine)
|
||||
err = alertsToTable(alerts, printMachine)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list alerts: %v", err)
|
||||
}
|
||||
|
@ -376,7 +333,7 @@ cscli alerts list --type ban`,
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliAlerts) NewDeleteCmd() *cobra.Command {
|
||||
func (cli *cliAlerts) NewDeleteCmd() *cobra.Command {
|
||||
var ActiveDecision *bool
|
||||
var AlertDeleteAll bool
|
||||
var delAlertByID string
|
||||
|
@ -451,12 +408,12 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
|
|||
|
||||
var alerts *models.DeleteAlertsResponse
|
||||
if delAlertByID == "" {
|
||||
alerts, _, err = Client.Alerts.Delete(context.Background(), alertDeleteFilter)
|
||||
alerts, _, err = cli.client.Alerts.Delete(context.Background(), alertDeleteFilter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete alerts : %v", err)
|
||||
}
|
||||
} else {
|
||||
alerts, _, err = Client.Alerts.DeleteOne(context.Background(), delAlertByID)
|
||||
alerts, _, err = cli.client.Alerts.DeleteOne(context.Background(), delAlertByID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete alert: %v", err)
|
||||
}
|
||||
|
@ -478,7 +435,7 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliAlerts) NewInspectCmd() *cobra.Command {
|
||||
func (cli *cliAlerts) NewInspectCmd() *cobra.Command {
|
||||
var details bool
|
||||
cmd := &cobra.Command{
|
||||
Use: `inspect "alert_id"`,
|
||||
|
@ -495,13 +452,13 @@ func (cli cliAlerts) NewInspectCmd() *cobra.Command {
|
|||
if err != nil {
|
||||
return fmt.Errorf("bad alert id %s", alertID)
|
||||
}
|
||||
alert, _, err := Client.Alerts.GetByID(context.Background(), id)
|
||||
alert, _, err := cli.client.Alerts.GetByID(context.Background(), id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't find alert with id %s: %s", alertID, err)
|
||||
}
|
||||
switch csConfig.Cscli.Output {
|
||||
case "human":
|
||||
if err := DisplayOneAlert(alert, details); err != nil {
|
||||
if err := displayOneAlert(alert, details); err != nil {
|
||||
continue
|
||||
}
|
||||
case "json":
|
||||
|
@ -528,7 +485,7 @@ func (cli cliAlerts) NewInspectCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliAlerts) NewFlushCmd() *cobra.Command {
|
||||
func (cli *cliAlerts) NewFlushCmd() *cobra.Command {
|
||||
var maxItems int
|
||||
var maxAge string
|
||||
cmd := &cobra.Command{
|
||||
|
@ -542,12 +499,12 @@ func (cli cliAlerts) NewFlushCmd() *cobra.Command {
|
|||
if err := require.LAPI(csConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
dbClient, err = database.NewClient(csConfig.DbConfig)
|
||||
db, err := database.NewClient(csConfig.DbConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create new database client: %s", err)
|
||||
}
|
||||
log.Info("Flushing alerts. !! This may take a long time !!")
|
||||
err = dbClient.FlushAlerts(maxAge, maxItems)
|
||||
err = db.FlushAlerts(maxAge, maxItems)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to flush alerts: %s", err)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import (
|
|||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -12,7 +13,6 @@ import (
|
|||
"github.com/fatih/color"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"slices"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
|
||||
|
@ -20,53 +20,33 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
||||
func getBouncers(out io.Writer, dbClient *database.Client) error {
|
||||
bouncers, err := dbClient.ListBouncers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list bouncers: %s", err)
|
||||
func askYesNo(message string, defaultAnswer bool) (bool, error) {
|
||||
var answer bool
|
||||
|
||||
prompt := &survey.Confirm{
|
||||
Message: message,
|
||||
Default: defaultAnswer,
|
||||
}
|
||||
|
||||
switch csConfig.Cscli.Output {
|
||||
case "human":
|
||||
getBouncersTable(out, bouncers)
|
||||
case "json":
|
||||
enc := json.NewEncoder(out)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(bouncers); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal: %w", err)
|
||||
}
|
||||
return nil
|
||||
case "raw":
|
||||
csvwriter := csv.NewWriter(out)
|
||||
err := csvwriter.Write([]string{"name", "ip", "revoked", "last_pull", "type", "version", "auth_type"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write raw header: %w", err)
|
||||
}
|
||||
for _, b := range bouncers {
|
||||
var revoked string
|
||||
if !b.Revoked {
|
||||
revoked = "validated"
|
||||
} else {
|
||||
revoked = "pending"
|
||||
}
|
||||
err := csvwriter.Write([]string{b.Name, b.IPAddress, revoked, b.LastPull.Format(time.RFC3339), b.Type, b.Version, b.AuthType})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write raw: %w", err)
|
||||
}
|
||||
}
|
||||
csvwriter.Flush()
|
||||
if err := survey.AskOne(prompt, &answer); err != nil {
|
||||
return defaultAnswer, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return answer, nil
|
||||
}
|
||||
|
||||
type cliBouncers struct {}
|
||||
|
||||
func NewCLIBouncers() *cliBouncers {
|
||||
return &cliBouncers{}
|
||||
type cliBouncers struct {
|
||||
db *database.Client
|
||||
cfg configGetter
|
||||
}
|
||||
|
||||
func (cli cliBouncers) NewCommand() *cobra.Command {
|
||||
func NewCLIBouncers(getconfig configGetter) *cliBouncers {
|
||||
return &cliBouncers{
|
||||
cfg: getconfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *cliBouncers) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "bouncers [action]",
|
||||
Short: "Manage bouncers [requires local API]",
|
||||
|
@ -76,94 +56,127 @@ Note: This command requires database direct access, so is intended to be run on
|
|||
Args: cobra.MinimumNArgs(1),
|
||||
Aliases: []string{"bouncer"},
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
var err error
|
||||
if err = require.LAPI(csConfig); err != nil {
|
||||
if err = require.LAPI(cli.cfg()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbClient, err = database.NewClient(csConfig.DbConfig)
|
||||
cli.db, err = database.NewClient(cli.cfg().DbConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create new database client: %s", err)
|
||||
return fmt.Errorf("can't connect to the database: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(cli.NewListCmd())
|
||||
cmd.AddCommand(cli.NewAddCmd())
|
||||
cmd.AddCommand(cli.NewDeleteCmd())
|
||||
cmd.AddCommand(cli.NewPruneCmd())
|
||||
cmd.AddCommand(cli.newListCmd())
|
||||
cmd.AddCommand(cli.newAddCmd())
|
||||
cmd.AddCommand(cli.newDeleteCmd())
|
||||
cmd.AddCommand(cli.newPruneCmd())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliBouncers) NewListCmd() *cobra.Command {
|
||||
func (cli *cliBouncers) list() error {
|
||||
out := color.Output
|
||||
|
||||
bouncers, err := cli.db.ListBouncers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list bouncers: %s", err)
|
||||
}
|
||||
|
||||
switch cli.cfg().Cscli.Output {
|
||||
case "human":
|
||||
getBouncersTable(out, bouncers)
|
||||
case "json":
|
||||
enc := json.NewEncoder(out)
|
||||
enc.SetIndent("", " ")
|
||||
|
||||
if err := enc.Encode(bouncers); err != nil {
|
||||
return fmt.Errorf("failed to marshal: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
case "raw":
|
||||
csvwriter := csv.NewWriter(out)
|
||||
|
||||
if err := csvwriter.Write([]string{"name", "ip", "revoked", "last_pull", "type", "version", "auth_type"}); err != nil {
|
||||
return fmt.Errorf("failed to write raw header: %w", err)
|
||||
}
|
||||
|
||||
for _, b := range bouncers {
|
||||
valid := "validated"
|
||||
if b.Revoked {
|
||||
valid = "pending"
|
||||
}
|
||||
|
||||
if err := csvwriter.Write([]string{b.Name, b.IPAddress, valid, b.LastPull.Format(time.RFC3339), b.Type, b.Version, b.AuthType}); err != nil {
|
||||
return fmt.Errorf("failed to write raw: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
csvwriter.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *cliBouncers) newListCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list all bouncers within the database",
|
||||
Example: `cscli bouncers list`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, arg []string) error {
|
||||
err := getBouncers(color.Output, dbClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list bouncers: %s", err)
|
||||
}
|
||||
return nil
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.list()
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliBouncers) add(cmd *cobra.Command, args []string) error {
|
||||
func (cli *cliBouncers) add(bouncerName string, key string) error {
|
||||
var err error
|
||||
|
||||
keyLength := 32
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
key, err := flags.GetString("key")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyName := args[0]
|
||||
var apiKey string
|
||||
|
||||
if keyName == "" {
|
||||
return fmt.Errorf("please provide a name for the api key")
|
||||
}
|
||||
apiKey = key
|
||||
if key == "" {
|
||||
apiKey, err = middlewares.GenerateAPIKey(keyLength)
|
||||
key, err = middlewares.GenerateAPIKey(keyLength)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate api key: %s", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate api key: %s", err)
|
||||
}
|
||||
_, err = dbClient.CreateBouncer(keyName, "", middlewares.HashSHA512(apiKey), types.ApiKeyAuthType)
|
||||
|
||||
_, err = cli.db.CreateBouncer(bouncerName, "", middlewares.HashSHA512(key), types.ApiKeyAuthType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create bouncer: %s", err)
|
||||
}
|
||||
|
||||
switch csConfig.Cscli.Output {
|
||||
switch cli.cfg().Cscli.Output {
|
||||
case "human":
|
||||
fmt.Printf("API key for '%s':\n\n", keyName)
|
||||
fmt.Printf(" %s\n\n", apiKey)
|
||||
fmt.Printf("API key for '%s':\n\n", bouncerName)
|
||||
fmt.Printf(" %s\n\n", key)
|
||||
fmt.Print("Please keep this key since you will not be able to retrieve it!\n")
|
||||
case "raw":
|
||||
fmt.Printf("%s", apiKey)
|
||||
fmt.Print(key)
|
||||
case "json":
|
||||
j, err := json.Marshal(apiKey)
|
||||
j, err := json.Marshal(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to marshal api key")
|
||||
}
|
||||
fmt.Printf("%s", string(j))
|
||||
|
||||
fmt.Print(string(j))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli cliBouncers) NewAddCmd() *cobra.Command {
|
||||
func (cli *cliBouncers) newAddCmd() *cobra.Command {
|
||||
var key string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "add MyBouncerName",
|
||||
Short: "add a single bouncer to the database",
|
||||
|
@ -171,127 +184,133 @@ func (cli cliBouncers) NewAddCmd() *cobra.Command {
|
|||
cscli bouncers add MyBouncerName --key <random-key>`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: cli.add,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cli.add(args[0], key)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringP("length", "l", "", "length of the api key")
|
||||
flags.MarkDeprecated("length", "use --key instead")
|
||||
flags.StringP("key", "k", "", "api key for the bouncer")
|
||||
flags.StringVarP(&key, "key", "k", "", "api key for the bouncer")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliBouncers) delete(cmd *cobra.Command, args []string) error {
|
||||
for _, bouncerID := range args {
|
||||
err := dbClient.DeleteBouncer(bouncerID)
|
||||
func (cli *cliBouncers) deleteValid(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
bouncers, err := cli.db.ListBouncers()
|
||||
if err != nil {
|
||||
cobra.CompError("unable to list bouncers " + err.Error())
|
||||
}
|
||||
|
||||
ret :=[]string{}
|
||||
|
||||
for _, bouncer := range bouncers {
|
||||
if strings.Contains(bouncer.Name, toComplete) && !slices.Contains(args, bouncer.Name) {
|
||||
ret = append(ret, bouncer.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func (cli *cliBouncers) delete(bouncers []string) error {
|
||||
for _, bouncerID := range bouncers {
|
||||
err := cli.db.DeleteBouncer(bouncerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete bouncer '%s': %s", bouncerID, err)
|
||||
}
|
||||
|
||||
log.Infof("bouncer '%s' deleted successfully", bouncerID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli cliBouncers) NewDeleteCmd() *cobra.Command {
|
||||
func (cli *cliBouncers) newDeleteCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete MyBouncerName",
|
||||
Short: "delete bouncer(s) from the database",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Aliases: []string{"remove"},
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
var err error
|
||||
dbClient, err = getDBClient()
|
||||
if err != nil {
|
||||
cobra.CompError("unable to create new database client: " + err.Error())
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
bouncers, err := dbClient.ListBouncers()
|
||||
if err != nil {
|
||||
cobra.CompError("unable to list bouncers " + err.Error())
|
||||
}
|
||||
ret := make([]string, 0)
|
||||
for _, bouncer := range bouncers {
|
||||
if strings.Contains(bouncer.Name, toComplete) && !slices.Contains(args, bouncer.Name) {
|
||||
ret = append(ret, bouncer.Name)
|
||||
}
|
||||
}
|
||||
return ret, cobra.ShellCompDirectiveNoFileComp
|
||||
ValidArgsFunction: cli.deleteValid,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cli.delete(args)
|
||||
},
|
||||
RunE: cli.delete,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliBouncers) NewPruneCmd() *cobra.Command {
|
||||
var parsedDuration time.Duration
|
||||
func (cli *cliBouncers) prune(duration time.Duration, force bool) error {
|
||||
if duration < 2*time.Minute {
|
||||
if yes, err := askYesNo(
|
||||
"The duration you provided is less than 2 minutes. " +
|
||||
"This may remove active bouncers. Continue?", false); err != nil {
|
||||
return err
|
||||
} else if !yes {
|
||||
fmt.Println("User aborted prune. No changes were made.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
bouncers, err := cli.db.QueryBouncersLastPulltimeLT(time.Now().UTC().Add(duration))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to query bouncers: %w", err)
|
||||
}
|
||||
|
||||
if len(bouncers) == 0 {
|
||||
fmt.Println("No bouncers to prune.")
|
||||
return nil
|
||||
}
|
||||
|
||||
getBouncersTable(color.Output, bouncers)
|
||||
|
||||
if !force {
|
||||
if yes, err := askYesNo(
|
||||
"You are about to PERMANENTLY remove the above bouncers from the database. " +
|
||||
"These will NOT be recoverable. Continue?", false); err != nil {
|
||||
return err
|
||||
} else if !yes {
|
||||
fmt.Println("User aborted prune. No changes were made.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
deleted, err := cli.db.BulkDeleteBouncers(bouncers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to prune bouncers: %s", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Successfully deleted %d bouncers\n", deleted)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *cliBouncers) newPruneCmd() *cobra.Command {
|
||||
var (
|
||||
duration time.Duration
|
||||
force bool
|
||||
)
|
||||
|
||||
const defaultDuration = 60 * time.Minute
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "prune",
|
||||
Short: "prune multiple bouncers from the database",
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Example: `cscli bouncers prune -d 60m
|
||||
cscli bouncers prune -d 60m --force`,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
dur, _ := cmd.Flags().GetString("duration")
|
||||
var err error
|
||||
parsedDuration, err = time.ParseDuration(fmt.Sprintf("-%s", dur))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse duration '%s': %s", dur, err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
if parsedDuration >= 0-2*time.Minute {
|
||||
var answer bool
|
||||
prompt := &survey.Confirm{
|
||||
Message: "The duration you provided is less than or equal 2 minutes this may remove active bouncers continue ?",
|
||||
Default: false,
|
||||
}
|
||||
if err := survey.AskOne(prompt, &answer); err != nil {
|
||||
return fmt.Errorf("unable to ask about prune check: %s", err)
|
||||
}
|
||||
if !answer {
|
||||
fmt.Println("user aborted prune no changes were made")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
bouncers, err := dbClient.QueryBouncersLastPulltimeLT(time.Now().UTC().Add(parsedDuration))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to query bouncers: %s", err)
|
||||
}
|
||||
if len(bouncers) == 0 {
|
||||
fmt.Println("no bouncers to prune")
|
||||
return nil
|
||||
}
|
||||
getBouncersTable(color.Output, bouncers)
|
||||
if !force {
|
||||
var answer bool
|
||||
prompt := &survey.Confirm{
|
||||
Message: "You are about to PERMANENTLY remove the above bouncers from the database these will NOT be recoverable, continue ?",
|
||||
Default: false,
|
||||
}
|
||||
if err := survey.AskOne(prompt, &answer); err != nil {
|
||||
return fmt.Errorf("unable to ask about prune check: %s", err)
|
||||
}
|
||||
if !answer {
|
||||
fmt.Println("user aborted prune no changes were made")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
nbDeleted, err := dbClient.BulkDeleteBouncers(bouncers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to prune bouncers: %s", err)
|
||||
}
|
||||
fmt.Printf("successfully delete %d bouncers\n", nbDeleted)
|
||||
return nil
|
||||
Example: `cscli bouncers prune -d 45m
|
||||
cscli bouncers prune -d 45m --force`,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.prune(duration, force)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringP("duration", "d", "60m", "duration of time since last pull")
|
||||
cmd.Flags().Bool("force", false, "force prune without asking for confirmation")
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.DurationVarP(&duration, "duration", "d", defaultDuration, "duration of time since last pull")
|
||||
flags.BoolVar(&force, "force", false, "force prune without asking for confirmation")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -43,14 +43,17 @@ var (
|
|||
// information needed to set up a random password on user's behalf
|
||||
)
|
||||
|
||||
type cliDashboard struct{}
|
||||
|
||||
func NewCLIDashboard() *cliDashboard {
|
||||
return &cliDashboard{}
|
||||
type cliDashboard struct{
|
||||
cfg configGetter
|
||||
}
|
||||
|
||||
func (cli cliDashboard) NewCommand() *cobra.Command {
|
||||
/* ---- UPDATE COMMAND */
|
||||
func NewCLIDashboard(getconfig configGetter) *cliDashboard {
|
||||
return &cliDashboard{
|
||||
cfg: getconfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *cliDashboard) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "dashboard [command]",
|
||||
Short: "Manage your metabase dashboard container [requires local API]",
|
||||
|
@ -65,8 +68,9 @@ cscli dashboard start
|
|||
cscli dashboard stop
|
||||
cscli dashboard remove
|
||||
`,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := require.LAPI(csConfig); err != nil {
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
if err := require.LAPI(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -74,13 +78,13 @@ cscli dashboard remove
|
|||
return err
|
||||
}
|
||||
|
||||
metabaseConfigFolderPath := filepath.Join(csConfig.ConfigPaths.ConfigDir, metabaseConfigFolder)
|
||||
metabaseConfigFolderPath := filepath.Join(cfg.ConfigPaths.ConfigDir, metabaseConfigFolder)
|
||||
metabaseConfigPath = filepath.Join(metabaseConfigFolderPath, metabaseConfigFile)
|
||||
if err := os.MkdirAll(metabaseConfigFolderPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := require.DB(csConfig); err != nil {
|
||||
if err := require.DB(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -99,16 +103,16 @@ cscli dashboard remove
|
|||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(cli.NewSetupCmd())
|
||||
cmd.AddCommand(cli.NewStartCmd())
|
||||
cmd.AddCommand(cli.NewStopCmd())
|
||||
cmd.AddCommand(cli.NewShowPasswordCmd())
|
||||
cmd.AddCommand(cli.NewRemoveCmd())
|
||||
cmd.AddCommand(cli.newSetupCmd())
|
||||
cmd.AddCommand(cli.newStartCmd())
|
||||
cmd.AddCommand(cli.newStopCmd())
|
||||
cmd.AddCommand(cli.newShowPasswordCmd())
|
||||
cmd.AddCommand(cli.newRemoveCmd())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliDashboard) NewSetupCmd() *cobra.Command {
|
||||
func (cli *cliDashboard) newSetupCmd() *cobra.Command {
|
||||
var force bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -122,9 +126,9 @@ cscli dashboard setup
|
|||
cscli dashboard setup --listen 0.0.0.0
|
||||
cscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
if metabaseDbPath == "" {
|
||||
metabaseDbPath = csConfig.ConfigPaths.DataDir
|
||||
metabaseDbPath = cli.cfg().ConfigPaths.DataDir
|
||||
}
|
||||
|
||||
if metabasePassword == "" {
|
||||
|
@ -145,10 +149,10 @@ cscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = chownDatabase(dockerGroup.Gid); err != nil {
|
||||
if err = cli.chownDatabase(dockerGroup.Gid); err != nil {
|
||||
return err
|
||||
}
|
||||
mb, err := metabase.SetupMetabase(csConfig.API.Server.DbConfig, metabaseListenAddress, metabaseListenPort, metabaseUser, metabasePassword, metabaseDbPath, dockerGroup.Gid, metabaseContainerID, metabaseImage)
|
||||
mb, err := metabase.SetupMetabase(cli.cfg().API.Server.DbConfig, metabaseListenAddress, metabaseListenPort, metabaseUser, metabasePassword, metabaseDbPath, dockerGroup.Gid, metabaseContainerID, metabaseImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -164,26 +168,28 @@ cscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
|
|||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "Force setup : override existing files")
|
||||
cmd.Flags().StringVarP(&metabaseDbPath, "dir", "d", "", "Shared directory with metabase container")
|
||||
cmd.Flags().StringVarP(&metabaseListenAddress, "listen", "l", metabaseListenAddress, "Listen address of container")
|
||||
cmd.Flags().StringVar(&metabaseImage, "metabase-image", metabaseImage, "Metabase image to use")
|
||||
cmd.Flags().StringVarP(&metabaseListenPort, "port", "p", metabaseListenPort, "Listen port of container")
|
||||
cmd.Flags().BoolVarP(&forceYes, "yes", "y", false, "force yes")
|
||||
//cmd.Flags().StringVarP(&metabaseUser, "user", "u", "crowdsec@crowdsec.net", "metabase user")
|
||||
cmd.Flags().StringVar(&metabasePassword, "password", "", "metabase password")
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&force, "force", "f", false, "Force setup : override existing files")
|
||||
flags.StringVarP(&metabaseDbPath, "dir", "d", "", "Shared directory with metabase container")
|
||||
flags.StringVarP(&metabaseListenAddress, "listen", "l", metabaseListenAddress, "Listen address of container")
|
||||
flags.StringVar(&metabaseImage, "metabase-image", metabaseImage, "Metabase image to use")
|
||||
flags.StringVarP(&metabaseListenPort, "port", "p", metabaseListenPort, "Listen port of container")
|
||||
flags.BoolVarP(&forceYes, "yes", "y", false, "force yes")
|
||||
//flags.StringVarP(&metabaseUser, "user", "u", "crowdsec@crowdsec.net", "metabase user")
|
||||
flags.StringVar(&metabasePassword, "password", "", "metabase password")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliDashboard) NewStartCmd() *cobra.Command {
|
||||
func (cli *cliDashboard) newStartCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start the metabase container.",
|
||||
Long: `Stats the metabase container using docker.`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
mb, err := metabase.NewMetabase(metabaseConfigPath, metabaseContainerID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -200,19 +206,20 @@ func (cli cliDashboard) NewStartCmd() *cobra.Command {
|
|||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVarP(&forceYes, "yes", "y", false, "force yes")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliDashboard) NewStopCmd() *cobra.Command {
|
||||
func (cli *cliDashboard) newStopCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "stop",
|
||||
Short: "Stops the metabase container.",
|
||||
Long: `Stops the metabase container using docker.`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
if err := metabase.StopContainer(metabaseContainerID); err != nil {
|
||||
return fmt.Errorf("unable to stop container '%s': %s", metabaseContainerID, err)
|
||||
}
|
||||
|
@ -223,12 +230,12 @@ func (cli cliDashboard) NewStopCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliDashboard) NewShowPasswordCmd() *cobra.Command {
|
||||
func (cli *cliDashboard) newShowPasswordCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{Use: "show-password",
|
||||
Short: "displays password of metabase.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
m := metabase.Metabase{}
|
||||
if err := m.LoadConfig(metabaseConfigPath); err != nil {
|
||||
return err
|
||||
|
@ -241,7 +248,7 @@ func (cli cliDashboard) NewShowPasswordCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliDashboard) NewRemoveCmd() *cobra.Command {
|
||||
func (cli *cliDashboard) newRemoveCmd() *cobra.Command {
|
||||
var force bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -254,7 +261,7 @@ func (cli cliDashboard) NewRemoveCmd() *cobra.Command {
|
|||
cscli dashboard remove
|
||||
cscli dashboard remove --force
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
if !forceYes {
|
||||
var answer bool
|
||||
prompt := &survey.Confirm{
|
||||
|
@ -291,8 +298,8 @@ cscli dashboard remove --force
|
|||
}
|
||||
log.Infof("container %s stopped & removed", metabaseContainerID)
|
||||
}
|
||||
log.Debugf("Removing metabase db %s", csConfig.ConfigPaths.DataDir)
|
||||
if err := metabase.RemoveDatabase(csConfig.ConfigPaths.DataDir); err != nil {
|
||||
log.Debugf("Removing metabase db %s", cli.cfg().ConfigPaths.DataDir)
|
||||
if err := metabase.RemoveDatabase(cli.cfg().ConfigPaths.DataDir); err != nil {
|
||||
log.Warnf("failed to remove metabase internal db : %s", err)
|
||||
}
|
||||
if force {
|
||||
|
@ -309,8 +316,10 @@ cscli dashboard remove --force
|
|||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "Remove also the metabase image")
|
||||
cmd.Flags().BoolVarP(&forceYes, "yes", "y", false, "force yes")
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&force, "force", "f", false, "Remove also the metabase image")
|
||||
flags.BoolVarP(&forceYes, "yes", "y", false, "force yes")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -431,22 +440,23 @@ func checkGroups(forceYes *bool) (*user.Group, error) {
|
|||
return user.LookupGroup(crowdsecGroup)
|
||||
}
|
||||
|
||||
func chownDatabase(gid string) error {
|
||||
func (cli *cliDashboard) chownDatabase(gid string) error {
|
||||
cfg := cli.cfg()
|
||||
intID, err := strconv.Atoi(gid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to convert group ID to int: %s", err)
|
||||
}
|
||||
|
||||
if stat, err := os.Stat(csConfig.DbConfig.DbPath); !os.IsNotExist(err) {
|
||||
if stat, err := os.Stat(cfg.DbConfig.DbPath); !os.IsNotExist(err) {
|
||||
info := stat.Sys()
|
||||
if err := os.Chown(csConfig.DbConfig.DbPath, int(info.(*syscall.Stat_t).Uid), intID); err != nil {
|
||||
return fmt.Errorf("unable to chown sqlite db file '%s': %s", csConfig.DbConfig.DbPath, err)
|
||||
if err := os.Chown(cfg.DbConfig.DbPath, int(info.(*syscall.Stat_t).Uid), intID); err != nil {
|
||||
return fmt.Errorf("unable to chown sqlite db file '%s': %s", cfg.DbConfig.DbPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
if csConfig.DbConfig.Type == "sqlite" && csConfig.DbConfig.UseWal != nil && *csConfig.DbConfig.UseWal {
|
||||
if cfg.DbConfig.Type == "sqlite" && cfg.DbConfig.UseWal != nil && *cfg.DbConfig.UseWal {
|
||||
for _, ext := range []string{"-wal", "-shm"} {
|
||||
file := csConfig.DbConfig.DbPath + ext
|
||||
file := cfg.DbConfig.DbPath + ext
|
||||
if stat, err := os.Stat(file); !os.IsNotExist(err) {
|
||||
info := stat.Sys()
|
||||
if err := os.Chown(file, int(info.(*syscall.Stat_t).Uid), intID); err != nil {
|
||||
|
|
|
@ -9,17 +9,21 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type cliDashboard struct{}
|
||||
type cliDashboard struct{
|
||||
cfg configGetter
|
||||
}
|
||||
|
||||
func NewCLIDashboard() *cliDashboard {
|
||||
return &cliDashboard{}
|
||||
func NewCLIDashboard(getconfig configGetter) *cliDashboard {
|
||||
return &cliDashboard{
|
||||
cfg: getconfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (cli cliDashboard) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "dashboard",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
log.Infof("Dashboard command is disabled on %s", runtime.GOOS)
|
||||
},
|
||||
}
|
||||
|
|
28
cmd/crowdsec-cli/flag.go
Normal file
28
cmd/crowdsec-cli/flag.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package main
|
||||
|
||||
// Custom types for flag validation and conversion.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type MachinePassword string
|
||||
|
||||
func (p *MachinePassword) String() string {
|
||||
return string(*p)
|
||||
}
|
||||
|
||||
func (p *MachinePassword) Set(v string) error {
|
||||
// a password can't be more than 72 characters
|
||||
// due to bcrypt limitations
|
||||
if len(v) > 72 {
|
||||
return errors.New("password too long (max 72 characters)")
|
||||
}
|
||||
*p = MachinePassword(v)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MachinePassword) Type() string {
|
||||
return "string"
|
||||
}
|
|
@ -13,13 +13,17 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||
)
|
||||
|
||||
type cliHub struct{}
|
||||
|
||||
func NewCLIHub() *cliHub {
|
||||
return &cliHub{}
|
||||
type cliHub struct{
|
||||
cfg configGetter
|
||||
}
|
||||
|
||||
func (cli cliHub) NewCommand() *cobra.Command {
|
||||
func NewCLIHub(getconfig configGetter) *cliHub {
|
||||
return &cliHub{
|
||||
cfg: getconfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *cliHub) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "hub [action]",
|
||||
Short: "Manage hub index",
|
||||
|
@ -34,23 +38,16 @@ cscli hub upgrade`,
|
|||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmd.AddCommand(cli.NewListCmd())
|
||||
cmd.AddCommand(cli.NewUpdateCmd())
|
||||
cmd.AddCommand(cli.NewUpgradeCmd())
|
||||
cmd.AddCommand(cli.NewTypesCmd())
|
||||
cmd.AddCommand(cli.newListCmd())
|
||||
cmd.AddCommand(cli.newUpdateCmd())
|
||||
cmd.AddCommand(cli.newUpgradeCmd())
|
||||
cmd.AddCommand(cli.newTypesCmd())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliHub) list(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
all, err := flags.GetBool("all")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hub, err := require.Hub(csConfig, nil, log.StandardLogger())
|
||||
func (cli *cliHub) list(all bool) error {
|
||||
hub, err := require.Hub(cli.cfg(), nil, log.StandardLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -80,24 +77,28 @@ func (cli cliHub) list(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliHub) NewListCmd() *cobra.Command {
|
||||
func (cli *cliHub) newListCmd() *cobra.Command {
|
||||
var all bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list [-a]",
|
||||
Short: "List all installed configurations",
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: cli.list,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.list(all)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolP("all", "a", false, "List disabled items as well")
|
||||
flags.BoolVarP(&all, "all", "a", false, "List disabled items as well")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliHub) update(cmd *cobra.Command, args []string) error {
|
||||
local := csConfig.Hub
|
||||
remote := require.RemoteHub(csConfig)
|
||||
func (cli *cliHub) update() error {
|
||||
local := cli.cfg().Hub
|
||||
remote := require.RemoteHub(cli.cfg())
|
||||
|
||||
// don't use require.Hub because if there is no index file, it would fail
|
||||
hub, err := cwhub.NewHub(local, remote, true, log.StandardLogger())
|
||||
|
@ -112,7 +113,7 @@ func (cli cliHub) update(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliHub) NewUpdateCmd() *cobra.Command {
|
||||
func (cli *cliHub) newUpdateCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Download the latest index (catalog of available configurations)",
|
||||
|
@ -121,21 +122,16 @@ Fetches the .index.json file from the hub, containing the list of available conf
|
|||
`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: cli.update,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.update()
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliHub) upgrade(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
force, err := flags.GetBool("force")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hub, err := require.Hub(csConfig, require.RemoteHub(csConfig), log.StandardLogger())
|
||||
func (cli *cliHub) upgrade(force bool) error {
|
||||
hub, err := require.Hub(cli.cfg(), require.RemoteHub(cli.cfg()), log.StandardLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -167,7 +163,9 @@ func (cli cliHub) upgrade(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliHub) NewUpgradeCmd() *cobra.Command {
|
||||
func (cli *cliHub) newUpgradeCmd() *cobra.Command {
|
||||
var force bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Short: "Upgrade all configurations to their latest version",
|
||||
|
@ -176,17 +174,19 @@ Upgrade all configs installed from Crowdsec Hub. Run 'sudo cscli hub update' if
|
|||
`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: cli.upgrade,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.upgrade(force)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.Bool("force", false, "Force upgrade: overwrite tainted and outdated files")
|
||||
flags.BoolVar(&force, "force", false, "Force upgrade: overwrite tainted and outdated files")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliHub) types(cmd *cobra.Command, args []string) error {
|
||||
switch csConfig.Cscli.Output {
|
||||
func (cli *cliHub) types() error {
|
||||
switch cli.cfg().Cscli.Output {
|
||||
case "human":
|
||||
s, err := yaml.Marshal(cwhub.ItemTypes)
|
||||
if err != nil {
|
||||
|
@ -210,7 +210,7 @@ func (cli cliHub) types(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliHub) NewTypesCmd() *cobra.Command {
|
||||
func (cli *cliHub) newTypesCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "types",
|
||||
Short: "List supported item types",
|
||||
|
@ -219,7 +219,9 @@ List the types of supported hub items.
|
|||
`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: cli.types,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.types()
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
|
|
|
@ -51,33 +51,16 @@ func (cli cliItem) NewCommand() *cobra.Command {
|
|||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmd.AddCommand(cli.NewInstallCmd())
|
||||
cmd.AddCommand(cli.NewRemoveCmd())
|
||||
cmd.AddCommand(cli.NewUpgradeCmd())
|
||||
cmd.AddCommand(cli.NewInspectCmd())
|
||||
cmd.AddCommand(cli.NewListCmd())
|
||||
cmd.AddCommand(cli.newInstallCmd())
|
||||
cmd.AddCommand(cli.newRemoveCmd())
|
||||
cmd.AddCommand(cli.newUpgradeCmd())
|
||||
cmd.AddCommand(cli.newInspectCmd())
|
||||
cmd.AddCommand(cli.newListCmd())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliItem) Install(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
downloadOnly, err := flags.GetBool("download-only")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
force, err := flags.GetBool("force")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ignoreError, err := flags.GetBool("ignore")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (cli cliItem) install(args []string, downloadOnly bool, force bool, ignoreError bool) error {
|
||||
hub, err := require.Hub(csConfig, require.RemoteHub(csConfig), log.StandardLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -110,7 +93,13 @@ func (cli cliItem) Install(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliItem) NewInstallCmd() *cobra.Command {
|
||||
func (cli cliItem) newInstallCmd() *cobra.Command {
|
||||
var (
|
||||
downloadOnly bool
|
||||
force bool
|
||||
ignoreError bool
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: coalesce.String(cli.installHelp.use, "install [item]..."),
|
||||
Short: coalesce.String(cli.installHelp.short, fmt.Sprintf("Install given %s", cli.oneOrMore)),
|
||||
|
@ -121,13 +110,15 @@ func (cli cliItem) NewInstallCmd() *cobra.Command {
|
|||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compAllItems(cli.name, args, toComplete)
|
||||
},
|
||||
RunE: cli.Install,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.install(args, downloadOnly, force, ignoreError)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolP("download-only", "d", false, "Only download packages, don't enable")
|
||||
flags.Bool("force", false, "Force install: overwrite tainted and outdated files")
|
||||
flags.Bool("ignore", false, fmt.Sprintf("Ignore errors when installing multiple %s", cli.name))
|
||||
flags.BoolVarP(&downloadOnly, "download-only", "d", false, "Only download packages, don't enable")
|
||||
flags.BoolVar(&force, "force", false, "Force install: overwrite tainted and outdated files")
|
||||
flags.BoolVar(&ignoreError, "ignore", false, fmt.Sprintf("Ignore errors when installing multiple %s", cli.name))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -145,24 +136,7 @@ func istalledParentNames(item *cwhub.Item) []string {
|
|||
return ret
|
||||
}
|
||||
|
||||
func (cli cliItem) Remove(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
purge, err := flags.GetBool("purge")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
force, err := flags.GetBool("force")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
all, err := flags.GetBool("all")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (cli cliItem) remove(args []string, purge bool, force bool, all bool) error {
|
||||
hub, err := require.Hub(csConfig, nil, log.StandardLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -243,7 +217,13 @@ func (cli cliItem) Remove(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliItem) NewRemoveCmd() *cobra.Command {
|
||||
func (cli cliItem) newRemoveCmd() *cobra.Command {
|
||||
var (
|
||||
purge bool
|
||||
force bool
|
||||
all bool
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: coalesce.String(cli.removeHelp.use, "remove [item]..."),
|
||||
Short: coalesce.String(cli.removeHelp.short, fmt.Sprintf("Remove given %s", cli.oneOrMore)),
|
||||
|
@ -254,30 +234,20 @@ func (cli cliItem) NewRemoveCmd() *cobra.Command {
|
|||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstalledItems(cli.name, args, toComplete)
|
||||
},
|
||||
RunE: cli.Remove,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.remove(args, purge, force, all)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.Bool("purge", false, "Delete source file too")
|
||||
flags.Bool("force", false, "Force remove: remove tainted and outdated files")
|
||||
flags.Bool("all", false, fmt.Sprintf("Remove all the %s", cli.name))
|
||||
flags.BoolVar(&purge, "purge", false, "Delete source file too")
|
||||
flags.BoolVar(&force, "force", false, "Force remove: remove tainted and outdated files")
|
||||
flags.BoolVar(&all, "all", false, fmt.Sprintf("Remove all the %s", cli.name))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliItem) Upgrade(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
force, err := flags.GetBool("force")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
all, err := flags.GetBool("all")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (cli cliItem) upgrade(args []string, force bool, all bool) error {
|
||||
hub, err := require.Hub(csConfig, require.RemoteHub(csConfig), log.StandardLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -341,7 +311,12 @@ func (cli cliItem) Upgrade(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliItem) NewUpgradeCmd() *cobra.Command {
|
||||
func (cli cliItem) newUpgradeCmd() *cobra.Command {
|
||||
var (
|
||||
all bool
|
||||
force bool
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: coalesce.String(cli.upgradeHelp.use, "upgrade [item]..."),
|
||||
Short: coalesce.String(cli.upgradeHelp.short, fmt.Sprintf("Upgrade given %s", cli.oneOrMore)),
|
||||
|
@ -351,43 +326,27 @@ func (cli cliItem) NewUpgradeCmd() *cobra.Command {
|
|||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstalledItems(cli.name, args, toComplete)
|
||||
},
|
||||
RunE: cli.Upgrade,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.upgrade(args, force, all)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolP("all", "a", false, fmt.Sprintf("Upgrade all the %s", cli.name))
|
||||
flags.Bool("force", false, "Force upgrade: overwrite tainted and outdated files")
|
||||
flags.BoolVarP(&all, "all", "a", false, fmt.Sprintf("Upgrade all the %s", cli.name))
|
||||
flags.BoolVar(&force, "force", false, "Force upgrade: overwrite tainted and outdated files")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliItem) Inspect(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
url, err := flags.GetString("url")
|
||||
if err != nil {
|
||||
return err
|
||||
func (cli cliItem) inspect(args []string, url string, diff bool, rev bool, noMetrics bool) error {
|
||||
if rev && !diff {
|
||||
return fmt.Errorf("--rev can only be used with --diff")
|
||||
}
|
||||
|
||||
if url != "" {
|
||||
csConfig.Cscli.PrometheusUrl = url
|
||||
}
|
||||
|
||||
diff, err := flags.GetBool("diff")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rev, err := flags.GetBool("rev")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
noMetrics, err := flags.GetBool("no-metrics")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remote := (*cwhub.RemoteHubCfg)(nil)
|
||||
|
||||
if diff {
|
||||
|
@ -411,7 +370,7 @@ func (cli cliItem) Inspect(cmd *cobra.Command, args []string) error {
|
|||
continue
|
||||
}
|
||||
|
||||
if err = InspectItem(item, !noMetrics); err != nil {
|
||||
if err = inspectItem(item, !noMetrics); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -425,7 +384,14 @@ func (cli cliItem) Inspect(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliItem) NewInspectCmd() *cobra.Command {
|
||||
func (cli cliItem) newInspectCmd() *cobra.Command {
|
||||
var (
|
||||
url string
|
||||
diff bool
|
||||
rev bool
|
||||
noMetrics bool
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: coalesce.String(cli.inspectHelp.use, "inspect [item]..."),
|
||||
Short: coalesce.String(cli.inspectHelp.short, fmt.Sprintf("Inspect given %s", cli.oneOrMore)),
|
||||
|
@ -436,45 +402,21 @@ func (cli cliItem) NewInspectCmd() *cobra.Command {
|
|||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstalledItems(cli.name, args, toComplete)
|
||||
},
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
diff, err := flags.GetBool("diff")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rev, err := flags.GetBool("rev")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rev && !diff {
|
||||
return fmt.Errorf("--rev can only be used with --diff")
|
||||
}
|
||||
|
||||
return nil
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.inspect(args, url, diff, rev, noMetrics)
|
||||
},
|
||||
RunE: cli.Inspect,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringP("url", "u", "", "Prometheus url")
|
||||
flags.Bool("diff", false, "Show diff with latest version (for tainted items)")
|
||||
flags.Bool("rev", false, "Reverse diff output")
|
||||
flags.Bool("no-metrics", false, "Don't show metrics (when cscli.output=human)")
|
||||
flags.StringVarP(&url, "url", "u", "", "Prometheus url")
|
||||
flags.BoolVar(&diff, "diff", false, "Show diff with latest version (for tainted items)")
|
||||
flags.BoolVar(&rev, "rev", false, "Reverse diff output")
|
||||
flags.BoolVar(&noMetrics, "no-metrics", false, "Don't show metrics (when cscli.output=human)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliItem) List(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
all, err := flags.GetBool("all")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (cli cliItem) list(args []string, all bool) error {
|
||||
hub, err := require.Hub(csConfig, nil, log.StandardLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -494,18 +436,22 @@ func (cli cliItem) List(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliItem) NewListCmd() *cobra.Command {
|
||||
func (cli cliItem) newListCmd() *cobra.Command {
|
||||
var all bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: coalesce.String(cli.listHelp.use, "list [item... | -a]"),
|
||||
Short: coalesce.String(cli.listHelp.short, fmt.Sprintf("List %s", cli.oneOrMore)),
|
||||
Long: coalesce.String(cli.listHelp.long, fmt.Sprintf("List of installed/available/specified %s", cli.name)),
|
||||
Example: cli.listHelp.example,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: cli.List,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cli.list(args, all)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolP("all", "a", false, "List disabled items as well")
|
||||
flags.BoolVarP(&all, "all", "a", false, "List disabled items as well")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -138,14 +138,12 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
|
|||
}
|
||||
|
||||
csvwriter.Flush()
|
||||
default:
|
||||
return fmt.Errorf("unknown output format '%s'", csConfig.Cscli.Output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InspectItem(item *cwhub.Item, showMetrics bool) error {
|
||||
func inspectItem(item *cwhub.Item, showMetrics bool) error {
|
||||
switch csConfig.Cscli.Output {
|
||||
case "human", "raw":
|
||||
enc := yaml.NewEncoder(os.Stdout)
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -101,53 +100,18 @@ func getLastHeartbeat(m *ent.Machine) (string, bool) {
|
|||
return hb, true
|
||||
}
|
||||
|
||||
func getAgents(out io.Writer, dbClient *database.Client) error {
|
||||
machines, err := dbClient.ListMachines()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list machines: %s", err)
|
||||
}
|
||||
|
||||
switch csConfig.Cscli.Output {
|
||||
case "human":
|
||||
getAgentsTable(out, machines)
|
||||
case "json":
|
||||
enc := json.NewEncoder(out)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(machines); err != nil {
|
||||
return fmt.Errorf("failed to marshal")
|
||||
}
|
||||
return nil
|
||||
case "raw":
|
||||
csvwriter := csv.NewWriter(out)
|
||||
err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version", "auth_type", "last_heartbeat"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write header: %s", err)
|
||||
}
|
||||
for _, m := range machines {
|
||||
validated := "false"
|
||||
if m.IsValidated {
|
||||
validated = "true"
|
||||
}
|
||||
hb, _ := getLastHeartbeat(m)
|
||||
err := csvwriter.Write([]string{m.MachineId, m.IpAddress, m.UpdatedAt.Format(time.RFC3339), validated, m.Version, m.AuthType, hb})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write raw output: %w", err)
|
||||
}
|
||||
}
|
||||
csvwriter.Flush()
|
||||
default:
|
||||
return fmt.Errorf("unknown output '%s'", csConfig.Cscli.Output)
|
||||
}
|
||||
return nil
|
||||
type cliMachines struct{
|
||||
db *database.Client
|
||||
cfg configGetter
|
||||
}
|
||||
|
||||
type cliMachines struct{}
|
||||
|
||||
func NewCLIMachines() *cliMachines {
|
||||
return &cliMachines{}
|
||||
func NewCLIMachines(getconfig configGetter) *cliMachines {
|
||||
return &cliMachines{
|
||||
cfg: getconfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (cli cliMachines) NewCommand() *cobra.Command {
|
||||
func (cli *cliMachines) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "machines [action]",
|
||||
Short: "Manage local API machines [requires local API]",
|
||||
|
@ -159,10 +123,10 @@ Note: This command requires database direct access, so is intended to be run on
|
|||
Aliases: []string{"machine"},
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
var err error
|
||||
if err = require.LAPI(csConfig); err != nil {
|
||||
if err = require.LAPI(cli.cfg()); err != nil {
|
||||
return err
|
||||
}
|
||||
dbClient, err = database.NewClient(csConfig.DbConfig)
|
||||
cli.db, err = database.NewClient(cli.cfg().DbConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create new database client: %s", err)
|
||||
}
|
||||
|
@ -170,16 +134,63 @@ Note: This command requires database direct access, so is intended to be run on
|
|||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(cli.NewListCmd())
|
||||
cmd.AddCommand(cli.NewAddCmd())
|
||||
cmd.AddCommand(cli.NewDeleteCmd())
|
||||
cmd.AddCommand(cli.NewValidateCmd())
|
||||
cmd.AddCommand(cli.NewPruneCmd())
|
||||
cmd.AddCommand(cli.newListCmd())
|
||||
cmd.AddCommand(cli.newAddCmd())
|
||||
cmd.AddCommand(cli.newDeleteCmd())
|
||||
cmd.AddCommand(cli.newValidateCmd())
|
||||
cmd.AddCommand(cli.newPruneCmd())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliMachines) NewListCmd() *cobra.Command {
|
||||
func (cli *cliMachines) list() error {
|
||||
out := color.Output
|
||||
|
||||
machines, err := cli.db.ListMachines()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list machines: %s", err)
|
||||
}
|
||||
|
||||
switch cli.cfg().Cscli.Output {
|
||||
case "human":
|
||||
getAgentsTable(out, machines)
|
||||
case "json":
|
||||
enc := json.NewEncoder(out)
|
||||
enc.SetIndent("", " ")
|
||||
|
||||
if err := enc.Encode(machines); err != nil {
|
||||
return fmt.Errorf("failed to marshal")
|
||||
}
|
||||
|
||||
return nil
|
||||
case "raw":
|
||||
csvwriter := csv.NewWriter(out)
|
||||
|
||||
err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version", "auth_type", "last_heartbeat"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write header: %s", err)
|
||||
}
|
||||
|
||||
for _, m := range machines {
|
||||
validated := "false"
|
||||
if m.IsValidated {
|
||||
validated = "true"
|
||||
}
|
||||
|
||||
hb, _ := getLastHeartbeat(m)
|
||||
|
||||
if err := csvwriter.Write([]string{m.MachineId, m.IpAddress, m.UpdatedAt.Format(time.RFC3339), validated, m.Version, m.AuthType, hb}); err != nil {
|
||||
return fmt.Errorf("failed to write raw output: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
csvwriter.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *cliMachines) newListCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list all machines in the database",
|
||||
|
@ -188,84 +199,60 @@ func (cli cliMachines) NewListCmd() *cobra.Command {
|
|||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
err := getAgents(color.Output, dbClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list machines: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return cli.list()
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliMachines) NewAddCmd() *cobra.Command {
|
||||
func (cli *cliMachines) newAddCmd() *cobra.Command {
|
||||
var (
|
||||
password MachinePassword
|
||||
dumpFile string
|
||||
apiURL string
|
||||
interactive bool
|
||||
autoAdd bool
|
||||
force bool
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "add a single machine to the database",
|
||||
DisableAutoGenTag: true,
|
||||
Long: `Register a new machine in the database. cscli should be on the same machine as LAPI.`,
|
||||
Example: `
|
||||
cscli machines add --auto
|
||||
Example: `cscli machines add --auto
|
||||
cscli machines add MyTestMachine --auto
|
||||
cscli machines add MyTestMachine --password MyPassword
|
||||
`,
|
||||
RunE: cli.add,
|
||||
cscli machines add -f- --auto > /tmp/mycreds.yaml`,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cli.add(args, string(password), dumpFile, apiURL, interactive, autoAdd, force)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringP("password", "p", "", "machine password to login to the API")
|
||||
flags.StringP("file", "f", "", "output file destination (defaults to "+csconfig.DefaultConfigPath("local_api_credentials.yaml")+")")
|
||||
flags.StringP("url", "u", "", "URL of the local API")
|
||||
flags.BoolP("interactive", "i", false, "interfactive mode to enter the password")
|
||||
flags.BoolP("auto", "a", false, "automatically generate password (and username if not provided)")
|
||||
flags.Bool("force", false, "will force add the machine if it already exist")
|
||||
flags.VarP(&password, "password", "p", "machine password to login to the API")
|
||||
flags.StringVarP(&dumpFile, "file", "f", "", "output file destination (defaults to "+csconfig.DefaultConfigPath("local_api_credentials.yaml")+")")
|
||||
flags.StringVarP(&apiURL, "url", "u", "", "URL of the local API")
|
||||
flags.BoolVarP(&interactive, "interactive", "i", false, "interfactive mode to enter the password")
|
||||
flags.BoolVarP(&autoAdd, "auto", "a", false, "automatically generate password (and username if not provided)")
|
||||
flags.BoolVar(&force, "force", false, "will force add the machine if it already exist")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
machinePassword, err := flags.GetString("password")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dumpFile, err := flags.GetString("file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apiURL, err := flags.GetString("url")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
interactive, err := flags.GetBool("interactive")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
autoAdd, err := flags.GetBool("auto")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
force, err := flags.GetBool("force")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var machineID string
|
||||
func (cli *cliMachines) add(args []string, machinePassword string, dumpFile string, apiURL string, interactive bool, autoAdd bool, force bool) error {
|
||||
var (
|
||||
err error
|
||||
machineID string
|
||||
)
|
||||
|
||||
// create machineID if not specified by user
|
||||
if len(args) == 0 {
|
||||
if !autoAdd {
|
||||
printHelp(cmd)
|
||||
return nil
|
||||
return fmt.Errorf("please specify a machine name to add, or use --auto")
|
||||
}
|
||||
|
||||
machineID, err = generateID("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate machine id: %s", err)
|
||||
|
@ -274,15 +261,18 @@ func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
|
|||
machineID = args[0]
|
||||
}
|
||||
|
||||
clientCfg := cli.cfg().API.Client
|
||||
serverCfg := cli.cfg().API.Server
|
||||
|
||||
/*check if file already exists*/
|
||||
if dumpFile == "" && csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" {
|
||||
credFile := csConfig.API.Client.CredentialsFilePath
|
||||
if dumpFile == "" && clientCfg != nil && clientCfg.CredentialsFilePath != "" {
|
||||
credFile := clientCfg.CredentialsFilePath
|
||||
// use the default only if the file does not exist
|
||||
_, err = os.Stat(credFile)
|
||||
|
||||
switch {
|
||||
case os.IsNotExist(err) || force:
|
||||
dumpFile = csConfig.API.Client.CredentialsFilePath
|
||||
dumpFile = credFile
|
||||
case err != nil:
|
||||
return fmt.Errorf("unable to stat '%s': %s", credFile, err)
|
||||
default:
|
||||
|
@ -302,78 +292,74 @@ func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
|
|||
machinePassword = generatePassword(passwordLength)
|
||||
} else if machinePassword == "" && interactive {
|
||||
qs := &survey.Password{
|
||||
Message: "Please provide a password for the machine",
|
||||
Message: "Please provide a password for the machine:",
|
||||
}
|
||||
survey.AskOne(qs, &machinePassword)
|
||||
}
|
||||
|
||||
password := strfmt.Password(machinePassword)
|
||||
_, err = dbClient.CreateMachine(&machineID, &password, "", true, force, types.PasswordAuthType)
|
||||
|
||||
_, err = cli.db.CreateMachine(&machineID, &password, "", true, force, types.PasswordAuthType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create machine: %s", err)
|
||||
}
|
||||
fmt.Printf("Machine '%s' successfully added to the local API.\n", machineID)
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Machine '%s' successfully added to the local API.\n", machineID)
|
||||
|
||||
if apiURL == "" {
|
||||
if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil && csConfig.API.Client.Credentials.URL != "" {
|
||||
apiURL = csConfig.API.Client.Credentials.URL
|
||||
} else if csConfig.API.Server != nil && csConfig.API.Server.ListenURI != "" {
|
||||
apiURL = "http://" + csConfig.API.Server.ListenURI
|
||||
if clientCfg != nil && clientCfg.Credentials != nil && clientCfg.Credentials.URL != "" {
|
||||
apiURL = clientCfg.Credentials.URL
|
||||
} else if serverCfg != nil && serverCfg.ListenURI != "" {
|
||||
apiURL = "http://" + serverCfg.ListenURI
|
||||
} else {
|
||||
return fmt.Errorf("unable to dump an api URL. Please provide it in your configuration or with the -u parameter")
|
||||
}
|
||||
}
|
||||
|
||||
apiCfg := csconfig.ApiCredentialsCfg{
|
||||
Login: machineID,
|
||||
Password: password.String(),
|
||||
URL: apiURL,
|
||||
}
|
||||
|
||||
apiConfigDump, err := yaml.Marshal(apiCfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to marshal api credentials: %s", err)
|
||||
}
|
||||
|
||||
if dumpFile != "" && dumpFile != "-" {
|
||||
err = os.WriteFile(dumpFile, apiConfigDump, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err)
|
||||
}
|
||||
fmt.Printf("API credentials written to '%s'.\n", dumpFile)
|
||||
fmt.Fprintf(os.Stderr, "API credentials written to '%s'.\n", dumpFile)
|
||||
} else {
|
||||
fmt.Printf("%s\n", string(apiConfigDump))
|
||||
fmt.Print(string(apiConfigDump))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli cliMachines) NewDeleteCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete [machine_name]...",
|
||||
Short: "delete machine(s) by name",
|
||||
Example: `cscli machines delete "machine1" "machine2"`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Aliases: []string{"remove"},
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
machines, err := dbClient.ListMachines()
|
||||
if err != nil {
|
||||
cobra.CompError("unable to list machines " + err.Error())
|
||||
}
|
||||
ret := make([]string, 0)
|
||||
for _, machine := range machines {
|
||||
if strings.Contains(machine.MachineId, toComplete) && !slices.Contains(args, machine.MachineId) {
|
||||
ret = append(ret, machine.MachineId)
|
||||
}
|
||||
}
|
||||
return ret, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
RunE: cli.delete,
|
||||
func (cli *cliMachines) deleteValid(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
machines, err := cli.db.ListMachines()
|
||||
if err != nil {
|
||||
cobra.CompError("unable to list machines " + err.Error())
|
||||
}
|
||||
|
||||
return cmd
|
||||
ret := []string{}
|
||||
|
||||
for _, machine := range machines {
|
||||
if strings.Contains(machine.MachineId, toComplete) && !slices.Contains(args, machine.MachineId) {
|
||||
ret = append(ret, machine.MachineId)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func (cli cliMachines) delete(_ *cobra.Command, args []string) error {
|
||||
for _, machineID := range args {
|
||||
err := dbClient.DeleteWatcher(machineID)
|
||||
func (cli *cliMachines) delete(machines []string) error {
|
||||
for _, machineID := range machines {
|
||||
err := cli.db.DeleteWatcher(machineID)
|
||||
if err != nil {
|
||||
log.Errorf("unable to delete machine '%s': %s", machineID, err)
|
||||
return nil
|
||||
|
@ -384,8 +370,83 @@ func (cli cliMachines) delete(_ *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli cliMachines) NewPruneCmd() *cobra.Command {
|
||||
var parsedDuration time.Duration
|
||||
func (cli *cliMachines) newDeleteCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete [machine_name]...",
|
||||
Short: "delete machine(s) by name",
|
||||
Example: `cscli machines delete "machine1" "machine2"`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Aliases: []string{"remove"},
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: cli.deleteValid,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cli.delete(args)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli *cliMachines) prune(duration time.Duration, notValidOnly bool, force bool) error {
|
||||
if duration < 2*time.Minute && !notValidOnly {
|
||||
if yes, err := askYesNo(
|
||||
"The duration you provided is less than 2 minutes. " +
|
||||
"This can break installations if the machines are only temporarily disconnected. Continue?", false); err != nil {
|
||||
return err
|
||||
} else if !yes {
|
||||
fmt.Println("User aborted prune. No changes were made.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
machines := []*ent.Machine{}
|
||||
if pending, err := cli.db.QueryPendingMachine(); err == nil {
|
||||
machines = append(machines, pending...)
|
||||
}
|
||||
|
||||
if !notValidOnly {
|
||||
if pending, err := cli.db.QueryLastValidatedHeartbeatLT(time.Now().UTC().Add(duration)); err == nil {
|
||||
machines = append(machines, pending...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(machines) == 0 {
|
||||
fmt.Println("no machines to prune")
|
||||
return nil
|
||||
}
|
||||
|
||||
getAgentsTable(color.Output, machines)
|
||||
|
||||
if !force {
|
||||
if yes, err := askYesNo(
|
||||
"You are about to PERMANENTLY remove the above machines from the database. " +
|
||||
"These will NOT be recoverable. Continue?", false); err != nil {
|
||||
return err
|
||||
} else if !yes {
|
||||
fmt.Println("User aborted prune. No changes were made.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
deleted, err := cli.db.BulkDeleteWatchers(machines)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to prune machines: %s", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "successfully delete %d machines\n", deleted)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *cliMachines) newPruneCmd() *cobra.Command {
|
||||
var (
|
||||
duration time.Duration
|
||||
notValidOnly bool
|
||||
force bool
|
||||
)
|
||||
|
||||
const defaultDuration = 10 * time.Minute
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "prune",
|
||||
Short: "prune multiple machines from the database",
|
||||
|
@ -395,76 +456,29 @@ cscli machines prune --duration 1h
|
|||
cscli machines prune --not-validated-only --force`,
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
dur, _ := cmd.Flags().GetString("duration")
|
||||
var err error
|
||||
parsedDuration, err = time.ParseDuration(fmt.Sprintf("-%s", dur))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse duration '%s': %s", dur, err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
notValidOnly, _ := cmd.Flags().GetBool("not-validated-only")
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
if parsedDuration >= 0-60*time.Second && !notValidOnly {
|
||||
var answer bool
|
||||
prompt := &survey.Confirm{
|
||||
Message: "The duration you provided is less than or equal 60 seconds this can break installations do you want to continue ?",
|
||||
Default: false,
|
||||
}
|
||||
if err := survey.AskOne(prompt, &answer); err != nil {
|
||||
return fmt.Errorf("unable to ask about prune check: %s", err)
|
||||
}
|
||||
if !answer {
|
||||
fmt.Println("user aborted prune no changes were made")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
machines := make([]*ent.Machine, 0)
|
||||
if pending, err := dbClient.QueryPendingMachine(); err == nil {
|
||||
machines = append(machines, pending...)
|
||||
}
|
||||
if !notValidOnly {
|
||||
if pending, err := dbClient.QueryLastValidatedHeartbeatLT(time.Now().UTC().Add(parsedDuration)); err == nil {
|
||||
machines = append(machines, pending...)
|
||||
}
|
||||
}
|
||||
if len(machines) == 0 {
|
||||
fmt.Println("no machines to prune")
|
||||
return nil
|
||||
}
|
||||
getAgentsTable(color.Output, machines)
|
||||
if !force {
|
||||
var answer bool
|
||||
prompt := &survey.Confirm{
|
||||
Message: "You are about to PERMANENTLY remove the above machines from the database these will NOT be recoverable, continue ?",
|
||||
Default: false,
|
||||
}
|
||||
if err := survey.AskOne(prompt, &answer); err != nil {
|
||||
return fmt.Errorf("unable to ask about prune check: %s", err)
|
||||
}
|
||||
if !answer {
|
||||
fmt.Println("user aborted prune no changes were made")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
nbDeleted, err := dbClient.BulkDeleteWatchers(machines)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to prune machines: %s", err)
|
||||
}
|
||||
fmt.Printf("successfully delete %d machines\n", nbDeleted)
|
||||
return nil
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.prune(duration, notValidOnly, force)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringP("duration", "d", "10m", "duration of time since validated machine last heartbeat")
|
||||
cmd.Flags().Bool("not-validated-only", false, "only prune machines that are not validated")
|
||||
cmd.Flags().Bool("force", false, "force prune without asking for confirmation")
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.DurationVarP(&duration, "duration", "d", defaultDuration, "duration of time since validated machine last heartbeat")
|
||||
flags.BoolVar(¬ValidOnly, "not-validated-only", false, "only prune machines that are not validated")
|
||||
flags.BoolVar(&force, "force", false, "force prune without asking for confirmation")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliMachines) NewValidateCmd() *cobra.Command {
|
||||
func (cli *cliMachines) validate(machineID string) error {
|
||||
if err := cli.db.ValidateMachine(machineID); err != nil {
|
||||
return fmt.Errorf("unable to validate machine '%s': %s", machineID, err)
|
||||
}
|
||||
log.Infof("machine '%s' validated successfully", machineID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *cliMachines) newValidateCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "validate a machine to access the local API",
|
||||
|
@ -472,14 +486,8 @@ func (cli cliMachines) NewValidateCmd() *cobra.Command {
|
|||
Example: `cscli machines validate "machine_name"`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
machineID := args[0]
|
||||
if err := dbClient.ValidateMachine(machineID); err != nil {
|
||||
return fmt.Errorf("unable to validate machine '%s': %s", machineID, err)
|
||||
}
|
||||
log.Infof("machine '%s' validated successfully", machineID)
|
||||
|
||||
return nil
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.validate(args[0])
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ var ConfigFilePath string
|
|||
var csConfig *csconfig.Config
|
||||
var dbClient *database.Client
|
||||
|
||||
var OutputFormat string
|
||||
var outputFormat string
|
||||
var OutputColor string
|
||||
|
||||
var mergedConfig string
|
||||
|
@ -29,6 +29,8 @@ var mergedConfig string
|
|||
// flagBranch overrides the value in csConfig.Cscli.HubBranch
|
||||
var flagBranch = ""
|
||||
|
||||
type configGetter func() *csconfig.Config
|
||||
|
||||
func initConfig() {
|
||||
var err error
|
||||
|
||||
|
@ -64,16 +66,18 @@ func initConfig() {
|
|||
csConfig.Cscli.HubBranch = flagBranch
|
||||
}
|
||||
|
||||
if OutputFormat != "" {
|
||||
csConfig.Cscli.Output = OutputFormat
|
||||
|
||||
if OutputFormat != "json" && OutputFormat != "raw" && OutputFormat != "human" {
|
||||
log.Fatalf("output format %s unknown", OutputFormat)
|
||||
}
|
||||
if outputFormat != "" {
|
||||
csConfig.Cscli.Output = outputFormat
|
||||
}
|
||||
|
||||
if csConfig.Cscli.Output == "" {
|
||||
csConfig.Cscli.Output = "human"
|
||||
}
|
||||
|
||||
if csConfig.Cscli.Output != "human" && csConfig.Cscli.Output != "json" && csConfig.Cscli.Output != "raw" {
|
||||
log.Fatalf("output format '%s' not supported: must be one of human, json, raw", csConfig.Cscli.Output)
|
||||
}
|
||||
|
||||
if csConfig.Cscli.Output == "json" {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
|
@ -146,7 +150,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
|||
cmd.SetOut(color.Output)
|
||||
|
||||
cmd.PersistentFlags().StringVarP(&ConfigFilePath, "config", "c", csconfig.DefaultConfigPath("config.yaml"), "path to crowdsec config file")
|
||||
cmd.PersistentFlags().StringVarP(&OutputFormat, "output", "o", "", "Output format: human, json, raw")
|
||||
cmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "", "Output format: human, json, raw")
|
||||
cmd.PersistentFlags().StringVarP(&OutputColor, "color", "", "auto", "Output color: yes, no, auto")
|
||||
cmd.PersistentFlags().BoolVar(&dbg_lvl, "debug", false, "Set logging to debug")
|
||||
cmd.PersistentFlags().BoolVar(&nfo_lvl, "info", false, "Set logging to info")
|
||||
|
@ -182,17 +186,22 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
|||
cmd.Flags().SortFlags = false
|
||||
cmd.PersistentFlags().SortFlags = false
|
||||
|
||||
// we use a getter because the config is not initialized until the Execute() call
|
||||
getconfig := func() *csconfig.Config {
|
||||
return csConfig
|
||||
}
|
||||
|
||||
cmd.AddCommand(NewCLIDoc().NewCommand(cmd))
|
||||
cmd.AddCommand(NewCLIVersion().NewCommand())
|
||||
cmd.AddCommand(NewConfigCmd())
|
||||
cmd.AddCommand(NewCLIHub().NewCommand())
|
||||
cmd.AddCommand(NewCLIHub(getconfig).NewCommand())
|
||||
cmd.AddCommand(NewMetricsCmd())
|
||||
cmd.AddCommand(NewCLIDashboard().NewCommand())
|
||||
cmd.AddCommand(NewCLIDashboard(getconfig).NewCommand())
|
||||
cmd.AddCommand(NewCLIDecisions().NewCommand())
|
||||
cmd.AddCommand(NewCLIAlerts().NewCommand())
|
||||
cmd.AddCommand(NewCLISimulation().NewCommand())
|
||||
cmd.AddCommand(NewCLIBouncers().NewCommand())
|
||||
cmd.AddCommand(NewCLIMachines().NewCommand())
|
||||
cmd.AddCommand(NewCLISimulation(getconfig).NewCommand())
|
||||
cmd.AddCommand(NewCLIBouncers(getconfig).NewCommand())
|
||||
cmd.AddCommand(NewCLIMachines(getconfig).NewCommand())
|
||||
cmd.AddCommand(NewCLICapi().NewCommand())
|
||||
cmd.AddCommand(NewLapiCmd())
|
||||
cmd.AddCommand(NewCompletionCmd())
|
||||
|
@ -201,7 +210,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
|||
cmd.AddCommand(NewCLIHubTest().NewCommand())
|
||||
cmd.AddCommand(NewCLINotifications().NewCommand())
|
||||
cmd.AddCommand(NewCLISupport().NewCommand())
|
||||
cmd.AddCommand(NewCLIPapi().NewCommand())
|
||||
cmd.AddCommand(NewCLIPapi(getconfig).NewCommand())
|
||||
cmd.AddCommand(NewCLICollection().NewCommand())
|
||||
cmd.AddCommand(NewCLIParser().NewCommand())
|
||||
cmd.AddCommand(NewCLIScenario().NewCommand())
|
||||
|
@ -214,10 +223,6 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
|||
cmd.AddCommand(NewSetupCmd())
|
||||
}
|
||||
|
||||
if fflag.PapiClient.IsEnabled() {
|
||||
cmd.AddCommand(NewCLIPapi().NewCommand())
|
||||
}
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -15,26 +16,31 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
)
|
||||
|
||||
type cliPapi struct {}
|
||||
|
||||
func NewCLIPapi() *cliPapi {
|
||||
return &cliPapi{}
|
||||
type cliPapi struct {
|
||||
cfg configGetter
|
||||
}
|
||||
|
||||
func (cli cliPapi) NewCommand() *cobra.Command {
|
||||
var cmd = &cobra.Command{
|
||||
func NewCLIPapi(getconfig configGetter) *cliPapi {
|
||||
return &cliPapi{
|
||||
cfg: getconfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *cliPapi) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "papi [action]",
|
||||
Short: "Manage interaction with Polling API (PAPI)",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := require.LAPI(csConfig); err != nil {
|
||||
cfg := cli.cfg()
|
||||
if err := require.LAPI(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := require.CAPI(csConfig); err != nil {
|
||||
if err := require.CAPI(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := require.PAPI(csConfig); err != nil {
|
||||
if err := require.PAPI(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -47,35 +53,36 @@ func (cli cliPapi) NewCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliPapi) NewStatusCmd() *cobra.Command {
|
||||
func (cli *cliPapi) NewStatusCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Get status of the Polling API",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
dbClient, err = database.NewClient(csConfig.DbConfig)
|
||||
cfg := cli.cfg()
|
||||
dbClient, err = database.NewClient(cfg.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to initialize database client : %s", err)
|
||||
return fmt.Errorf("unable to initialize database client: %s", err)
|
||||
}
|
||||
|
||||
apic, err := apiserver.NewAPIC(csConfig.API.Server.OnlineClient, dbClient, csConfig.API.Server.ConsoleConfig, csConfig.API.Server.CapiWhitelists)
|
||||
apic, err := apiserver.NewAPIC(cfg.API.Server.OnlineClient, dbClient, cfg.API.Server.ConsoleConfig, cfg.API.Server.CapiWhitelists)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("unable to initialize API client : %s", err)
|
||||
return fmt.Errorf("unable to initialize API client: %s", err)
|
||||
}
|
||||
|
||||
papi, err := apiserver.NewPAPI(apic, dbClient, csConfig.API.Server.ConsoleConfig, log.GetLevel())
|
||||
papi, err := apiserver.NewPAPI(apic, dbClient, cfg.API.Server.ConsoleConfig, log.GetLevel())
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("unable to initialize PAPI client : %s", err)
|
||||
return fmt.Errorf("unable to initialize PAPI client: %s", err)
|
||||
}
|
||||
|
||||
perms, err := papi.GetPermissions()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("unable to get PAPI permissions: %s", err)
|
||||
return fmt.Errorf("unable to get PAPI permissions: %s", err)
|
||||
}
|
||||
var lastTimestampStr *string
|
||||
lastTimestampStr, err = dbClient.GetConfigItem(apiserver.PapiPullKey)
|
||||
|
@ -90,45 +97,48 @@ func (cli cliPapi) NewStatusCmd() *cobra.Command {
|
|||
for _, sub := range perms.Categories {
|
||||
log.Infof(" - %s", sub)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliPapi) NewSyncCmd() *cobra.Command {
|
||||
func (cli *cliPapi) NewSyncCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "sync",
|
||||
Short: "Sync with the Polling API, pulling all non-expired orders for the instance",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
cfg := cli.cfg()
|
||||
t := tomb.Tomb{}
|
||||
dbClient, err = database.NewClient(csConfig.DbConfig)
|
||||
|
||||
dbClient, err = database.NewClient(cfg.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to initialize database client : %s", err)
|
||||
return fmt.Errorf("unable to initialize database client: %s", err)
|
||||
}
|
||||
|
||||
apic, err := apiserver.NewAPIC(csConfig.API.Server.OnlineClient, dbClient, csConfig.API.Server.ConsoleConfig, csConfig.API.Server.CapiWhitelists)
|
||||
|
||||
apic, err := apiserver.NewAPIC(cfg.API.Server.OnlineClient, dbClient, cfg.API.Server.ConsoleConfig, cfg.API.Server.CapiWhitelists)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to initialize API client : %s", err)
|
||||
return fmt.Errorf("unable to initialize API client: %s", err)
|
||||
}
|
||||
|
||||
t.Go(apic.Push)
|
||||
|
||||
papi, err := apiserver.NewPAPI(apic, dbClient, csConfig.API.Server.ConsoleConfig, log.GetLevel())
|
||||
|
||||
papi, err := apiserver.NewPAPI(apic, dbClient, cfg.API.Server.ConsoleConfig, log.GetLevel())
|
||||
if err != nil {
|
||||
log.Fatalf("unable to initialize PAPI client : %s", err)
|
||||
return fmt.Errorf("unable to initialize PAPI client: %s", err)
|
||||
}
|
||||
|
||||
t.Go(papi.SyncDecisions)
|
||||
|
||||
err = papi.PullOnce(time.Time{}, true)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("unable to sync decisions: %s", err)
|
||||
return fmt.Errorf("unable to sync decisions: %s", err)
|
||||
}
|
||||
|
||||
log.Infof("Sending acknowledgements to CAPI")
|
||||
|
@ -138,6 +148,7 @@ func (cli cliPapi) NewSyncCmd() *cobra.Command {
|
|||
t.Wait()
|
||||
time.Sleep(5 * time.Second) //FIXME: the push done by apic.Push is run inside a sub goroutine, sleep to make sure it's done
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,17 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||
)
|
||||
|
||||
type cliSimulation struct{}
|
||||
|
||||
func NewCLISimulation() *cliSimulation {
|
||||
return &cliSimulation{}
|
||||
type cliSimulation struct{
|
||||
cfg configGetter
|
||||
}
|
||||
|
||||
func (cli cliSimulation) NewCommand() *cobra.Command {
|
||||
func NewCLISimulation(getconfig configGetter) *cliSimulation {
|
||||
return &cliSimulation{
|
||||
cfg: getconfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *cliSimulation) NewCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "simulation [command]",
|
||||
Short: "Manage simulation status of scenarios",
|
||||
|
@ -27,16 +31,16 @@ func (cli cliSimulation) NewCommand() *cobra.Command {
|
|||
cscli simulation enable crowdsecurity/ssh-bf
|
||||
cscli simulation disable crowdsecurity/ssh-bf`,
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := csConfig.LoadSimulation(); err != nil {
|
||||
log.Fatal(err)
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
if err := cli.cfg().LoadSimulation(); err != nil {
|
||||
return err
|
||||
}
|
||||
if csConfig.Cscli.SimulationConfig == nil {
|
||||
if cli.cfg().Cscli.SimulationConfig == nil {
|
||||
return fmt.Errorf("no simulation configured")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
PersistentPostRun: func(cmd *cobra.Command, _ []string) {
|
||||
if cmd.Name() != "status" {
|
||||
log.Infof(ReloadMessage())
|
||||
}
|
||||
|
@ -52,7 +56,7 @@ cscli simulation disable crowdsecurity/ssh-bf`,
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliSimulation) NewEnableCmd() *cobra.Command {
|
||||
func (cli *cliSimulation) NewEnableCmd() *cobra.Command {
|
||||
var forceGlobalSimulation bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -60,10 +64,10 @@ func (cli cliSimulation) NewEnableCmd() *cobra.Command {
|
|||
Short: "Enable the simulation, globally or on specified scenarios",
|
||||
Example: `cscli simulation enable`,
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
hub, err := require.Hub(csConfig, nil, nil)
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
hub, err := require.Hub(cli.cfg(), nil, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
|
@ -76,37 +80,35 @@ func (cli cliSimulation) NewEnableCmd() *cobra.Command {
|
|||
if !item.State.Installed {
|
||||
log.Warningf("'%s' isn't enabled", scenario)
|
||||
}
|
||||
isExcluded := slices.Contains(csConfig.Cscli.SimulationConfig.Exclusions, scenario)
|
||||
if *csConfig.Cscli.SimulationConfig.Simulation && !isExcluded {
|
||||
isExcluded := slices.Contains(cli.cfg().Cscli.SimulationConfig.Exclusions, scenario)
|
||||
if *cli.cfg().Cscli.SimulationConfig.Simulation && !isExcluded {
|
||||
log.Warning("global simulation is already enabled")
|
||||
continue
|
||||
}
|
||||
if !*csConfig.Cscli.SimulationConfig.Simulation && isExcluded {
|
||||
if !*cli.cfg().Cscli.SimulationConfig.Simulation && isExcluded {
|
||||
log.Warningf("simulation for '%s' already enabled", scenario)
|
||||
continue
|
||||
}
|
||||
if *csConfig.Cscli.SimulationConfig.Simulation && isExcluded {
|
||||
if err := removeFromExclusion(scenario); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if *cli.cfg().Cscli.SimulationConfig.Simulation && isExcluded {
|
||||
cli.removeFromExclusion(scenario)
|
||||
log.Printf("simulation enabled for '%s'", scenario)
|
||||
continue
|
||||
}
|
||||
if err := addToExclusion(scenario); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cli.addToExclusion(scenario)
|
||||
log.Printf("simulation mode for '%s' enabled", scenario)
|
||||
}
|
||||
if err := dumpSimulationFile(); err != nil {
|
||||
log.Fatalf("simulation enable: %s", err)
|
||||
if err := cli.dumpSimulationFile(); err != nil {
|
||||
return fmt.Errorf("simulation enable: %s", err)
|
||||
}
|
||||
} else if forceGlobalSimulation {
|
||||
if err := enableGlobalSimulation(); err != nil {
|
||||
log.Fatalf("unable to enable global simulation mode : %s", err)
|
||||
if err := cli.enableGlobalSimulation(); err != nil {
|
||||
return fmt.Errorf("unable to enable global simulation mode: %s", err)
|
||||
}
|
||||
} else {
|
||||
printHelp(cmd)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVarP(&forceGlobalSimulation, "global", "g", false, "Enable global simulation (reverse mode)")
|
||||
|
@ -114,7 +116,7 @@ func (cli cliSimulation) NewEnableCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliSimulation) NewDisableCmd() *cobra.Command {
|
||||
func (cli *cliSimulation) NewDisableCmd() *cobra.Command {
|
||||
var forceGlobalSimulation bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -122,18 +124,16 @@ func (cli cliSimulation) NewDisableCmd() *cobra.Command {
|
|||
Short: "Disable the simulation mode. Disable only specified scenarios",
|
||||
Example: `cscli simulation disable`,
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
for _, scenario := range args {
|
||||
isExcluded := slices.Contains(csConfig.Cscli.SimulationConfig.Exclusions, scenario)
|
||||
if !*csConfig.Cscli.SimulationConfig.Simulation && !isExcluded {
|
||||
isExcluded := slices.Contains(cli.cfg().Cscli.SimulationConfig.Exclusions, scenario)
|
||||
if !*cli.cfg().Cscli.SimulationConfig.Simulation && !isExcluded {
|
||||
log.Warningf("%s isn't in simulation mode", scenario)
|
||||
continue
|
||||
}
|
||||
if !*csConfig.Cscli.SimulationConfig.Simulation && isExcluded {
|
||||
if err := removeFromExclusion(scenario); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !*cli.cfg().Cscli.SimulationConfig.Simulation && isExcluded {
|
||||
cli.removeFromExclusion(scenario)
|
||||
log.Printf("simulation mode for '%s' disabled", scenario)
|
||||
continue
|
||||
}
|
||||
|
@ -141,21 +141,21 @@ func (cli cliSimulation) NewDisableCmd() *cobra.Command {
|
|||
log.Warningf("simulation mode is enabled but is already disable for '%s'", scenario)
|
||||
continue
|
||||
}
|
||||
if err := addToExclusion(scenario); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cli.addToExclusion(scenario)
|
||||
log.Printf("simulation mode for '%s' disabled", scenario)
|
||||
}
|
||||
if err := dumpSimulationFile(); err != nil {
|
||||
log.Fatalf("simulation disable: %s", err)
|
||||
if err := cli.dumpSimulationFile(); err != nil {
|
||||
return fmt.Errorf("simulation disable: %s", err)
|
||||
}
|
||||
} else if forceGlobalSimulation {
|
||||
if err := disableGlobalSimulation(); err != nil {
|
||||
log.Fatalf("unable to disable global simulation mode : %s", err)
|
||||
if err := cli.disableGlobalSimulation(); err != nil {
|
||||
return fmt.Errorf("unable to disable global simulation mode: %s", err)
|
||||
}
|
||||
} else {
|
||||
printHelp(cmd)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVarP(&forceGlobalSimulation, "global", "g", false, "Disable global simulation (reverse mode)")
|
||||
|
@ -163,16 +163,14 @@ func (cli cliSimulation) NewDisableCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (cli cliSimulation) NewStatusCmd() *cobra.Command {
|
||||
func (cli *cliSimulation) NewStatusCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Show simulation mode status",
|
||||
Example: `cscli simulation status`,
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := simulationStatus(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
cli.status()
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
},
|
||||
|
@ -181,29 +179,29 @@ func (cli cliSimulation) NewStatusCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func addToExclusion(name string) error {
|
||||
csConfig.Cscli.SimulationConfig.Exclusions = append(csConfig.Cscli.SimulationConfig.Exclusions, name)
|
||||
return nil
|
||||
func (cli *cliSimulation) addToExclusion(name string) {
|
||||
cfg := cli.cfg()
|
||||
cfg.Cscli.SimulationConfig.Exclusions = append(cfg.Cscli.SimulationConfig.Exclusions, name)
|
||||
}
|
||||
|
||||
func removeFromExclusion(name string) error {
|
||||
index := slices.Index(csConfig.Cscli.SimulationConfig.Exclusions, name)
|
||||
func (cli *cliSimulation) removeFromExclusion(name string) {
|
||||
cfg := cli.cfg()
|
||||
index := slices.Index(cfg.Cscli.SimulationConfig.Exclusions, name)
|
||||
|
||||
// Remove element from the slice
|
||||
csConfig.Cscli.SimulationConfig.Exclusions[index] = csConfig.Cscli.SimulationConfig.Exclusions[len(csConfig.Cscli.SimulationConfig.Exclusions)-1]
|
||||
csConfig.Cscli.SimulationConfig.Exclusions[len(csConfig.Cscli.SimulationConfig.Exclusions)-1] = ""
|
||||
csConfig.Cscli.SimulationConfig.Exclusions = csConfig.Cscli.SimulationConfig.Exclusions[:len(csConfig.Cscli.SimulationConfig.Exclusions)-1]
|
||||
|
||||
return nil
|
||||
cfg.Cscli.SimulationConfig.Exclusions[index] = cfg.Cscli.SimulationConfig.Exclusions[len(cfg.Cscli.SimulationConfig.Exclusions)-1]
|
||||
cfg.Cscli.SimulationConfig.Exclusions[len(cfg.Cscli.SimulationConfig.Exclusions)-1] = ""
|
||||
cfg.Cscli.SimulationConfig.Exclusions = cfg.Cscli.SimulationConfig.Exclusions[:len(cfg.Cscli.SimulationConfig.Exclusions)-1]
|
||||
}
|
||||
|
||||
func enableGlobalSimulation() error {
|
||||
csConfig.Cscli.SimulationConfig.Simulation = new(bool)
|
||||
*csConfig.Cscli.SimulationConfig.Simulation = true
|
||||
csConfig.Cscli.SimulationConfig.Exclusions = []string{}
|
||||
func (cli *cliSimulation) enableGlobalSimulation() error {
|
||||
cfg := cli.cfg()
|
||||
cfg.Cscli.SimulationConfig.Simulation = new(bool)
|
||||
*cfg.Cscli.SimulationConfig.Simulation = true
|
||||
cfg.Cscli.SimulationConfig.Exclusions = []string{}
|
||||
|
||||
if err := dumpSimulationFile(); err != nil {
|
||||
log.Fatalf("unable to dump simulation file: %s", err)
|
||||
if err := cli.dumpSimulationFile(); err != nil {
|
||||
return fmt.Errorf("unable to dump simulation file: %s", err)
|
||||
}
|
||||
|
||||
log.Printf("global simulation: enabled")
|
||||
|
@ -211,59 +209,61 @@ func enableGlobalSimulation() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func dumpSimulationFile() error {
|
||||
newConfigSim, err := yaml.Marshal(csConfig.Cscli.SimulationConfig)
|
||||
func (cli *cliSimulation) dumpSimulationFile() error {
|
||||
cfg := cli.cfg()
|
||||
newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to marshal simulation configuration: %s", err)
|
||||
}
|
||||
err = os.WriteFile(csConfig.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
||||
err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("write simulation config in '%s' failed: %s", csConfig.ConfigPaths.SimulationFilePath, err)
|
||||
return fmt.Errorf("write simulation config in '%s' failed: %s", cfg.ConfigPaths.SimulationFilePath, err)
|
||||
}
|
||||
log.Debugf("updated simulation file %s", csConfig.ConfigPaths.SimulationFilePath)
|
||||
log.Debugf("updated simulation file %s", cfg.ConfigPaths.SimulationFilePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func disableGlobalSimulation() error {
|
||||
csConfig.Cscli.SimulationConfig.Simulation = new(bool)
|
||||
*csConfig.Cscli.SimulationConfig.Simulation = false
|
||||
func (cli *cliSimulation) disableGlobalSimulation() error {
|
||||
cfg := cli.cfg()
|
||||
cfg.Cscli.SimulationConfig.Simulation = new(bool)
|
||||
*cfg.Cscli.SimulationConfig.Simulation = false
|
||||
|
||||
csConfig.Cscli.SimulationConfig.Exclusions = []string{}
|
||||
newConfigSim, err := yaml.Marshal(csConfig.Cscli.SimulationConfig)
|
||||
cfg.Cscli.SimulationConfig.Exclusions = []string{}
|
||||
newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to marshal new simulation configuration: %s", err)
|
||||
}
|
||||
err = os.WriteFile(csConfig.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
||||
err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write new simulation config in '%s' : %s", csConfig.ConfigPaths.SimulationFilePath, err)
|
||||
return fmt.Errorf("unable to write new simulation config in '%s' : %s", cfg.ConfigPaths.SimulationFilePath, err)
|
||||
}
|
||||
|
||||
log.Printf("global simulation: disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
func simulationStatus() error {
|
||||
if csConfig.Cscli.SimulationConfig == nil {
|
||||
func (cli *cliSimulation) status() {
|
||||
cfg := cli.cfg()
|
||||
if cfg.Cscli.SimulationConfig == nil {
|
||||
log.Printf("global simulation: disabled (configuration file is missing)")
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if *csConfig.Cscli.SimulationConfig.Simulation {
|
||||
if *cfg.Cscli.SimulationConfig.Simulation {
|
||||
log.Println("global simulation: enabled")
|
||||
if len(csConfig.Cscli.SimulationConfig.Exclusions) > 0 {
|
||||
if len(cfg.Cscli.SimulationConfig.Exclusions) > 0 {
|
||||
log.Println("Scenarios not in simulation mode :")
|
||||
for _, scenario := range csConfig.Cscli.SimulationConfig.Exclusions {
|
||||
for _, scenario := range cfg.Cscli.SimulationConfig.Exclusions {
|
||||
log.Printf(" - %s", scenario)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Println("global simulation: disabled")
|
||||
if len(csConfig.Cscli.SimulationConfig.Exclusions) > 0 {
|
||||
if len(cfg.Cscli.SimulationConfig.Exclusions) > 0 {
|
||||
log.Println("Scenarios in simulation mode :")
|
||||
for _, scenario := range csConfig.Cscli.SimulationConfig.Exclusions {
|
||||
for _, scenario := range cfg.Cscli.SimulationConfig.Exclusions {
|
||||
log.Printf(" - %s", scenario)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -149,19 +149,21 @@ func collectHubItems(hub *cwhub.Hub, itemType string) []byte {
|
|||
|
||||
func collectBouncers(dbClient *database.Client) ([]byte, error) {
|
||||
out := bytes.NewBuffer(nil)
|
||||
err := getBouncers(out, dbClient)
|
||||
bouncers, err := dbClient.ListBouncers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to list bouncers: %s", err)
|
||||
}
|
||||
getBouncersTable(out, bouncers)
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func collectAgents(dbClient *database.Client) ([]byte, error) {
|
||||
out := bytes.NewBuffer(nil)
|
||||
err := getAgents(out, dbClient)
|
||||
machines, err := dbClient.ListMachines()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to list machines: %s", err)
|
||||
}
|
||||
getAgentsTable(out, machines)
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -47,17 +46,6 @@ func manageCliDecisionAlerts(ip *string, ipRange *string, scope *string, value *
|
|||
return nil
|
||||
}
|
||||
|
||||
func getDBClient() (*database.Client, error) {
|
||||
if err := csConfig.LoadAPIServer(true); err != nil || csConfig.DisableAPI {
|
||||
return nil, err
|
||||
}
|
||||
ret, err := database.NewClient(csConfig.DbConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func removeFromSlice(val string, slice []string) []string {
|
||||
var i int
|
||||
var value string
|
||||
|
|
|
@ -316,6 +316,7 @@ config.yaml) each time the container is run.
|
|||
| `BOUNCERS_ALLOWED_OU` | bouncer-ou | OU values allowed for bouncers, separated by comma |
|
||||
| | | |
|
||||
| __Hub management__ | | |
|
||||
| `NO_HUB_UPGRADE` | false | Skip hub update / upgrade when the container starts |
|
||||
| `COLLECTIONS` | | Collections to install, separated by space: `-e COLLECTIONS="crowdsecurity/linux crowdsecurity/apache2"` |
|
||||
| `PARSERS` | | Parsers to install, separated by space |
|
||||
| `SCENARIOS` | | Scenarios to install, separated by space |
|
||||
|
|
|
@ -303,8 +303,12 @@ fi
|
|||
conf_set_if "$PLUGIN_DIR" '.config_paths.plugin_dir = strenv(PLUGIN_DIR)'
|
||||
|
||||
## Install hub items
|
||||
|
||||
cscli hub update || true
|
||||
cscli hub upgrade || true
|
||||
|
||||
if isfalse "$NO_HUB_UPGRADE"; then
|
||||
cscli hub upgrade || true
|
||||
fi
|
||||
|
||||
cscli_if_clean parsers install crowdsecurity/docker-logs
|
||||
cscli_if_clean parsers install crowdsecurity/cri-logs
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[packages]
|
||||
pytest-dotenv = "0.5.2"
|
||||
pytest-xdist = "3.5.0"
|
||||
pytest-cs = {ref = "0.7.18", git = "https://github.com/crowdsecurity/pytest-cs.git"}
|
||||
pytest-cs = {ref = "0.7.19", git = "https://github.com/crowdsecurity/pytest-cs.git"}
|
||||
|
||||
[dev-packages]
|
||||
gnureadline = "8.1.2"
|
||||
|
|
142
docker/test/Pipfile.lock
generated
142
docker/test/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "575cb97d0b7fb66caf843191b843724307f7bc39c3c160f22330ba38ee055c80"
|
||||
"sha256": "b5d25a7199d15a900b285be1af97cf7b7083c6637d631ad777b454471c8319fe"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -79,7 +79,7 @@
|
|||
"sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956",
|
||||
"sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"markers": "platform_python_implementation != 'PyPy'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
|
@ -180,32 +180,41 @@
|
|||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960",
|
||||
"sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a",
|
||||
"sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc",
|
||||
"sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a",
|
||||
"sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf",
|
||||
"sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1",
|
||||
"sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39",
|
||||
"sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406",
|
||||
"sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a",
|
||||
"sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a",
|
||||
"sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c",
|
||||
"sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be",
|
||||
"sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15",
|
||||
"sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2",
|
||||
"sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d",
|
||||
"sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157",
|
||||
"sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003",
|
||||
"sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248",
|
||||
"sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a",
|
||||
"sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec",
|
||||
"sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309",
|
||||
"sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7",
|
||||
"sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"
|
||||
"sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380",
|
||||
"sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589",
|
||||
"sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea",
|
||||
"sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65",
|
||||
"sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a",
|
||||
"sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3",
|
||||
"sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008",
|
||||
"sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1",
|
||||
"sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2",
|
||||
"sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635",
|
||||
"sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2",
|
||||
"sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90",
|
||||
"sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee",
|
||||
"sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a",
|
||||
"sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242",
|
||||
"sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12",
|
||||
"sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2",
|
||||
"sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d",
|
||||
"sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be",
|
||||
"sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee",
|
||||
"sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6",
|
||||
"sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529",
|
||||
"sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929",
|
||||
"sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1",
|
||||
"sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6",
|
||||
"sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a",
|
||||
"sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446",
|
||||
"sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9",
|
||||
"sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888",
|
||||
"sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4",
|
||||
"sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33",
|
||||
"sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==41.0.7"
|
||||
"version": "==42.0.2"
|
||||
},
|
||||
"docker": {
|
||||
"hashes": [
|
||||
|
@ -249,33 +258,33 @@
|
|||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12",
|
||||
"sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"
|
||||
"sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981",
|
||||
"sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==1.3.0"
|
||||
"version": "==1.4.0"
|
||||
},
|
||||
"psutil": {
|
||||
"hashes": [
|
||||
"sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340",
|
||||
"sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6",
|
||||
"sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284",
|
||||
"sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c",
|
||||
"sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7",
|
||||
"sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c",
|
||||
"sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e",
|
||||
"sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6",
|
||||
"sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056",
|
||||
"sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9",
|
||||
"sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68",
|
||||
"sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df",
|
||||
"sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e",
|
||||
"sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414",
|
||||
"sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508",
|
||||
"sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"
|
||||
"sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d",
|
||||
"sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73",
|
||||
"sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8",
|
||||
"sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2",
|
||||
"sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e",
|
||||
"sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36",
|
||||
"sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7",
|
||||
"sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c",
|
||||
"sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee",
|
||||
"sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421",
|
||||
"sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf",
|
||||
"sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81",
|
||||
"sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0",
|
||||
"sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631",
|
||||
"sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4",
|
||||
"sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==5.9.7"
|
||||
"version": "==5.9.8"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
|
@ -286,15 +295,15 @@
|
|||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac",
|
||||
"sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"
|
||||
"sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c",
|
||||
"sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==7.4.3"
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==8.0.0"
|
||||
},
|
||||
"pytest-cs": {
|
||||
"git": "https://github.com/crowdsecurity/pytest-cs.git",
|
||||
"ref": "df835beabc539be7f7f627b21caa0d6ad333daae"
|
||||
"ref": "aea7e8549faa32f5e1d1f17755a5db3712396a2a"
|
||||
},
|
||||
"pytest-datadir": {
|
||||
"hashes": [
|
||||
|
@ -322,11 +331,11 @@
|
|||
},
|
||||
"python-dotenv": {
|
||||
"hashes": [
|
||||
"sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba",
|
||||
"sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"
|
||||
"sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca",
|
||||
"sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==1.0.0"
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
|
@ -359,6 +368,7 @@
|
|||
"sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4",
|
||||
"sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba",
|
||||
"sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8",
|
||||
"sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef",
|
||||
"sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5",
|
||||
"sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd",
|
||||
"sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3",
|
||||
|
@ -402,11 +412,11 @@
|
|||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3",
|
||||
"sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"
|
||||
"sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20",
|
||||
"sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.1.0"
|
||||
"version": "==2.2.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
|
@ -476,11 +486,11 @@
|
|||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
"sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27",
|
||||
"sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"
|
||||
"sha256:1050a3ab8473488d7eee163796b02e511d0735cf43a04ba2a8348bd0f2eaf8a5",
|
||||
"sha256:48fbc236fbe0e138b88773fa0437751f14c3645fb483f1d4c5dee58b37e5ce73"
|
||||
],
|
||||
"markers": "python_version >= '3.11'",
|
||||
"version": "==8.18.1"
|
||||
"version": "==8.21.0"
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
|
@ -561,18 +571,18 @@
|
|||
},
|
||||
"traitlets": {
|
||||
"hashes": [
|
||||
"sha256:f14949d23829023013c47df20b4a76ccd1a85effb786dc060f34de7948361b33",
|
||||
"sha256:fcdaa8ac49c04dfa0ed3ee3384ef6dfdb5d6f3741502be247279407679296772"
|
||||
"sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74",
|
||||
"sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==5.14.0"
|
||||
"version": "==5.14.1"
|
||||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02",
|
||||
"sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"
|
||||
"sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859",
|
||||
"sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"
|
||||
],
|
||||
"version": "==0.2.12"
|
||||
"version": "==0.2.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ def test_tls_mutual_split_lapi_agent(crowdsec, flavor, certs_dir):
|
|||
assert "You can successfully interact with Local API (LAPI)" in stdout
|
||||
|
||||
|
||||
def test_tls_client_ou(crowdsec, certs_dir):
|
||||
def test_tls_client_ou(crowdsec, flavor, certs_dir):
|
||||
"""Check behavior of client certificate vs AGENTS_ALLOWED_OU"""
|
||||
|
||||
rand = uuid.uuid1()
|
||||
|
@ -270,8 +270,8 @@ def test_tls_client_ou(crowdsec, certs_dir):
|
|||
certs_dir(lapi_hostname=lapiname, agent_ou='custom-client-ou'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'},
|
||||
}
|
||||
|
||||
cs_lapi = crowdsec(name=lapiname, environment=lapi_env, volumes=volumes)
|
||||
cs_agent = crowdsec(name=agentname, environment=agent_env, volumes=volumes)
|
||||
cs_lapi = crowdsec(flavor=flavor, name=lapiname, environment=lapi_env, volumes=volumes)
|
||||
cs_agent = crowdsec(flavor=flavor, name=agentname, environment=agent_env, volumes=volumes)
|
||||
|
||||
with cs_lapi as lapi:
|
||||
lapi.wait_for_log([
|
||||
|
@ -300,8 +300,8 @@ def test_tls_client_ou(crowdsec, certs_dir):
|
|||
certs_dir(lapi_hostname=lapiname, agent_ou='custom-client-ou'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'},
|
||||
}
|
||||
|
||||
cs_lapi = crowdsec(name=lapiname, environment=lapi_env, volumes=volumes)
|
||||
cs_agent = crowdsec(name=agentname, environment=agent_env, volumes=volumes)
|
||||
cs_lapi = crowdsec(flavor=flavor, name=lapiname, environment=lapi_env, volumes=volumes)
|
||||
cs_agent = crowdsec(flavor=flavor, name=agentname, environment=agent_env, volumes=volumes)
|
||||
|
||||
with cs_lapi as lapi:
|
||||
lapi.wait_for_log([
|
||||
|
|
|
@ -285,7 +285,7 @@ func (c *Config) LoadAPIServer(inCli bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
if c.API.Server.OnlineClient == nil || c.API.Server.OnlineClient.Credentials == nil {
|
||||
if (c.API.Server.OnlineClient == nil || c.API.Server.OnlineClient.Credentials == nil) && !inCli {
|
||||
log.Printf("push and pull to Central API disabled")
|
||||
}
|
||||
|
||||
|
@ -297,7 +297,7 @@ func (c *Config) LoadAPIServer(inCli bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if c.API.Server.CapiWhitelistsPath != "" {
|
||||
if c.API.Server.CapiWhitelistsPath != "" && !inCli {
|
||||
log.Infof("loaded capi whitelist from %s: %d IPs, %d CIDRs", c.API.Server.CapiWhitelistsPath, len(c.API.Server.CapiWhitelists.Ips), len(c.API.Server.CapiWhitelists.Cidrs))
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ type Config struct {
|
|||
Hub *LocalHubCfg `yaml:"-"`
|
||||
}
|
||||
|
||||
func NewConfig(configFile string, disableAgent bool, disableAPI bool, quiet bool) (*Config, string, error) {
|
||||
func NewConfig(configFile string, disableAgent bool, disableAPI bool, inCli bool) (*Config, string, error) {
|
||||
patcher := yamlpatch.NewPatcher(configFile, ".local")
|
||||
patcher.SetQuiet(quiet)
|
||||
patcher.SetQuiet(inCli)
|
||||
fcontent, err := patcher.MergedPatchContent()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
|
|
@ -34,13 +34,18 @@ teardown() {
|
|||
rune -0 jq -r '.msg' <(stderr)
|
||||
assert_output --partial 'already exists: please remove it, use "--force" or specify a different file with "-f"'
|
||||
rune -0 cscli machines add local -a --force
|
||||
assert_output --partial "Machine 'local' successfully added to the local API."
|
||||
assert_stderr --partial "Machine 'local' successfully added to the local API."
|
||||
}
|
||||
|
||||
@test "passwords have a size limit" {
|
||||
rune -1 cscli machines add local --password "$(printf '%73s' '' | tr ' ' x)"
|
||||
assert_stderr --partial "password too long (max 72 characters)"
|
||||
}
|
||||
|
||||
@test "add a new machine and delete it" {
|
||||
rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human
|
||||
assert_output --partial "Machine 'CiTestMachine' successfully added to the local API"
|
||||
assert_output --partial "API credentials written to '/dev/null'"
|
||||
assert_stderr --partial "Machine 'CiTestMachine' successfully added to the local API"
|
||||
assert_stderr --partial "API credentials written to '/dev/null'"
|
||||
|
||||
# we now have two machines
|
||||
rune -0 cscli machines list -o json
|
||||
|
|
Loading…
Add table
Reference in a new issue