feat(crowdsec): migrate host-install to a dedicated libreportal-crowdsec helper
CrowdSec's host-side install (the agent + nftables bouncer the LibrePortal
Traefik plugin talks to) had stayed on blanket sudo throughout the rootless +
de-sudo hardening: `sudo apt-get install crowdsec`, `curl | sudo bash`,
`sudo sed -i /etc/crowdsec/config.yaml`, `sudo touch + sudo chmod /var/log/
crowdsec*.log`, `echo $key | sudo tee /etc/crowdsec/traefik_bouncer.key`,
plus `sudo cscli capi register / console enroll / bouncers add`. None of
those are in the scoped LP_HELPERS / LP_SYSTEM sudoers grant the manager
now holds, so any user who enabled crowdsec would have hit hard sudo
failures on every privileged step.
Follow the libreportal-appcfg / libreportal-bininstall pattern: one new
root-owned helper at /usr/local/lib/libreportal/libreportal-crowdsec
that does every privileged op behind a fixed action vocabulary with strict
argument validation. The manager calls in via runCrowdsec — the scoped
sudoers grants exactly one binary, the same trust boundary the other
helpers rely on.
Actions:
install apt repo + agent + firewall-bouncer + enable +
crowdsecurity/{linux,sshd} collections + reload
(idempotent — skips parts already in place)
services <verb> enable | disable | restart
capi <verb> register | unregister | status
console <verb> enroll <token> | disenroll | status
token format strictly validated
bouncer-traefik-init cscli register + write the manager-owned key file
atomically (returns EXISTS or GENERATED:<key>)
bouncer-priority bouncer yaml nftables priority → -100
(moved from libreportal-appcfg; one helper for
every crowdsec root op)
bind-lapi flip listen_uri to 0.0.0.0:8080 in config.yaml
prometheus <on…|off> flip the prometheus block (validated addr/port)
touch-host-logs create + chmod 0644 /var/log/crowdsec*.log so the
libreportal container can tail them
Wired in via:
- new sudoers Cmnd_Alias entry for the helper in LP_HELPERS
- new helper baked alongside the others by initRootHelpers
(replaces __SYSTEM_DIR__ / __CONTAINERS_DIR__ / __MANAGER__ at
install, with safe runtime fallbacks if unbaked)
- new runCrowdsec dispatch in scripts/docker/command/run_privileged.sh
containers/crowdsec/scripts/crowdsec_install_host.sh now drives the whole
flow through runCrowdsec — every `sudo …` is gone, the compose-toggle sed
uses runFileOp, and the security_crowdsec CFG mirror uses runInstallOp
(configs/ is manager-owned). Net: install script shrinks ~80 lines while
gaining a single auditable trust boundary. crowdsec_fix_priority.sh swung
over to runCrowdsec bouncer-priority too — the appcfg crowdsec_priority
action drops out cleanly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
10dc7d0bc0
commit
7513a62fde
@ -8,8 +8,8 @@ appCrowdSecFixPriority() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# The bouncer yaml is root-owned under /etc/crowdsec; the backup + nftables
|
# The bouncer yaml is root-owned under /etc/crowdsec; the backup + nftables
|
||||||
# ipv4/ipv6 priority rewrite (to -100) runs in the root-owned appcfg helper.
|
# ipv4/ipv6 priority rewrite (to -100) runs in the root-owned crowdsec helper.
|
||||||
runAppCfg crowdsec-priority
|
runCrowdsec bouncer-priority
|
||||||
checkSuccess "Patched nftables priority to -100 in $cfg"
|
checkSuccess "Patched nftables priority to -100 in $cfg"
|
||||||
|
|
||||||
runSystem systemctl restart crowdsec-firewall-bouncer
|
runSystem systemctl restart crowdsec-firewall-bouncer
|
||||||
|
|||||||
@ -10,12 +10,14 @@ crowdsecToggleLibrePortalLogMounts() {
|
|||||||
|
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
on)
|
on)
|
||||||
sudo sed -i '/# >>> crowdsec-host-logs >>>/,/# <<< crowdsec-host-logs <<</ {
|
# Lines inside the marker block that look like `#-/var/log/crowdsec...:`
|
||||||
|
# become `-/var/log/...:` so docker compose picks up the bind-mounts.
|
||||||
|
runFileOp sed -i '/# >>> crowdsec-host-logs >>>/,/# <<< crowdsec-host-logs <<</ {
|
||||||
/crowdsec.*\.log:/ s/^\([[:space:]]*\)#-/\1-/
|
/crowdsec.*\.log:/ s/^\([[:space:]]*\)#-/\1-/
|
||||||
}' "$compose"
|
}' "$compose"
|
||||||
;;
|
;;
|
||||||
off)
|
off)
|
||||||
sudo sed -i '/# >>> crowdsec-host-logs >>>/,/# <<< crowdsec-host-logs <<</ {
|
runFileOp sed -i '/# >>> crowdsec-host-logs >>>/,/# <<< crowdsec-host-logs <<</ {
|
||||||
/crowdsec.*\.log:/ s/^\([[:space:]]*\)-/\1#-/
|
/crowdsec.*\.log:/ s/^\([[:space:]]*\)-/\1#-/
|
||||||
}' "$compose"
|
}' "$compose"
|
||||||
;;
|
;;
|
||||||
@ -30,12 +32,9 @@ crowdsecToggleLibrePortalLogMounts() {
|
|||||||
|
|
||||||
installCrowdsecHost()
|
installCrowdsecHost()
|
||||||
{
|
{
|
||||||
# Touch + chmod 0644 the host log files so the libreportal container
|
# Make /var/log/crowdsec*.log world-readable so the libreportal container
|
||||||
# (UID 1001) can read them. Then enable the bind-mounts.
|
# (UID 1001) can tail them via the bind-mount we're about to enable.
|
||||||
for _l in /var/log/crowdsec.log /var/log/crowdsec-firewall-bouncer.log; do
|
runCrowdsec touch-host-logs
|
||||||
sudo touch "$_l" 2>/dev/null || true
|
|
||||||
sudo chmod 0644 "$_l" 2>/dev/null || true
|
|
||||||
done
|
|
||||||
crowdsecToggleLibrePortalLogMounts on
|
crowdsecToggleLibrePortalLogMounts on
|
||||||
|
|
||||||
local desired_state="${CFG_CROWDSEC_ENABLED:-true}"
|
local desired_state="${CFG_CROWDSEC_ENABLED:-true}"
|
||||||
@ -47,53 +46,16 @@ installCrowdsecHost()
|
|||||||
|
|
||||||
((menu_number++))
|
((menu_number++))
|
||||||
echo ""
|
echo ""
|
||||||
echo "---- $menu_number. Adding the CrowdSec apt repository."
|
echo "---- $menu_number. Installing the CrowdSec agent + firewall bouncer."
|
||||||
echo ""
|
|
||||||
|
|
||||||
local result=$(curl -fsSL https://install.crowdsec.net | sudo bash)
|
|
||||||
checkSuccess "Adding CrowdSec repository"
|
|
||||||
|
|
||||||
((menu_number++))
|
|
||||||
echo ""
|
|
||||||
echo "---- $menu_number. Installing the CrowdSec agent."
|
|
||||||
echo ""
|
echo ""
|
||||||
isNotice "First-time install ~30-70 MB GeoLite2 DB + parser hub, 1-3 mins."
|
isNotice "First-time install ~30-70 MB GeoLite2 DB + parser hub, 1-3 mins."
|
||||||
|
|
||||||
local result=$(sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q crowdsec </dev/null 2>&1)
|
# One-shot: adds the apt repo, installs both packages, enables both
|
||||||
checkSuccess "Installing CrowdSec package"
|
# services, installs the crowdsecurity/linux + /sshd collections, then
|
||||||
|
# reloads the agent. All of it lives in libreportal-crowdsec so the
|
||||||
((menu_number++))
|
# manager never needs `sudo apt-get` / `sudo bash`.
|
||||||
echo ""
|
local result=$(runCrowdsec install)
|
||||||
echo "---- $menu_number. Installing the CrowdSec firewall bouncer."
|
checkSuccess "Installing CrowdSec agent + firewall bouncer + baseline collections"
|
||||||
echo ""
|
|
||||||
|
|
||||||
local result=$(sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q crowdsec-firewall-bouncer-nftables </dev/null 2>&1)
|
|
||||||
checkSuccess "Installing CrowdSec firewall bouncer (nftables)"
|
|
||||||
|
|
||||||
((menu_number++))
|
|
||||||
echo ""
|
|
||||||
echo "---- $menu_number. Enabling CrowdSec services."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
local result=$(runSystem systemctl enable --now crowdsec)
|
|
||||||
checkSuccess "Enabling CrowdSec agent"
|
|
||||||
|
|
||||||
local result=$(runSystem systemctl enable --now crowdsec-firewall-bouncer)
|
|
||||||
checkSuccess "Enabling CrowdSec firewall bouncer"
|
|
||||||
|
|
||||||
((menu_number++))
|
|
||||||
echo ""
|
|
||||||
echo "---- $menu_number. Installing baseline collections."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
local result=$(runSystem cscli collections install crowdsecurity/linux)
|
|
||||||
checkSuccess "Installing crowdsecurity/linux collection"
|
|
||||||
|
|
||||||
local result=$(runSystem cscli collections install crowdsecurity/sshd)
|
|
||||||
checkSuccess "Installing crowdsecurity/sshd collection"
|
|
||||||
|
|
||||||
local result=$(runSystem systemctl reload crowdsec)
|
|
||||||
checkSuccess "Reloading CrowdSec to pick up collections"
|
|
||||||
|
|
||||||
((menu_number++))
|
((menu_number++))
|
||||||
echo ""
|
echo ""
|
||||||
@ -106,14 +68,14 @@ installCrowdsecHost()
|
|||||||
# unregistering. Idempotent on either branch.
|
# unregistering. Idempotent on either branch.
|
||||||
local community_blocklist="${CFG_CROWDSEC_COMMUNITY_BLOCKLIST:-true}"
|
local community_blocklist="${CFG_CROWDSEC_COMMUNITY_BLOCKLIST:-true}"
|
||||||
if [[ "$community_blocklist" == "true" ]]; then
|
if [[ "$community_blocklist" == "true" ]]; then
|
||||||
if runSystem cscli capi status 2>&1 | grep -qi 'You can successfully'; then
|
if runCrowdsec capi status 2>&1 | grep -qi 'You can successfully'; then
|
||||||
isNotice "Community blocklist already registered."
|
isNotice "Community blocklist already registered."
|
||||||
else
|
else
|
||||||
local result=$(runSystem cscli capi register 2>&1)
|
local result=$(runCrowdsec capi register 2>&1)
|
||||||
checkSuccess "Registered with CrowdSec Central API (community blocklist)"
|
checkSuccess "Registered with CrowdSec Central API (community blocklist)"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
local result=$(runSystem cscli capi unregister 2>&1)
|
local result=$(runCrowdsec capi unregister 2>&1)
|
||||||
checkSuccess "Unregistered from CrowdSec Central API (community blocklist disabled)"
|
checkSuccess "Unregistered from CrowdSec Central API (community blocklist disabled)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -122,26 +84,26 @@ installCrowdsecHost()
|
|||||||
echo "---- $menu_number. SaaS Console enrollment toggle."
|
echo "---- $menu_number. SaaS Console enrollment toggle."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# cscli console enroll <token> registers this agent with the hosted
|
# `console enroll <token>` registers this agent with the hosted
|
||||||
# dashboard at app.crowdsec.net. Idempotent: if already enrolled,
|
# dashboard at app.crowdsec.net. Idempotent: if already enrolled,
|
||||||
# skip. If toggled off, disenroll. Quietly skipped when the flag
|
# skip. If toggled off, disenroll. Quietly skipped when the flag
|
||||||
# is on but the token field is empty (user hasn't pasted one yet).
|
# is on but the token field is empty (user hasn't pasted one yet).
|
||||||
local console_enroll="${CFG_CROWDSEC_CONSOLE_ENROLL:-false}"
|
local console_enroll="${CFG_CROWDSEC_CONSOLE_ENROLL:-false}"
|
||||||
local console_token="${CFG_CROWDSEC_CONSOLE_TOKEN:-}"
|
local console_token="${CFG_CROWDSEC_CONSOLE_TOKEN:-}"
|
||||||
local enrolled=false
|
local enrolled=false
|
||||||
runSystem cscli console status 2>&1 | grep -qi 'enrolled' && enrolled=true
|
runCrowdsec console status 2>&1 | grep -qi 'enrolled' && enrolled=true
|
||||||
if [[ "$console_enroll" == "true" ]]; then
|
if [[ "$console_enroll" == "true" ]]; then
|
||||||
if [[ -z "$console_token" ]]; then
|
if [[ -z "$console_token" ]]; then
|
||||||
isNotice "Console enrollment ON but CFG_CROWDSEC_CONSOLE_TOKEN is empty — paste your token from app.crowdsec.net to complete."
|
isNotice "Console enrollment ON but CFG_CROWDSEC_CONSOLE_TOKEN is empty — paste your token from app.crowdsec.net to complete."
|
||||||
elif [[ "$enrolled" == true ]]; then
|
elif [[ "$enrolled" == true ]]; then
|
||||||
isNotice "Already enrolled with the SaaS console — skipping."
|
isNotice "Already enrolled with the SaaS console — skipping."
|
||||||
else
|
else
|
||||||
local result=$(runSystem cscli console enroll "$console_token" 2>&1)
|
local result=$(runCrowdsec console enroll "$console_token" 2>&1)
|
||||||
checkSuccess "Enrolled with app.crowdsec.net SaaS console"
|
checkSuccess "Enrolled with app.crowdsec.net SaaS console"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [[ "$enrolled" == true ]]; then
|
if [[ "$enrolled" == true ]]; then
|
||||||
local result=$(runSystem cscli console disenroll 2>&1)
|
local result=$(runCrowdsec console disenroll 2>&1)
|
||||||
checkSuccess "Disenrolled from app.crowdsec.net SaaS console"
|
checkSuccess "Disenrolled from app.crowdsec.net SaaS console"
|
||||||
else
|
else
|
||||||
isNotice "SaaS console enrollment disabled — skipping."
|
isNotice "SaaS console enrollment disabled — skipping."
|
||||||
@ -157,14 +119,14 @@ installCrowdsecHost()
|
|||||||
# via host.docker.internal:host-gateway. The bouncer API key is
|
# via host.docker.internal:host-gateway. The bouncer API key is
|
||||||
# required (HTTP 401 without it), so internet exposure is gated.
|
# required (HTTP 401 without it), so internet exposure is gated.
|
||||||
# External access on 8080 should still be blocked at UFW.
|
# External access on 8080 should still be blocked at UFW.
|
||||||
local lapi_cfg="/etc/crowdsec/config.yaml"
|
local bind_result
|
||||||
if [[ -f "$lapi_cfg" ]] && ! sudo grep -qE 'listen_uri:[[:space:]]*0\.0\.0\.0:8080' "$lapi_cfg"; then
|
bind_result=$(runCrowdsec bind-lapi 2>&1)
|
||||||
sudo sed -i 's|listen_uri:.*|listen_uri: 0.0.0.0:8080|' "$lapi_cfg"
|
if [[ "$bind_result" == "ALREADY_BOUND" ]]; then
|
||||||
checkSuccess "LAPI bound to 0.0.0.0:8080"
|
|
||||||
runSystem systemctl restart crowdsec
|
|
||||||
checkSuccess "CrowdSec restarted"
|
|
||||||
else
|
|
||||||
isNotice "LAPI already bound to 0.0.0.0:8080 — skipping."
|
isNotice "LAPI already bound to 0.0.0.0:8080 — skipping."
|
||||||
|
else
|
||||||
|
checkSuccess "LAPI bound to 0.0.0.0:8080"
|
||||||
|
runCrowdsec services restart
|
||||||
|
checkSuccess "CrowdSec restarted"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
((menu_number++))
|
((menu_number++))
|
||||||
@ -174,72 +136,62 @@ installCrowdsecHost()
|
|||||||
|
|
||||||
# When monitoring is on, bind CrowdSec's Prometheus metrics endpoint to
|
# When monitoring is on, bind CrowdSec's Prometheus metrics endpoint to
|
||||||
# a docker-reachable address (the 127.0.0.1 default can't be scraped
|
# a docker-reachable address (the 127.0.0.1 default can't be scraped
|
||||||
# from the Prometheus container). The sed is scoped to the prometheus:
|
# from the Prometheus container). When off, rebind to localhost if a
|
||||||
# block. When off, rebind to localhost if a prior run opened it.
|
# prior run opened it. The helper does the scoped edit in the
|
||||||
local cs_cfg="/etc/crowdsec/config.yaml"
|
# prometheus: block.
|
||||||
local mon_enabled="${CFG_CROWDSEC_MONITORING:-false}"
|
local mon_enabled="${CFG_CROWDSEC_MONITORING:-false}"
|
||||||
local prom_listen="${CFG_CROWDSEC_PROMETHEUS_LISTEN:-0.0.0.0:6060}"
|
local prom_listen="${CFG_CROWDSEC_PROMETHEUS_LISTEN:-0.0.0.0:6060}"
|
||||||
local prom_addr="${prom_listen%%:*}"
|
local prom_addr="${prom_listen%%:*}"
|
||||||
local prom_port="${prom_listen##*:}"
|
local prom_port="${prom_listen##*:}"
|
||||||
if [[ "$mon_enabled" == "true" && -f "$cs_cfg" ]]; then
|
if [[ "$mon_enabled" == "true" ]]; then
|
||||||
if ! sudo grep -qE "listen_addr:[[:space:]]*${prom_addr}" "$cs_cfg"; then
|
local result=$(runCrowdsec prometheus on "$prom_addr" "$prom_port")
|
||||||
sudo sed -i "/^prometheus:/,/^[^[:space:]#]/ {
|
|
||||||
s|enabled:.*|enabled: true|
|
|
||||||
s|listen_addr:.*|listen_addr: ${prom_addr}|
|
|
||||||
s|listen_port:.*|listen_port: ${prom_port}|
|
|
||||||
}" "$cs_cfg"
|
|
||||||
checkSuccess "CrowdSec metrics endpoint bound to ${prom_listen}"
|
checkSuccess "CrowdSec metrics endpoint bound to ${prom_listen}"
|
||||||
runSystem systemctl restart crowdsec
|
runCrowdsec services restart
|
||||||
checkSuccess "CrowdSec restarted"
|
checkSuccess "CrowdSec restarted"
|
||||||
else
|
else
|
||||||
isNotice "CrowdSec metrics already bound to ${prom_addr} — skipping."
|
local result=$(runCrowdsec prometheus off)
|
||||||
fi
|
|
||||||
elif [[ -f "$cs_cfg" ]] && sudo grep -qE 'listen_addr:[[:space:]]*0\.0\.0\.0' "$cs_cfg"; then
|
|
||||||
sudo sed -i "/^prometheus:/,/^[^[:space:]#]/ s|listen_addr:.*|listen_addr: 127.0.0.1|" "$cs_cfg"
|
|
||||||
checkSuccess "CrowdSec metrics endpoint rebound to 127.0.0.1 (monitoring off)"
|
checkSuccess "CrowdSec metrics endpoint rebound to 127.0.0.1 (monitoring off)"
|
||||||
runSystem systemctl restart crowdsec
|
runCrowdsec services restart
|
||||||
checkSuccess "CrowdSec restarted"
|
checkSuccess "CrowdSec restarted"
|
||||||
else
|
|
||||||
isNotice "Monitoring off — CrowdSec metrics endpoint left at its default."
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate a dedicated bouncer key for Traefik (idempotent: skip if
|
((menu_number++))
|
||||||
# already registered). Two sinks for the value:
|
echo ""
|
||||||
|
echo "---- $menu_number. Traefik bouncer API key."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Generate a dedicated bouncer key for Traefik. Two sinks for the value:
|
||||||
# 1) /etc/crowdsec/traefik_bouncer.key — raw key, bind-mounted into
|
# 1) /etc/crowdsec/traefik_bouncer.key — raw key, bind-mounted into
|
||||||
# the Traefik container read-only; the plugin reads it via
|
# the Traefik container read-only; the plugin reads it via
|
||||||
# crowdsecLapiKeyFile. /etc/crowdsec/ is outside the framework's
|
# crowdsecLapiKeyFile. /etc/crowdsec/ is outside the framework's
|
||||||
# sourceScanFiles sweep so a bare key file is safe here.
|
# sourceScanFiles sweep so a bare key file is safe here.
|
||||||
# 2) /docker/configs/network/network_crowdsec — CFG_CROWDSEC_TRAEFIK_LAPI_KEY
|
# 2) ${configs_dir}security/security_crowdsec — CFG_CROWDSEC_TRAEFIK_LAPI_KEY
|
||||||
# line, sourced by the framework and visible on the config page.
|
# line, sourced by the framework and visible on the config page.
|
||||||
# Editing the CFG var manually does not re-register the bouncer
|
# Editing the CFG var manually does not re-register the bouncer
|
||||||
# (use the rotate Tools action for that); this is a visibility
|
# (use the rotate Tools action for that); this is a visibility
|
||||||
# surface, not the auth source of truth.
|
# surface, not the auth source of truth.
|
||||||
local key_file="/etc/crowdsec/traefik_bouncer.key"
|
# The helper handles cscli + tee + chown + chmod atomically.
|
||||||
local cfg_file="${configs_dir}security/security_crowdsec"
|
local init_result
|
||||||
|
init_result=$(runCrowdsec bouncer-traefik-init 2>&1)
|
||||||
if ! runSystem cscli bouncers list -o raw 2>/dev/null | grep -q '^traefik-bouncer'; then
|
if [[ "$init_result" == "EXISTS" ]]; then
|
||||||
local bouncer_key
|
isNotice "Bouncer 'traefik-bouncer' already registered — leaving existing key file untouched at /etc/crowdsec/traefik_bouncer.key."
|
||||||
bouncer_key=$(runSystem cscli bouncers add traefik-bouncer -o raw 2>&1 | tail -1)
|
elif [[ "$init_result" == GENERATED:* ]]; then
|
||||||
if [[ -n "$bouncer_key" && "$bouncer_key" != *"error"* ]]; then
|
local bouncer_key="${init_result#GENERATED:}"
|
||||||
echo "$bouncer_key" | sudo tee "$key_file" >/dev/null
|
|
||||||
sudo chown "$sudo_user_name:$sudo_user_name" "$key_file"
|
|
||||||
sudo chmod 0600 "$key_file"
|
|
||||||
checkSuccess "Traefik bouncer API key generated"
|
checkSuccess "Traefik bouncer API key generated"
|
||||||
|
|
||||||
# Write the key into the live config file so it's visible /
|
# Mirror the key into the live config file so it's visible /
|
||||||
# editable via the framework's config page like any other
|
# editable via the framework's config page like any other CFG_*
|
||||||
# CFG_* setting.
|
# setting. configs/ is manager-owned, so runInstallOp suffices.
|
||||||
|
local cfg_file="${configs_dir}security/security_crowdsec"
|
||||||
if [[ -f "$cfg_file" ]]; then
|
if [[ -f "$cfg_file" ]]; then
|
||||||
sudo sed -i "s|^CFG_CROWDSEC_TRAEFIK_LAPI_KEY=.*|CFG_CROWDSEC_TRAEFIK_LAPI_KEY=${bouncer_key}|" "$cfg_file"
|
runInstallOp sed -i "s|^CFG_CROWDSEC_TRAEFIK_LAPI_KEY=.*|CFG_CROWDSEC_TRAEFIK_LAPI_KEY=${bouncer_key}|" "$cfg_file"
|
||||||
checkSuccess "Key mirrored to CFG_CROWDSEC_TRAEFIK_LAPI_KEY"
|
checkSuccess "Key mirrored to CFG_CROWDSEC_TRAEFIK_LAPI_KEY"
|
||||||
else
|
else
|
||||||
isNotice "Live config not present yet — key applied on next install."
|
isNotice "Live config not present yet — key applied on next install."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
isNotice "Failed to generate bouncer key — Traefik integration won't authenticate. Re-run installCrowdsecHost to retry."
|
isNotice "Failed to generate bouncer key: $init_result"
|
||||||
fi
|
isNotice "Traefik integration won't authenticate. Re-run installCrowdsecHost to retry."
|
||||||
else
|
|
||||||
isNotice "Bouncer 'traefik-bouncer' already registered — leaving existing key file untouched at $key_file."
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
((menu_number++))
|
((menu_number++))
|
||||||
@ -291,11 +243,8 @@ installCrowdsecHost()
|
|||||||
echo "---- $menu_number. Re-enabling CrowdSec services."
|
echo "---- $menu_number. Re-enabling CrowdSec services."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local result=$(runSystem systemctl enable --now crowdsec)
|
local result=$(runCrowdsec services enable)
|
||||||
checkSuccess "Enabling CrowdSec agent"
|
checkSuccess "Enabling CrowdSec agent + firewall bouncer"
|
||||||
|
|
||||||
local result=$(runSystem systemctl enable --now crowdsec-firewall-bouncer)
|
|
||||||
checkSuccess "Enabling CrowdSec firewall bouncer"
|
|
||||||
|
|
||||||
isSuccessful "CrowdSec services re-enabled."
|
isSuccessful "CrowdSec services re-enabled."
|
||||||
menu_number=0
|
menu_number=0
|
||||||
@ -312,11 +261,8 @@ installCrowdsecHost()
|
|||||||
echo "---- $menu_number. Stopping and disabling CrowdSec services."
|
echo "---- $menu_number. Stopping and disabling CrowdSec services."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local result=$(runSystem systemctl disable --now crowdsec-firewall-bouncer 2>&1)
|
local result=$(runCrowdsec services disable)
|
||||||
checkSuccess "Disabling CrowdSec firewall bouncer"
|
checkSuccess "Disabling CrowdSec agent + firewall bouncer"
|
||||||
|
|
||||||
local result=$(runSystem systemctl disable --now crowdsec 2>&1)
|
|
||||||
checkSuccess "Disabling CrowdSec agent"
|
|
||||||
|
|
||||||
isSuccessful "CrowdSec disabled. Package remains installed — set CFG_CROWDSEC_ENABLED=true to re-enable, or uninstall via the Tools tab."
|
isSuccessful "CrowdSec disabled. Package remains installed — set CFG_CROWDSEC_ENABLED=true to re-enable, or uninstall via the Tools tab."
|
||||||
menu_number=0
|
menu_number=0
|
||||||
|
|||||||
5
init.sh
5
init.sh
@ -892,7 +892,8 @@ Cmnd_Alias LP_HELPERS = ${lp_lib_dir}/libreportal-ownership, \\
|
|||||||
${lp_lib_dir}/libreportal-socket, \\
|
${lp_lib_dir}/libreportal-socket, \\
|
||||||
${lp_lib_dir}/libreportal-svc, \\
|
${lp_lib_dir}/libreportal-svc, \\
|
||||||
${lp_lib_dir}/libreportal-bininstall, \\
|
${lp_lib_dir}/libreportal-bininstall, \\
|
||||||
${lp_lib_dir}/libreportal-appcfg
|
${lp_lib_dir}/libreportal-appcfg, \\
|
||||||
|
${lp_lib_dir}/libreportal-crowdsec
|
||||||
Cmnd_Alias LP_SYSTEM = /usr/bin/systemctl, /usr/sbin/ufw, /usr/local/bin/ufw-docker, \\
|
Cmnd_Alias LP_SYSTEM = /usr/bin/systemctl, /usr/sbin/ufw, /usr/local/bin/ufw-docker, \\
|
||||||
/usr/sbin/nft, /usr/sbin/sysctl, /sbin/sysctl, \\
|
/usr/sbin/nft, /usr/sbin/sysctl, /sbin/sysctl, \\
|
||||||
/usr/bin/loginctl, /usr/sbin/service
|
/usr/bin/loginctl, /usr/sbin/service
|
||||||
@ -932,7 +933,7 @@ initRootHelpers()
|
|||||||
# sudo's (the trust boundary the scoped sudoers relies on).
|
# sudo's (the trust boundary the scoped sudoers relies on).
|
||||||
sudo install -d -m 0755 -o root -g root "$lp_lib_dir"
|
sudo install -d -m 0755 -o root -g root "$lp_lib_dir"
|
||||||
local helper helper_src helper_dst helper_tmp
|
local helper helper_src helper_dst helper_tmp
|
||||||
for helper in libreportal-ownership libreportal-dns libreportal-ssh-access libreportal-socket libreportal-svc libreportal-bininstall libreportal-appcfg; do
|
for helper in libreportal-ownership libreportal-dns libreportal-ssh-access libreportal-socket libreportal-svc libreportal-bininstall libreportal-appcfg libreportal-crowdsec; do
|
||||||
helper_src="$script_dir/scripts/system/$helper"
|
helper_src="$script_dir/scripts/system/$helper"
|
||||||
helper_dst="$lp_lib_dir/$helper"
|
helper_dst="$lp_lib_dir/$helper"
|
||||||
if [[ ! -f "$helper_src" ]]; then
|
if [[ ! -f "$helper_src" ]]; then
|
||||||
|
|||||||
@ -136,10 +136,19 @@ runSvc() { _runRootHelper libreportal-svc "$@"; }
|
|||||||
runBinInstall() { _runRootHelper libreportal-bininstall "$@"; }
|
runBinInstall() { _runRootHelper libreportal-bininstall "$@"; }
|
||||||
|
|
||||||
# App config-file rewrites owned by in-container uids / root /etc:
|
# App config-file rewrites owned by in-container uids / root /etc:
|
||||||
# {adguard-auth <user> <bcrypt>|crowdsec-priority|
|
# {adguard-auth <user> <bcrypt>|owncloud-config <public> <host> <ip> <public_ip>|
|
||||||
# owncloud-config <public> <host> <ip> <public_ip>}
|
# wireguard-ip-forward}
|
||||||
runAppCfg() { _runRootHelper libreportal-appcfg "$@"; }
|
runAppCfg() { _runRootHelper libreportal-appcfg "$@"; }
|
||||||
|
|
||||||
|
# CrowdSec host-side privileged ops — apt install of the agent + firewall
|
||||||
|
# bouncer, cscli register/enroll, /etc/crowdsec/* edits, /var/log/crowdsec*.log
|
||||||
|
# touch+chmod, /etc/crowdsec/traefik_bouncer.key write. One audit funnel for
|
||||||
|
# every operation the host-side CrowdSec install needs the manager can't drop:
|
||||||
|
# {install|services <enable|disable|restart>|capi <register|unregister|status>
|
||||||
|
# |console <enroll <token>|disenroll|status>|bouncer-traefik-init|
|
||||||
|
# bouncer-priority|bind-lapi|prometheus <on <addr> <port>|off>|touch-host-logs}
|
||||||
|
runCrowdsec() { _runRootHelper libreportal-crowdsec "$@"; }
|
||||||
|
|
||||||
# Genuine system-administration command (ufw/systemctl/apt/sysctl/useradd, /etc
|
# Genuine system-administration command (ufw/systemctl/apt/sysctl/useradd, /etc
|
||||||
# edits). Needs real root in both modes; funnelled through one place so it can
|
# edits). Needs real root in both modes; funnelled through one place so it can
|
||||||
# later be confined to a scoped sudoers allowlist.
|
# later be confined to a scoped sudoers allowlist.
|
||||||
|
|||||||
@ -720,6 +720,7 @@ declare -gA LP_FN_MAP=(
|
|||||||
[runAsManager]="docker/command/run_privileged.sh"
|
[runAsManager]="docker/command/run_privileged.sh"
|
||||||
[runBackupOp]="docker/command/run_privileged.sh"
|
[runBackupOp]="docker/command/run_privileged.sh"
|
||||||
[runBinInstall]="docker/command/run_privileged.sh"
|
[runBinInstall]="docker/command/run_privileged.sh"
|
||||||
|
[runCrowdsec]="docker/command/run_privileged.sh"
|
||||||
[runFileOp]="docker/command/run_privileged.sh"
|
[runFileOp]="docker/command/run_privileged.sh"
|
||||||
[runFileWrite]="docker/command/run_privileged.sh"
|
[runFileWrite]="docker/command/run_privileged.sh"
|
||||||
[runInstallOp]="docker/command/run_privileged.sh"
|
[runInstallOp]="docker/command/run_privileged.sh"
|
||||||
@ -1582,6 +1583,7 @@ declare -gA LP_FN_ROOT=(
|
|||||||
[runAsManager]="scripts"
|
[runAsManager]="scripts"
|
||||||
[runBackupOp]="scripts"
|
[runBackupOp]="scripts"
|
||||||
[runBinInstall]="scripts"
|
[runBinInstall]="scripts"
|
||||||
|
[runCrowdsec]="scripts"
|
||||||
[runFileOp]="scripts"
|
[runFileOp]="scripts"
|
||||||
[runFileWrite]="scripts"
|
[runFileWrite]="scripts"
|
||||||
[runInstallOp]="scripts"
|
[runInstallOp]="scripts"
|
||||||
@ -2462,6 +2464,7 @@ runAppCfg() { source "${install_scripts_dir}docker/command/run_privileged.sh"; r
|
|||||||
runAsManager() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runAsManager "$@"; }
|
runAsManager() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runAsManager "$@"; }
|
||||||
runBackupOp() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runBackupOp "$@"; }
|
runBackupOp() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runBackupOp "$@"; }
|
||||||
runBinInstall() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runBinInstall "$@"; }
|
runBinInstall() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runBinInstall "$@"; }
|
||||||
|
runCrowdsec() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runCrowdsec "$@"; }
|
||||||
runFileOp() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runFileOp "$@"; }
|
runFileOp() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runFileOp "$@"; }
|
||||||
runFileWrite() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runFileWrite "$@"; }
|
runFileWrite() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runFileWrite "$@"; }
|
||||||
runInstallOp() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runInstallOp "$@"; }
|
runInstallOp() { source "${install_scripts_dir}docker/command/run_privileged.sh"; runInstallOp "$@"; }
|
||||||
|
|||||||
@ -68,22 +68,6 @@ EOF
|
|||||||
sysctl --system >/dev/null 2>&1 || sysctl -p "$dropin" >/dev/null 2>&1 || true
|
sysctl --system >/dev/null 2>&1 || sysctl -p "$dropin" >/dev/null 2>&1 || true
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- CrowdSec: set nftables ipv4/ipv6 priority to -100 in the bouncer yaml ------
|
|
||||||
crowdsec_priority() {
|
|
||||||
local cfg="/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml"
|
|
||||||
[[ -f "$cfg" ]] || { echo "libreportal-appcfg: $cfg not found" >&2; return 1; }
|
|
||||||
cp "$cfg" "${cfg}.bak.$(date +%Y%m%d-%H%M%S)"
|
|
||||||
awk -v p="-100" '
|
|
||||||
BEGIN { in_v4=0; in_v6=0 }
|
|
||||||
/^[[:space:]]*ipv4:/ { in_v4=1; in_v6=0; print; next }
|
|
||||||
/^[[:space:]]*ipv6:/ { in_v6=1; in_v4=0; print; next }
|
|
||||||
/^[a-zA-Z]/ { in_v4=0; in_v6=0 }
|
|
||||||
in_v4 && /^[[:space:]]+priority:/ { sub(/priority:.*/, "priority: " p) }
|
|
||||||
in_v6 && /^[[:space:]]+priority:/ { sub(/priority:.*/, "priority: " p) }
|
|
||||||
{ print }
|
|
||||||
' "$cfg" > "${cfg}.new" && mv "${cfg}.new" "$cfg"
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- ownCloud: normalise trusted_domains + overwrite.cli.url in config.php ------
|
# --- ownCloud: normalise trusted_domains + overwrite.cli.url in config.php ------
|
||||||
owncloud_config() {
|
owncloud_config() {
|
||||||
local public="$1" host_setup="$2" ip_setup="$3" public_ip="$4"
|
local public="$1" host_setup="$2" ip_setup="$3" public_ip="$4"
|
||||||
@ -143,8 +127,7 @@ EOL
|
|||||||
action="${1:-}"; shift 2>/dev/null || true
|
action="${1:-}"; shift 2>/dev/null || true
|
||||||
case "$action" in
|
case "$action" in
|
||||||
adguard-auth) adguard_auth "${1:-}" "${2:-}" ;;
|
adguard-auth) adguard_auth "${1:-}" "${2:-}" ;;
|
||||||
crowdsec-priority) crowdsec_priority ;;
|
|
||||||
owncloud-config) owncloud_config "${1:-}" "${2:-}" "${3:-}" "${4:-}" ;;
|
owncloud-config) owncloud_config "${1:-}" "${2:-}" "${3:-}" "${4:-}" ;;
|
||||||
wireguard-ip-forward) wireguard_ip_forward ;;
|
wireguard-ip-forward) wireguard_ip_forward ;;
|
||||||
*) echo "usage: libreportal-appcfg {adguard-auth <user> <bcrypt>|crowdsec-priority|owncloud-config <public> <host> <ip> <public_ip>|wireguard-ip-forward}" >&2; exit 2 ;;
|
*) echo "usage: libreportal-appcfg {adguard-auth <user> <bcrypt>|owncloud-config <public> <host> <ip> <public_ip>|wireguard-ip-forward}" >&2; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
202
scripts/system/libreportal-crowdsec
Normal file
202
scripts/system/libreportal-crowdsec
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# LibrePortal CrowdSec helper — root-privileged ops for the host-side CrowdSec
|
||||||
|
# install (the agent + firewall bouncer the LibrePortal Traefik bouncer talks
|
||||||
|
# to). Installed root:root 0755 to /usr/local/lib/libreportal/ by init.sh.
|
||||||
|
# Self-contained: each action runs a FIXED set of ops with strictly-validated
|
||||||
|
# args, so the scoped sudoers needn't grant the manager blanket apt/cscli/tee/
|
||||||
|
# sed/chown on /etc/crowdsec or /var/log. The runtime calls in via
|
||||||
|
# `runCrowdsec <action> [args…]` (run_privileged.sh).
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
[[ $EUID -eq 0 ]] || { echo "libreportal-crowdsec: must run as root" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Baked at install; unbaked copies keep the "__" sentinel.
|
||||||
|
SYSTEM_DIR="__SYSTEM_DIR__"
|
||||||
|
CONTAINERS_DIR="__CONTAINERS_DIR__"
|
||||||
|
MANAGER_USER="__MANAGER__"
|
||||||
|
[[ "$SYSTEM_DIR" == *"__"* || -z "$SYSTEM_DIR" ]] && SYSTEM_DIR="/libreportal-system"
|
||||||
|
[[ "$CONTAINERS_DIR" == *"__"* || -z "$CONTAINERS_DIR" ]] && CONTAINERS_DIR="/libreportal-containers"
|
||||||
|
[[ "$MANAGER_USER" == *"__"* || -z "$MANAGER_USER" ]] && MANAGER_USER="libreportal"
|
||||||
|
|
||||||
|
CFG_FILE="/etc/crowdsec/config.yaml"
|
||||||
|
BOUNCER_CFG="/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml"
|
||||||
|
TRAEFIK_KEY_FILE="/etc/crowdsec/traefik_bouncer.key"
|
||||||
|
HOST_LOGS=("/var/log/crowdsec.log" "/var/log/crowdsec-firewall-bouncer.log")
|
||||||
|
|
||||||
|
# --- install: add repo + apt install + initial config -------------------------
|
||||||
|
# Idempotent: skips repo add if /etc/apt/sources.list.d/crowdsec_*.list exists,
|
||||||
|
# skips apt install if cscli is on PATH, skips collections install if already
|
||||||
|
# present.
|
||||||
|
crowdsec_install() {
|
||||||
|
if ! command -v cscli >/dev/null 2>&1; then
|
||||||
|
if ! ls /etc/apt/sources.list.d/crowdsec_*.list >/dev/null 2>&1; then
|
||||||
|
curl -fsSL https://install.crowdsec.net | bash >/dev/null 2>&1 || {
|
||||||
|
echo "libreportal-crowdsec: failed to add CrowdSec apt repo" >&2; return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -q crowdsec </dev/null >/dev/null 2>&1 || {
|
||||||
|
echo "libreportal-crowdsec: apt-get install crowdsec failed" >&2; return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
if ! dpkg -s crowdsec-firewall-bouncer-nftables >/dev/null 2>&1; then
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -q crowdsec-firewall-bouncer-nftables </dev/null >/dev/null 2>&1 || {
|
||||||
|
echo "libreportal-crowdsec: apt-get install firewall-bouncer failed" >&2; return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
systemctl enable --now crowdsec >/dev/null 2>&1 || true
|
||||||
|
systemctl enable --now crowdsec-firewall-bouncer >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Baseline collections. cscli is idempotent ("already installed" exits 0).
|
||||||
|
cscli collections install crowdsecurity/linux >/dev/null 2>&1 || true
|
||||||
|
cscli collections install crowdsecurity/sshd >/dev/null 2>&1 || true
|
||||||
|
systemctl reload crowdsec >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- services {enable|disable|restart} ----------------------------------------
|
||||||
|
crowdsec_services() {
|
||||||
|
case "${1:-}" in
|
||||||
|
enable)
|
||||||
|
systemctl enable --now crowdsec || return 1
|
||||||
|
systemctl enable --now crowdsec-firewall-bouncer || return 1
|
||||||
|
;;
|
||||||
|
disable)
|
||||||
|
systemctl disable --now crowdsec-firewall-bouncer >/dev/null 2>&1 || true
|
||||||
|
systemctl disable --now crowdsec >/dev/null 2>&1 || true
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
systemctl restart crowdsec || return 1
|
||||||
|
;;
|
||||||
|
*) echo "libreportal-crowdsec: services {enable|disable|restart}" >&2; return 2 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- capi {register|unregister|status} ----------------------------------------
|
||||||
|
# Community blocklist (subscribe + send anonymous attack signals).
|
||||||
|
crowdsec_capi() {
|
||||||
|
case "${1:-}" in
|
||||||
|
register) cscli capi register 2>&1 ;;
|
||||||
|
unregister) cscli capi unregister 2>&1 ;;
|
||||||
|
status) cscli capi status 2>&1 ;;
|
||||||
|
*) echo "libreportal-crowdsec: capi {register|unregister|status}" >&2; return 2 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- console {enroll <token>|disenroll|status} --------------------------------
|
||||||
|
# SaaS dashboard at app.crowdsec.net. Token strictly validated.
|
||||||
|
crowdsec_console() {
|
||||||
|
case "${1:-}" in
|
||||||
|
enroll)
|
||||||
|
local token="${2:-}"
|
||||||
|
[[ "$token" =~ ^[A-Za-z0-9_-]{8,128}$ ]] || {
|
||||||
|
echo "libreportal-crowdsec: console enroll <token> — invalid token format" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cscli console enroll "$token" 2>&1
|
||||||
|
;;
|
||||||
|
disenroll) cscli console disenroll 2>&1 ;;
|
||||||
|
status) cscli console status 2>&1 ;;
|
||||||
|
*) echo "libreportal-crowdsec: console {enroll <token>|disenroll|status}" >&2; return 2 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- bouncer-traefik-init: cscli register + write key file --------------------
|
||||||
|
# Idempotent: if the bouncer is already registered, skips and prints the existing
|
||||||
|
# key-file marker so callers can decide whether to keep or rotate. Returns the
|
||||||
|
# fresh key on stdout when newly generated, "EXISTS" when already registered.
|
||||||
|
crowdsec_bouncer_traefik_init() {
|
||||||
|
if cscli bouncers list -o raw 2>/dev/null | grep -q '^traefik-bouncer'; then
|
||||||
|
echo "EXISTS"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local key
|
||||||
|
key=$(cscli bouncers add traefik-bouncer -o raw 2>&1 | tail -1)
|
||||||
|
[[ -n "$key" && "$key" != *"error"* ]] || {
|
||||||
|
echo "libreportal-crowdsec: cscli bouncers add failed: $key" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
# Write the key file: bind-mounted RO into Traefik. Manager-owned so the
|
||||||
|
# WebUI/config layer (running as the manager) can read it; mode 0600.
|
||||||
|
printf '%s\n' "$key" > "$TRAEFIK_KEY_FILE"
|
||||||
|
chown "${MANAGER_USER}:${MANAGER_USER}" "$TRAEFIK_KEY_FILE"
|
||||||
|
chmod 0600 "$TRAEFIK_KEY_FILE"
|
||||||
|
echo "GENERATED:$key"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- bind-lapi: set listen_uri to 0.0.0.0:8080 in config.yaml -----------------
|
||||||
|
# Traefik talks to LAPI via host.docker.internal:8080. Bouncer API key gates
|
||||||
|
# external access (HTTP 401 without it).
|
||||||
|
crowdsec_bind_lapi() {
|
||||||
|
[[ -f "$CFG_FILE" ]] || { echo "libreportal-crowdsec: $CFG_FILE not found" >&2; return 1; }
|
||||||
|
if grep -qE 'listen_uri:[[:space:]]*0\.0\.0\.0:8080' "$CFG_FILE"; then
|
||||||
|
echo "ALREADY_BOUND"; return 0
|
||||||
|
fi
|
||||||
|
sed -i 's|listen_uri:.*|listen_uri: 0.0.0.0:8080|' "$CFG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- prometheus {on <addr> <port>|off} ----------------------------------------
|
||||||
|
# Flip CrowdSec's Prometheus metrics endpoint. `on` requires an addr + port the
|
||||||
|
# Prometheus container can reach; `off` rebinds to 127.0.0.1.
|
||||||
|
crowdsec_prometheus() {
|
||||||
|
[[ -f "$CFG_FILE" ]] || { echo "libreportal-crowdsec: $CFG_FILE not found" >&2; return 1; }
|
||||||
|
case "${1:-}" in
|
||||||
|
on)
|
||||||
|
local addr="${2:-}" port="${3:-}"
|
||||||
|
[[ "$addr" =~ ^[A-Za-z0-9.-]+$ ]] || { echo "libreportal-crowdsec: invalid addr" >&2; return 1; }
|
||||||
|
[[ "$port" =~ ^[0-9]+$ ]] || { echo "libreportal-crowdsec: invalid port" >&2; return 1; }
|
||||||
|
sed -i "/^prometheus:/,/^[^[:space:]#]/ {
|
||||||
|
s|enabled:.*|enabled: true|
|
||||||
|
s|listen_addr:.*|listen_addr: ${addr}|
|
||||||
|
s|listen_port:.*|listen_port: ${port}|
|
||||||
|
}" "$CFG_FILE"
|
||||||
|
;;
|
||||||
|
off)
|
||||||
|
sed -i "/^prometheus:/,/^[^[:space:]#]/ s|listen_addr:.*|listen_addr: 127.0.0.1|" "$CFG_FILE"
|
||||||
|
;;
|
||||||
|
*) echo "libreportal-crowdsec: prometheus {on <addr> <port>|off}" >&2; return 2 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- touch-host-logs: make crowdsec logs readable by the libreportal container --
|
||||||
|
crowdsec_touch_host_logs() {
|
||||||
|
local l
|
||||||
|
for l in "${HOST_LOGS[@]}"; do
|
||||||
|
touch "$l" 2>/dev/null || true
|
||||||
|
chmod 0644 "$l" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- bouncer-priority: set nftables ipv4/ipv6 priority to -100 -----------------
|
||||||
|
# Moved here from libreportal-appcfg.crowdsec_priority. Same transform; lives in
|
||||||
|
# the dedicated crowdsec helper so all CrowdSec-touching root ops are in one
|
||||||
|
# auditable file.
|
||||||
|
crowdsec_bouncer_priority() {
|
||||||
|
[[ -f "$BOUNCER_CFG" ]] || { echo "libreportal-crowdsec: $BOUNCER_CFG not found" >&2; return 1; }
|
||||||
|
cp "$BOUNCER_CFG" "${BOUNCER_CFG}.bak.$(date +%Y%m%d-%H%M%S)"
|
||||||
|
awk -v p="-100" '
|
||||||
|
BEGIN { in_v4=0; in_v6=0 }
|
||||||
|
/^[[:space:]]*ipv4:/ { in_v4=1; in_v6=0; print; next }
|
||||||
|
/^[[:space:]]*ipv6:/ { in_v6=1; in_v4=0; print; next }
|
||||||
|
/^[a-zA-Z]/ { in_v4=0; in_v6=0 }
|
||||||
|
in_v4 && /^[[:space:]]+priority:/ { sub(/priority:.*/, "priority: " p) }
|
||||||
|
in_v6 && /^[[:space:]]+priority:/ { sub(/priority:.*/, "priority: " p) }
|
||||||
|
{ print }
|
||||||
|
' "$BOUNCER_CFG" > "${BOUNCER_CFG}.new" && mv "${BOUNCER_CFG}.new" "$BOUNCER_CFG"
|
||||||
|
}
|
||||||
|
|
||||||
|
action="${1:-}"; shift 2>/dev/null || true
|
||||||
|
case "$action" in
|
||||||
|
install) crowdsec_install ;;
|
||||||
|
services) crowdsec_services "${1:-}" ;;
|
||||||
|
capi) crowdsec_capi "${1:-}" ;;
|
||||||
|
console) crowdsec_console "${1:-}" "${2:-}" ;;
|
||||||
|
bouncer-traefik-init) crowdsec_bouncer_traefik_init ;;
|
||||||
|
bouncer-priority) crowdsec_bouncer_priority ;;
|
||||||
|
bind-lapi) crowdsec_bind_lapi ;;
|
||||||
|
prometheus) crowdsec_prometheus "${1:-}" "${2:-}" "${3:-}" ;;
|
||||||
|
touch-host-logs) crowdsec_touch_host_logs ;;
|
||||||
|
*)
|
||||||
|
echo "usage: libreportal-crowdsec {install|services <enable|disable|restart>|capi <register|unregister|status>|console <enroll <token>|disenroll|status>|bouncer-traefik-init|bouncer-priority|bind-lapi|prometheus <on <addr> <port>|off>|touch-host-logs}" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Loading…
x
Reference in New Issue
Block a user