diff --git a/Dockerfile b/Dockerfile index fb5e8b196..988634b6b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,9 @@ RUN apk add --no-cache git gcc libc-dev make bash gettext binutils-gold coreutil cscli parsers install crowdsecurity/whitelists && \ go install github.com/mikefarah/yq/v4@v4.31.2 + # In case we need to remove agents here.. + # cscli machines list -o json | yq '.[].machineId' | xargs -r cscli machines delete + FROM alpine:latest as slim RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community tzdata bash && \ diff --git a/Dockerfile.debian b/Dockerfile.debian index 8333d40fe..4629f7fcb 100644 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -23,6 +23,9 @@ RUN apt-get update && \ cscli parsers install crowdsecurity/whitelists && \ go install github.com/mikefarah/yq/v4@v4.31.2 + # In case we need to remove agents here.. + # cscli machines list -o json | yq '.[].machineId' | xargs -r cscli machines delete + FROM debian:bullseye-slim as slim ENV DEBIAN_FRONTEND=noninteractive @@ -50,7 +53,6 @@ COPY --from=build /go/src/crowdsec/docker/config.yaml /staging/etc/crowdsec/conf RUN yq -n '.url="http://0.0.0.0:8080"' | install -m 0600 /dev/stdin /staging/etc/crowdsec/local_api_credentials.yaml && \ yq eval -i ".plugin_config.group = \"nogroup\"" /staging/etc/crowdsec/config.yaml - ENTRYPOINT /bin/bash docker_start.sh FROM slim as plugins diff --git a/docker/docker_start.sh b/docker/docker_start.sh index c89734022..c5aa0fe31 100755 --- a/docker/docker_start.sh +++ b/docker/docker_start.sh @@ -185,13 +185,22 @@ elif [ -n "$USE_WAL" ] && isfalse "$USE_WAL"; then conf_set '.db_config.use_wal = false' fi -# regenerate local agent credentials (even if agent is disabled, cscli needs a -# connection to the API) -cscli machines delete "$CUSTOM_HOSTNAME" 2>/dev/null || true +lapi_credentials_path=$(conf_get '.api.client.credentials_path') + + if isfalse "$DISABLE_LOCAL_API"; then - if isfalse "$USE_TLS" || [ "$CLIENT_CERT_FILE" = "" ]; then - echo "Regenerate local agent credentials" - cscli machines add "$CUSTOM_HOSTNAME" --auto + # generate local agent credentials (even if agent is disabled, cscli needs a + # connection to the API) + if ( isfalse "$USE_TLS" || [ "$CLIENT_CERT_FILE" = "" ] ); then + if yq -e '.login==strenv(CUSTOM_HOSTNAME)' "$lapi_credentials_path" && ( cscli machines list -o json | yq -e 'any_c(.machineId==strenv(CUSTOM_HOSTNAME))' >/dev/null ); then + echo "Local agent already registered" + else + echo "Generate local agent credentials" + # if the db is persistent but the credentials are not, we need to + # delete the old machine to generate new credentials + cscli machines delete "$CUSTOM_HOSTNAME" >/dev/null 2>&1 || true + cscli machines add "$CUSTOM_HOSTNAME" --auto + fi fi echo "Check if lapi needs to register an additional agent" @@ -205,8 +214,6 @@ fi # ---------------- -lapi_credentials_path=$(conf_get '.api.client.credentials_path') - conf_set_if "$LOCAL_API_URL" '.url = strenv(LOCAL_API_URL)' "$lapi_credentials_path" if istrue "$DISABLE_LOCAL_API"; then diff --git a/docker/test/tests/test_agent.py b/docker/test/tests/test_agent.py new file mode 100644 index 000000000..e1ede3f89 --- /dev/null +++ b/docker/test/tests/test_agent.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +from http import HTTPStatus + +import pytest + +pytestmark = pytest.mark.docker + + +def test_no_agent(crowdsec, flavor): + """Test DISABLE_AGENT=true""" + env = { + 'DISABLE_AGENT': 'true', + } + with crowdsec(flavor=flavor, environment=env) as cs: + cs.wait_for_log("*CrowdSec Local API listening on 0.0.0.0:8080*") + cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK) + res = cs.cont.exec_run('cscli lapi status') + assert res.exit_code == 0 + stdout = res.output.decode() + assert "You can successfully interact with Local API (LAPI)" in stdout + + +def test_machine_register(crowdsec, flavor, tmp_path_factory): + """A local agent is always registered for use by cscli""" + + data_dir = tmp_path_factory.mktemp('data') + + env = { + 'DISABLE_AGENT': 'true', + } + + volumes = { + data_dir: {'bind': '/var/lib/crowdsec/data', 'mode': 'rw'}, + } + + with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs: + cs.wait_for_log([ + "*Generate local agent credentials*", + "*CrowdSec Local API listening on 0.0.0.0:8080*", + ]) + cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK) + res = cs.cont.exec_run('cscli lapi status') + assert res.exit_code == 0 + stdout = res.output.decode() + assert "You can successfully interact with Local API (LAPI)" in stdout + + # The local agent is not registered, because we didn't persist local_api_credentials.yaml + + with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs: + cs.wait_for_log([ + "*Generate local agent credentials*", + "*CrowdSec Local API listening on 0.0.0.0:8080*", + ]) + cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK) + res = cs.cont.exec_run('cscli lapi status') + assert res.exit_code == 0 + stdout = res.output.decode() + assert "You can successfully interact with Local API (LAPI)" in stdout + + config_dir = tmp_path_factory.mktemp('config') + + volumes[config_dir] = {'bind': '/etc/crowdsec', 'mode': 'rw'} + + with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs: + cs.wait_for_log([ + "*Generate local agent credentials*", + "*CrowdSec Local API listening on 0.0.0.0:8080*", + ]) + cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK) + res = cs.cont.exec_run('cscli lapi status') + assert res.exit_code == 0 + stdout = res.output.decode() + assert "You can successfully interact with Local API (LAPI)" in stdout + + # The local agent is now already registered + + with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs: + cs.wait_for_log([ + "*Local agent already registered*", + "*CrowdSec Local API listening on 0.0.0.0:8080*", + ]) + cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK) + res = cs.cont.exec_run('cscli lapi status') + assert res.exit_code == 0 + stdout = res.output.decode() + assert "You can successfully interact with Local API (LAPI)" in stdout diff --git a/docker/test/tests/test_noagent.py b/docker/test/tests/test_noagent.py deleted file mode 100644 index cbf2d0432..000000000 --- a/docker/test/tests/test_noagent.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -from http import HTTPStatus - -import pytest - -pytestmark = pytest.mark.docker - - -def test_no_agent(crowdsec, flavor): - """Test DISABLE_AGENT=true""" - env = { - 'DISABLE_AGENT': 'true', - } - with crowdsec(flavor=flavor, environment=env) as cs: - cs.wait_for_log("*CrowdSec Local API listening on 0.0.0.0:8080*") - cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK) - res = cs.cont.exec_run('cscli lapi status') - assert res.exit_code == 0 - stdout = res.output.decode() - assert "You can successfully interact with Local API (LAPI)" in stdout