This commit is contained in:
mjallen18
2026-03-24 14:41:22 -05:00
parent 4cc58ab381
commit 84eb2e3734
11 changed files with 799 additions and 48 deletions

View File

@@ -6,15 +6,51 @@
...
}:
let
inherit (lib.${namespace}) mkOpt;
inherit (lib.${namespace}) mkOpt mkBoolOpt;
name = "crowdsec";
cfg = config.${namespace}.services.${name};
ntfyServer = "https://ntfy.mjallen.dev";
ntfyTopic = "crowdsec";
# CrowdSec HTTP notification plugin config — written to
# /etc/crowdsec/notifications/ntfy.yaml at runtime. Credentials are
# injected via EnvironmentFile so the plugin can reference them with
# {{env "NTFY_USER"}} / {{env "NTFY_PASSWORD"}} in the URL.
ntfyPluginConfig = pkgs.writeText "crowdsec-ntfy.yaml" ''
type: http
name: ntfy_plugin
log_level: info
format: |
{{range . -}}
CrowdSec blocked: {{.Scenario}}
Source IP: {{.Source.Value}}
Country: {{.Source.Cn}}
Decisions: {{.Decisions | len}}
{{range .Decisions -}}
Action: {{.Type}} for {{.Duration}}
{{end}}
{{- end}}
url: ${ntfyServer}/${ntfyTopic}
method: POST
headers:
Title: "CrowdSec: {{(index . 0).Scenario}}"
Priority: "high"
Tags: "rotating_light,shield"
Authorization: "Basic {{b64enc (print (env "NTFY_USER") ":" (env "NTFY_PASSWORD"))}}"
skip_tls_verify: false
timeout: 10s
'';
crowdsecConfig = lib.${namespace}.mkModule {
inherit config name;
description = "crowdsec";
options = with lib; {
apiKey = mkOpt types.str "" "API key for crowdsec bouncer";
ntfy = {
enable = mkBoolOpt false "Send ntfy notifications on new CrowdSec alerts";
envFile = mkOpt types.str "" "Path to env file containing NTFY_USER and NTFY_PASSWORD";
};
};
moduleConfig = {
services = {
@@ -199,6 +235,57 @@ let
user = "crowdsec";
group = "crowdsec";
};
# ---------------------------------------------------------------------------
# ntfy notifications via the CrowdSec HTTP notification plugin
# ---------------------------------------------------------------------------
# Drop the plugin config YAML into /etc/crowdsec/notifications/.
# CrowdSec scans this directory on startup and registers any plugin
# config files it finds.
environment.etc."crowdsec/notifications/ntfy.yaml" = lib.mkIf cfg.ntfy.enable {
source = ntfyPluginConfig;
mode = "0440";
user = "crowdsec";
group = "crowdsec";
};
# CrowdSec profiles.yaml: route every alert to the ntfy plugin.
# This replaces the default "do nothing" profile.
environment.etc."crowdsec/profiles.yaml" = lib.mkIf cfg.ntfy.enable {
text = ''
name: default_ip_remediation
filters:
- Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
- type: ban
duration: 4h
notifications:
- ntfy_plugin
on_success: break
---
name: default_range_remediation
filters:
- Alert.Remediation == true && Alert.GetScope() == "Range"
decisions:
- type: ban
duration: 4h
notifications:
- ntfy_plugin
on_success: break
'';
mode = "0440";
user = "crowdsec";
group = "crowdsec";
};
# Inject NTFY_USER and NTFY_PASSWORD into the crowdsec service so the
# HTTP plugin template can reference them. The plugin config uses
# {{env "NTFY_BASIC_AUTH"}} — a pre-encoded "user:pass" base64 string
# for the Authorization: Basic header — computed in ExecStartPre.
systemd.services.crowdsec.serviceConfig.EnvironmentFile = lib.mkIf cfg.ntfy.enable [
cfg.ntfy.envFile
];
};
};
in