diff --git a/README.md b/README.md index b1034a2..f3f0936 100644 --- a/README.md +++ b/README.md @@ -72,22 +72,20 @@ Display the configuration By default whitelist is enabled to never ban IP on the local network, for test purpose you could disable it - cscli parsers remove crowdsecurity/whitelists + runagent -m crowdsec1 cscli parsers remove crowdsecurity/whitelists systemctl restart crowdsec1 -## List Banned IP in nftables sets +### cscli -Banned IP are contained inside nft sets that you can list by the command line below +Crowdsec come with a cli tool, available within the application environment. Get a shell with: -- ipv4 -`nft list set ip crowdsec crowdsec-blacklists` -- ipv6 -`nft list set ip6 crowdsec6 crowdsec6-blacklists` + runagent -m crowdsec1 bash -l -### cscli +Then run the tool as -crowdsec come with a cli, do `cscli --help`, if you want to know on a specific command `cscli --help` + cscli --help +- help on a specific command: `cscli --help` - get a glance : `cscli metrics` - see the state of installed bouncers : `cscli bouncers list` - see the active decisions(ban): `cscli decisions list` @@ -111,12 +109,12 @@ crowdsec come with a cli, do `cscli --help`, if you want to know on a specific c You can see the metrics of crowdsec at https://app.crowdsec.net/, for this purpose you need to create a login for a single user or an organization in the website, then in the top right menu click in `enroll an instance` and retrieve the keys, then enroll your container and restart it. - cscli console enroll + runagent -m crowdsec1 cscli console enroll systemctl restart crowdsec1 you can force the enrollment with another key - cscli console enroll --overwrite + runagent -m crowdsec1 cscli console enroll --overwrite systemctl restart crowdsec1 Once done you need to accept inside the website the `Instance enroll request` @@ -127,29 +125,31 @@ To uninstall the instance: remove-module --no-preserve crowdsec1 -## Uninstall the crowdsec binary bouncer +## Uninstall the old crowdsec binary bouncer Previous to the version 1.0.6 the bouncer was installed on the host following a repository method, after this version the bouncer is shipped in a full container. With the upgrade the service `crowdsec-firewall-bouncer` has been stopped but not removed from the host. For a full cleaning you can - remove firewalld permanent sets: - `firewall-cmd --permanent --delete-ipset=crowdsec-blacklists` - `firewall-cmd --permanent --delete-ipset=crowdsec6-blacklists` + + firewall-cmd --permanent --delete-ipset=crowdsec-blacklists + firewall-cmd --permanent --delete-ipset=crowdsec6-blacklists - remove the bouncer on rocky linux - `dnf remove -y crowdsec-firewall-bouncer-iptables` - `rm /etc/yum.repos.d/crowdsec_crowdsec.repo` + + dnf remove -y crowdsec-firewall-bouncer-iptables + rm -rvf /etc/yum.repos.d/crowdsec_crowdsec.repo /etc/crowdsec /usr/local/sbin/cscli - remove the bouncer on debian - `apt-get -y remove crowdsec-firewall-bouncer-iptables` - `rm /etc/apt/sources.list.d/crowdsec_crowdsec.list` + + apt-get -y remove crowdsec-firewall-bouncer-iptables + rm -rvf /etc/apt/sources.list.d/crowdsec_crowdsec.list /etc/crowdsec /usr/local/sbin/cscli ## Testing Test the module using the `test-module.sh` script: - ./test-module.sh ghcr.io/nethserver/crowdsec:latest The tests are made using [Robot Framework](https://robotframework.org/) @@ -161,4 +161,4 @@ Translated with [Weblate](https://hosted.weblate.org/projects/ns8/). To setup the translation process: - add [GitHub Weblate app](https://docs.weblate.org/en/latest/admin/continuous.html#github-setup) to your repository -- add your repository to [hosted.weblate.org]((https://hosted.weblate.org) or ask a NethServer developer to add it to ns8 Weblate project +- add your repository to [hosted.weblate.org](https://hosted.weblate.org) or ask a NethServer developer to add it to ns8 Weblate project diff --git a/imageroot/actions/create-module/02create-first-configuration b/imageroot/actions/create-module/02create-first-configuration deleted file mode 100755 index 3f5faa1..0000000 --- a/imageroot/actions/create-module/02create-first-configuration +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2022 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# - -set -e - -# build folder to store configuration -mkdir -vp crowdsec_config -# first build of the configuration -exec expand-configuration diff --git a/imageroot/actions/create-module/10initialize b/imageroot/actions/create-module/10initialize index ca2f015..ace926b 100755 --- a/imageroot/actions/create-module/10initialize +++ b/imageroot/actions/create-module/10initialize @@ -8,49 +8,28 @@ set -e exec 1>&2 # Send any output to stderr, to not alter the action response protocol -install -m 644 "${AGENT_INSTALL_DIR}/crowdsec.service" "/etc/systemd/system/${MODULE_ID}.service" +# Run with TEST_MODE=true to generate the initial configuration +mkdir -vp crowdsec_config +podman run -i --rm --replace --name "${MODULE_ID}-init" \ + --log-driver=none \ + --privileged \ + --network=host \ + --env=TEST_MODE=true \ + --volume ./crowdsec_config:/etc/crowdsec:Z \ + --volume "${MODULE_ID}-data":/var/lib/crowdsec/data:Z \ + --volume "${CROWDSEC_JOURNAL}":/run/log/journal \ + ${CROWDSEC_IMAGE} + +install-systemd-units -# create the service to update the crowdsec hub for collections -tmpfile=$(mktemp) -trap "rm -f \${tmpfile}" EXIT -cat <${tmpfile} -[Unit] -Description=Update the crowdsec HUB -Requisite=${MODULE_ID}.service - -[Service] -Type=oneshot -ExecStart=/usr/bin/podman exec -ti ${MODULE_ID} cscli hub update -ExecStart=/usr/bin/podman exec -ti ${MODULE_ID} cscli hub upgrade -SyslogIdentifier=%N -EOF -install -v -m 644 "${tmpfile}" /etc/systemd/system/${MODULE_ID}-upgrade-hub.service - -# create the timer to update the crowdsec hub for collections -tmpfile=$(mktemp) -trap "rm -f \${tmpfile}" EXIT -cat <${tmpfile} -[Unit] -Description=Timer of crowdsec hub update - -[Timer] -OnActiveSec=15 minutes -OnUnitInactiveSec=15 days - -[Install] -WantedBy=timers.target -EOF -install -v -m 644 "${tmpfile}" /etc/systemd/system/${MODULE_ID}-upgrade-hub.timer - -systemctl daemon-reload systemctl enable --now "${MODULE_ID}.service" systemctl enable --now "${MODULE_ID}-upgrade-hub.timer" # Install default collections -podman exec -ti ${MODULE_ID} cscli hub update -podman exec -ti ${MODULE_ID} cscli hub upgrade +podman exec -i ${MODULE_ID} cscli hub update +podman exec -i ${MODULE_ID} cscli hub upgrade -podman exec -ti ${MODULE_ID} cscli collections install \ +podman exec -i ${MODULE_ID} cscli collections install \ crowdsecurity/apache2 \ crowdsecurity/base-http-scenarios \ crowdsecurity/dovecot \ @@ -70,4 +49,4 @@ podman exec -ti ${MODULE_ID} cscli collections install \ crowdsecurity/whitelist-good-actors # we need it if we want to ban with IP from country -podman exec -ti ${MODULE_ID} cscli parsers install crowdsecurity/geoip-enrich +podman exec -i ${MODULE_ID} cscli parsers install crowdsecurity/geoip-enrich diff --git a/imageroot/actions/create-module/35register-local-bouncer b/imageroot/actions/create-module/35register-local-bouncer index 79e6522..53da010 100755 --- a/imageroot/actions/create-module/35register-local-bouncer +++ b/imageroot/actions/create-module/35register-local-bouncer @@ -10,4 +10,4 @@ set -e echo "Register the localhost bouncer" secret=$(cat secrets/bouncer_keys_firewall.secret) -podman exec -ti ${MODULE_ID} cscli bouncers add localhost -k "$secret" +podman exec -i ${MODULE_ID} cscli bouncers add localhost -k "$secret" diff --git a/imageroot/actions/create-module/40crowdsec-configuration b/imageroot/actions/create-module/40crowdsec-configuration deleted file mode 100755 index c2b4486..0000000 --- a/imageroot/actions/create-module/40crowdsec-configuration +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 - -# -# Copyright (C) 2022 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# - -import os -import json -import agent -import agent.tasks - -from jinja2 import Environment, FileSystemLoader, select_autoescape - -files =["crowdsec_config/config.yaml.local"] -for f in files: - try: - os.remove(f) - except FileNotFoundError: - pass - -jenv = Environment( - loader=FileSystemLoader(os.getenv("AGENT_INSTALL_DIR")+"/templates"), - autoescape=select_autoescape(), - keep_trailing_newline=True, -) -# placeholder for later -properties = {} - -template = jenv.get_template('config.yaml.local') -output = template.render(properties) -with open("crowdsec_config/config.yaml.local","w") as f: - f.write(output) diff --git a/imageroot/actions/create-module/50start-bouncer b/imageroot/actions/create-module/50start-bouncer index 7f881bd..a1649ff 100755 --- a/imageroot/actions/create-module/50start-bouncer +++ b/imageroot/actions/create-module/50start-bouncer @@ -8,13 +8,6 @@ set -e exec 1>&2 # Send any output to stderr, to not alter the action response protocol -tmpfile=$(mktemp) -trap "rm -f \${tmpfile}" EXIT -envsubst >${tmpfile} <"${AGENT_INSTALL_DIR}/crowdsec-firewall-bouncer.service" -install -m 644 "${tmpfile}" "/etc/systemd/system/${MODULE_ID}-firewall-bouncer.service" -# reload and start service -systemctl daemon-reload - # API server could be slow to start: # ignore bouncer connect error if it fails to start on first run systemctl enable --now ${MODULE_ID}-firewall-bouncer.service diff --git a/imageroot/actions/create-module/70crowdsec-wrapper b/imageroot/actions/create-module/70crowdsec-wrapper deleted file mode 100755 index d8467c0..0000000 --- a/imageroot/actions/create-module/70crowdsec-wrapper +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2022 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# -exec 1>&2 # never generate action output. Write to stderr instead. -set -e # stop at the first error - -# Create a wrapper to use the container : cscli metrics - -tmpfile=$(mktemp) -trap "rm -f \${tmpfile}" EXIT -cat <${tmpfile} -#!/bin/bash - -podman exec -ti ${MODULE_ID} cscli "\${@}" -EOF -install -v -m 0755 "${tmpfile}" /usr/local/sbin/cscli diff --git a/imageroot/actions/destroy-module/75remove-crowdsec-wrapper b/imageroot/actions/destroy-module/75remove-crowdsec-wrapper deleted file mode 100755 index 1c21fc5..0000000 --- a/imageroot/actions/destroy-module/75remove-crowdsec-wrapper +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2022 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# - -exec 1>&2 # Send any output to stderr, to not alter the action response protocol -set -e - -# remove the wrapper to use the container -rm -vf /usr/local/sbin/cscli -# remove residual config -rm -rvf /etc/crowdsec diff --git a/imageroot/bin/cscli b/imageroot/bin/cscli new file mode 100755 index 0000000..55e29ef --- /dev/null +++ b/imageroot/bin/cscli @@ -0,0 +1,12 @@ +#!/bin/bash + +# +# Copyright (C) 2022 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +if [[ -t 1 ]]; then + with_tty=1 +fi + +exec podman exec -${with_tty:+t}i "${MODULE_ID}" cscli "${@}" diff --git a/imageroot/bin/expand-configuration b/imageroot/bin/expand-configuration index 598cf84..a3dbc5a 100755 --- a/imageroot/bin/expand-configuration +++ b/imageroot/bin/expand-configuration @@ -35,10 +35,8 @@ template = jenv.get_template('acquis.yaml') with open("crowdsec_config/acquis.yaml","w") as f: f.write(template.render()) -# The first start crowdsec expects other configuration files -# if these files are not present then the service fails to start -# we start the first time with the default configuration -if os.path.isfile("crowdsec_config/config.yaml.local"): +# expand config.yaml.local +if True: files =["crowdsec_config/config.yaml.local"] for f in files: try: @@ -58,8 +56,9 @@ if os.path.isfile("crowdsec_config/config.yaml.local"): output = template.render(properties) with open("crowdsec_config/config.yaml.local","w") as f: f.write(output) + ## template of local_api_credentials.yaml.local -if os.path.isfile("crowdsec_config/config.yaml.local"): +if True: files =["crowdsec_config/local_api_credentials.yaml.local"] for f in files: try: diff --git a/imageroot/bin/install-systemd-units b/imageroot/bin/install-systemd-units new file mode 100755 index 0000000..feacaf6 --- /dev/null +++ b/imageroot/bin/install-systemd-units @@ -0,0 +1,21 @@ +#!/bin/bash + +# +# Copyright (C) 2022 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +# install the main controller service +install -m 644 "${AGENT_INSTALL_DIR}/crowdsec.service" "/etc/systemd/system/${MODULE_ID}.service" + +# install the firewall bouncer service +envsubst <"${AGENT_INSTALL_DIR}/crowdsec-firewall-bouncer.service" >"/etc/systemd/system/${MODULE_ID}-firewall-bouncer.service" + +# create the service to update the crowdsec hub for collections +envsubst <"${AGENT_INSTALL_DIR}/crowdsec-upgrade-hub.service" >"/etc/systemd/system/${MODULE_ID}-upgrade-hub.service" + +# create the timer to update the crowdsec hub for collections +envsubst <"${AGENT_INSTALL_DIR}/crowdsec-upgrade-hub.timer" >"/etc/systemd/system/${MODULE_ID}-upgrade-hub.timer" + +# reload and start service +systemctl daemon-reload \ No newline at end of file diff --git a/imageroot/crowdsec-firewall-bouncer.service b/imageroot/crowdsec-firewall-bouncer.service index 549481c..a97099a 100644 --- a/imageroot/crowdsec-firewall-bouncer.service +++ b/imageroot/crowdsec-firewall-bouncer.service @@ -23,8 +23,10 @@ LimitNOFILE=65536 TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/%N.pid %t/%N.cid ExecStartPre=runagent -m ${MODULE_ID} expand-bouncer-configuration +SuccessExitStatus=1 ExecStart=/usr/bin/podman run \ --detach \ + --init \ --conmon-pidfile %t/%N.pid \ --cidfile %t/%N.cid \ --cgroups=no-conmon \ @@ -36,8 +38,6 @@ ExecStart=/usr/bin/podman run \ ${CROWDSEC_FIREWALL_BOUNCER_IMAGE} ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%N.cid -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%N.cid -ExecReload=runagent -m ${MODULE_ID} expand-bouncer-configuration -ExecReload=/usr/bin/podman kill -s HUP %N PIDFile=%t/%N.pid Type=forking diff --git a/imageroot/crowdsec-upgrade-hub.service b/imageroot/crowdsec-upgrade-hub.service new file mode 100644 index 0000000..7510711 --- /dev/null +++ b/imageroot/crowdsec-upgrade-hub.service @@ -0,0 +1,9 @@ +[Unit] +Description=Update the crowdsec HUB +Requisite=${MODULE_ID}.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/podman exec -i ${MODULE_ID} cscli hub update +ExecStart=/usr/bin/podman exec -i ${MODULE_ID} cscli hub upgrade +SyslogIdentifier=%N diff --git a/imageroot/crowdsec-upgrade-hub.timer b/imageroot/crowdsec-upgrade-hub.timer new file mode 100644 index 0000000..0b3ab5f --- /dev/null +++ b/imageroot/crowdsec-upgrade-hub.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Timer of crowdsec hub update + +[Timer] +OnActiveSec=15 minutes +OnUnitInactiveSec=15 days + +[Install] +WantedBy=timers.target diff --git a/imageroot/crowdsec.service b/imageroot/crowdsec.service index 8131639..d788f6b 100644 --- a/imageroot/crowdsec.service +++ b/imageroot/crowdsec.service @@ -24,8 +24,10 @@ ExecStartPre=/bin/mkdir -vp crowdsec_config/postoverflows/s01-whitelist ExecStartPre=/bin/mkdir -vp crowdsec_config/parsers/s02-enrich ExecStartPre=/usr/local/bin/runagent -m %N expand-configuration ExecStartPre=/usr/local/bin/runagent -m %N expand-smarthost +SuccessExitStatus=143 ExecStart=/usr/bin/podman run \ --detach \ + --init \ --privileged \ --conmon-pidfile %t/%N.pid \ --cidfile %t/%N.cid \ diff --git a/imageroot/events/module-added/00event_validation b/imageroot/events/module-added/00event_validation deleted file mode 100755 index 1678432..0000000 --- a/imageroot/events/module-added/00event_validation +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 - -# -# Copyright (C) 2022 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# - -import json -import sys -import agent -import os - -event = json.load(sys.stdin) - -if str(event['node']) != os.environ['NODE_ID']: - print(agent.SD_DEBUG + "Event ignored: source is another node") - sys.exit(2) diff --git a/imageroot/events/module-added/10handler b/imageroot/events/module-added/10handler deleted file mode 100755 index 0b663f7..0000000 --- a/imageroot/events/module-added/10handler +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2022 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# - -set -e - -exec 1>&2 # Send any output to stderr, to not alter the action response protocol - -# when crowdsec restart we look after modules in redis to filter with journald (SYSLOG_IDENTIFIER) -systemctl restart ${MODULE_ID}.service diff --git a/imageroot/events/module-removed/00event_validation b/imageroot/events/module-removed/00event_validation deleted file mode 120000 index 3983cd9..0000000 --- a/imageroot/events/module-removed/00event_validation +++ /dev/null @@ -1 +0,0 @@ -../module-added/00event_validation \ No newline at end of file diff --git a/imageroot/events/module-removed/10handler b/imageroot/events/module-removed/10handler deleted file mode 100755 index 0b663f7..0000000 --- a/imageroot/events/module-removed/10handler +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2022 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# - -set -e - -exec 1>&2 # Send any output to stderr, to not alter the action response protocol - -# when crowdsec restart we look after modules in redis to filter with journald (SYSLOG_IDENTIFIER) -systemctl restart ${MODULE_ID}.service diff --git a/imageroot/templates/crowdsec-firewall-bouncer.yaml.local b/imageroot/templates/crowdsec-firewall-bouncer.yaml.local index 4a120b4..74b0012 100644 --- a/imageroot/templates/crowdsec-firewall-bouncer.yaml.local +++ b/imageroot/templates/crowdsec-firewall-bouncer.yaml.local @@ -1,7 +1,6 @@ mode: nftables pid_dir: /var/run/ update_frequency: 10s -daemonize: true log_mode: stdout log_dir: /var/log/ log_level: info diff --git a/imageroot/update-module.d/10install_systemd_units b/imageroot/update-module.d/10install_systemd_units new file mode 100755 index 0000000..e1e1a01 --- /dev/null +++ b/imageroot/update-module.d/10install_systemd_units @@ -0,0 +1,8 @@ +#!/bin/bash + +# +# Copyright (C) 2022 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +install-systemd-units diff --git a/imageroot/update-module.d/10update-bouncer-to-container b/imageroot/update-module.d/10update-bouncer-to-container deleted file mode 100755 index 4be1bbf..0000000 --- a/imageroot/update-module.d/10update-bouncer-to-container +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2024 Nethesis S.r.l. -# SPDX-License-Identifier: GPL-3.0-or-later -# -# Stop the bouncer installed on the host and start the bouncer container -# Needed to upgrade from crowdsec:1.0.6 - -if systemctl is-active -q crowdsec-firewall-bouncer.service; then - # stop the bouncer - echo "Stop the crowdsec bouncer and disable it, removal of firewalld rules and bouncer binary can be done manually" - systemctl disable --now crowdsec-firewall-bouncer.service - # start the bouncer - echo "Enable and start the systemd service of the crowdsec bouncer container" - ../actions/create-module/50start-bouncer -fi diff --git a/imageroot/update-module.d/15fix_6900 b/imageroot/update-module.d/15fix_6900 new file mode 100755 index 0000000..42762b4 --- /dev/null +++ b/imageroot/update-module.d/15fix_6900 @@ -0,0 +1,15 @@ +#!/bin/bash + +# +# Copyright (C) 2024 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Stop the bouncer installed on the host and start the bouncer container +# Needed to upgrade from crowdsec:1.0.6 + +if systemctl is-active -q crowdsec-firewall-bouncer.service || systemctl is-enabled crowdsec-firewall-bouncer.service &>/dev/null ; then + echo "Uninstall the old crowdsec binary bouncer" + echo "Check the app README for manual clean up instructions" + systemctl disable --now crowdsec-firewall-bouncer.service + systemctl enable --now "${MODULE_ID}-firewall-bouncer.service" +fi diff --git a/imageroot/update-module.d/20restart b/imageroot/update-module.d/20restart index cf1fe1a..58b2568 100755 --- a/imageroot/update-module.d/20restart +++ b/imageroot/update-module.d/20restart @@ -7,7 +7,5 @@ set -e -exec 1>&2 # Send any output to stderr, to not alter the action response protocol - # we need to start again to expand configuration -systemctl restart "${MODULE_ID}.service" +systemctl try-restart "${MODULE_ID}.service" "${MODULE_ID}-firewall-bouncer.service" diff --git a/ui/public/i18n/en/translation.json b/ui/public/i18n/en/translation.json index 35fd22f..ed27f98 100644 --- a/ui/public/i18n/en/translation.json +++ b/ui/public/i18n/en/translation.json @@ -46,7 +46,7 @@ "helo_host": "Helo_host", "helo_host_must_be_relevant_for_smtp": "This might be needed to properly receive email notifications. If your antispam system adds score due to default 'localhost' Helo name, you can set a specific Helo FQDN here", "helo_host_placeholder": "sub.domain.com", - "enable_smarthosts_for_notifications": "Smarthost settings", + "enable_smarthosts_for_notifications": "Go to cluster Settings", "bad_email_address": "Bad email address", "bad_IP_or_hostname": "Bad CIDR, IP or FQDN", "processing": "Processing...", @@ -54,7 +54,7 @@ "token_not_valid":"The token provided is not valid", "whitelists_tips": "No ban will occur for members of this list", "smarthost_is_disabled": "Email notifications are disabled", - "smarthosts_is_needed_to_send_notifications": "Enable Smarthost to receive CrowdSec email notifications" + "smarthosts_is_needed_to_send_notifications": "To receive mail notifications from Crowdsec, change the cluster's email settings" }, "unban":{ "title": "Banned IP", diff --git a/ui/public/metadata.json b/ui/public/metadata.json index e300ae9..472d4cf 100644 --- a/ui/public/metadata.json +++ b/ui/public/metadata.json @@ -12,7 +12,7 @@ } ], "docs": { - "documentation_url": "https://doc.crowdsec.net/docs/intro/", + "documentation_url": "https://docs.nethserver.org/projects/ns8/en/latest/crowdsec.html", "bug_url": "https://github.com/NethServer/dev", "code_url": "https://github.com/NethServer/ns8-crowdsec" },