Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
result
|
||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
132
README.md
Normal file
132
README.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Crowdsec for NixOS
|
||||||
|
|
||||||
|
This repository contains a [Nix flake](https://nixos.wiki/wiki/Flakes) for running [Crowdsec](https://www.crowdsec.net/) on NixOS.
|
||||||
|
|
||||||
|
CrowdSec is a security tool designed to protect servers, services, and applications by analyzing user behavior and network traffic to detect and block potential attacks. It operates similarly to Fail2Ban but with a few key differences:
|
||||||
|
|
||||||
|
CrowdSec leverages the power of its community by sharing information about attacks among users. When one user detects a new threat, the details are shared across the network, allowing others to protect themselves against this threat, effectively creating a collective intelligence about emerging threats.
|
||||||
|
|
||||||
|
In simple terms, think of CrowdSec as a neighborhood watch program for the internet, where everyone contributes to and benefits from a shared pool of intelligence about potential threats.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Crowdsec engine
|
||||||
|
|
||||||
|
To setup the [security engine](https://docs.crowdsec.net/docs/getting_started/security_engine_intro/), import the module and activate the service.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
crowdsec = {
|
||||||
|
url = "github:kampka/nix-flake-crowdsec";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = flakes @ {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
crowdsec,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
nixosConfiguration.<your-hostname> = nixpkgs.lib.nixosSystem {
|
||||||
|
# ...
|
||||||
|
modules = [
|
||||||
|
# ...
|
||||||
|
crowdsec.nixosModules.crowdsec
|
||||||
|
|
||||||
|
({ pkgs, lib, ... }: {
|
||||||
|
services.crowdsec = {
|
||||||
|
enable = true;
|
||||||
|
enrollKeyFile = "/path/to/enroll-key";
|
||||||
|
settings = {
|
||||||
|
api.server = {
|
||||||
|
listen_uri = "127.0.0.1:8080";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In case you are setting up a central security engine, adjust the `listen_uri` to be reachable by your bouncers.
|
||||||
|
|
||||||
|
To enroll your crowdsec engine into the central API, you need to obtain an enrollment key from the central [app dashboard](https://app.crowdsec.net/).
|
||||||
|
Enrolling your engine will give it access to community or commercial blocklist and decisions, depending on your plan.
|
||||||
|
Enrollment is optional, if you do not want to enroll your engine and just at on your own logs / events, simply omit the `enrollKeyFile` from the settings.
|
||||||
|
|
||||||
|
For additional configuration options, please consult the (Crowdsec documentation)[https://docs.crowdsec.net/docs/configuration/crowdsec_configuration/].
|
||||||
|
|
||||||
|
|
||||||
|
### Crowdsec firewall bouncer
|
||||||
|
|
||||||
|
This flake ships the Crowdsec [firewall bouncer](https://docs.crowdsec.net/docs/getting_started/security_engine_intro/).
|
||||||
|
It will block traffic from blacklisted IPs on the firewall level.
|
||||||
|
|
||||||
|
At the time of writing, only `iptables` support has proper defaults and testing.
|
||||||
|
If you are using `nftables` (`networking.nftables.enable = true`), you need to supply bouncer configuration yourself (PRs welcome).
|
||||||
|
Please consult the [bouncer documentation](https://docs.crowdsec.net/u/bouncers/firewall/#nftables-specific-directives) for directions.
|
||||||
|
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
crowdsec = {
|
||||||
|
url = "github:kampka/nix-flake-crowdsec";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = flakes @ {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
crowdsec,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
nixosConfiguration.<your-hostname> = nixpkgs.lib.nixosSystem {
|
||||||
|
# ...
|
||||||
|
modules = [
|
||||||
|
# ...
|
||||||
|
crowdsec.nixosModules.crowdsec-firewall-bouncer;
|
||||||
|
|
||||||
|
({ pkgs, lib, ... }: {
|
||||||
|
nixpkgs.overlays = [crowdsec.overlays.default];
|
||||||
|
services.crowdsec-firewall-bouncer = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
api_key = "<api-key>";
|
||||||
|
api_url = "http://localhost:8080";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to connect to your security engine, you need to [add your bouncer](https://docs.crowdsec.net/docs/cscli/cscli_bouncers_add/) to the security engine.
|
||||||
|
You can either use a pre-generated key or have the security engine generate one for you.
|
||||||
|
Depending on your security requirements and secrets management, this process is scriptable through an `ExecStartPre` script of the engine, eg.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
services.crowdsec = {
|
||||||
|
ExecStartPre = let
|
||||||
|
script = pkgs.writeScriptBin "register-bouncer" ''
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
set -eu
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
if ! cscli bouncers list | grep -q "my-bouncer"; then
|
||||||
|
cscli bouncers add "my-bouncer" --key "<api-key>"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
in ["${script}/bin/register-bouncer"];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
58
flake.lock
generated
Normal file
58
flake.lock
generated
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694529238,
|
||||||
|
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "flake-utils",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1697915759,
|
||||||
|
"narHash": "sha256-WyMj5jGcecD+KC8gEs+wFth1J1wjisZf8kVZH13f1Zo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "51d906d2341c9e866e48c2efcaac0f2d70bfd43e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
27
flake.nix
Normal file
27
flake.nix
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
description = "A Aggregate prometheus exporters into a single endpoint";
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
flake-utils,
|
||||||
|
}: let
|
||||||
|
systems = flake-utils.lib.eachDefaultSystem (system: let
|
||||||
|
pkgs = import nixpkgs {inherit system;};
|
||||||
|
|
||||||
|
bouncer-firewall = pkgs.callPackage ./packages/bouncer-firewall {};
|
||||||
|
in {
|
||||||
|
formatter = pkgs.alejandra;
|
||||||
|
packages."crowdsec-firewall-bouncer" = bouncer-firewall;
|
||||||
|
});
|
||||||
|
in (systems
|
||||||
|
// {
|
||||||
|
nixosModules = {
|
||||||
|
crowdsec = import ./modules/crowdsec;
|
||||||
|
crowdsec-firewall-bouncer = import ./modules/crowdsec-firewall-bouncer;
|
||||||
|
};
|
||||||
|
overlays.default = final: prev: {
|
||||||
|
crowdsec-firewall-bouncer = systems.packages.${final.system}.crowdsec-firewall-bouncer;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
101
modules/crowdsec-firewall-bouncer/default.nix
Normal file
101
modules/crowdsec-firewall-bouncer/default.nix
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.services.crowdsec-firewall-bouncer;
|
||||||
|
format = pkgs.formats.yaml {};
|
||||||
|
configFile = format.generate "crowdsec.yaml" cfg.settings;
|
||||||
|
|
||||||
|
pkg = cfg.package;
|
||||||
|
|
||||||
|
backend =
|
||||||
|
if config.networking.nftables.enable
|
||||||
|
then "nftables"
|
||||||
|
else "iptables";
|
||||||
|
|
||||||
|
defaultSettings = with lib; {
|
||||||
|
log_mode = "stdout";
|
||||||
|
|
||||||
|
mode = mkDefault backend;
|
||||||
|
ipset_type = mkDefault "nethash";
|
||||||
|
update_frequency = mkDefault "10s";
|
||||||
|
deny_action = mkDefault "DROP";
|
||||||
|
blacklists_ipv4 = mkDefault "crowdsec-blacklists";
|
||||||
|
blacklists_ipv6 = mkDefault "crowdsec6-blacklists";
|
||||||
|
iptables_chains = mkDefault ["INPUT"];
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
options.services.crowdsec-firewall-bouncer = with lib; {
|
||||||
|
enable = mkEnableOption "CrowSec Firewall Bouncer";
|
||||||
|
package = mkPackageOption pkgs "crowdsec-firewall-bouncer" {};
|
||||||
|
settings = mkOption {
|
||||||
|
description = mdDoc ''
|
||||||
|
Settings for CrowdSec Firewall Bouncer. Refer to <https://docs.crowdsec.net/u/bouncers/firewall/#configuration-directives> for details.
|
||||||
|
'';
|
||||||
|
type = format.type;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = lib.mkIf (cfg.enable) {
|
||||||
|
services.crowdsec-firewall-bouncer.settings = defaultSettings;
|
||||||
|
|
||||||
|
systemd.packages = [pkg];
|
||||||
|
systemd.services = {
|
||||||
|
crowdsec-firewall-bouncer = {
|
||||||
|
description = "Crowdsec Firewall Bouncer";
|
||||||
|
|
||||||
|
path = [pkg pkgs.ipset pkgs.iptables pkgs.nftables];
|
||||||
|
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
partOf = ["firewall.service"];
|
||||||
|
|
||||||
|
serviceConfig = with lib; {
|
||||||
|
Type = "notify";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 10;
|
||||||
|
|
||||||
|
LimitNOFILE = mkDefault 65536;
|
||||||
|
|
||||||
|
MemoryDenyWriteExecute = mkDefault true;
|
||||||
|
|
||||||
|
CapabilityBoundingSet = mkDefault ["CAP_NET_ADMIN" "CAP_NET_RAW"];
|
||||||
|
|
||||||
|
NoNewPrivileges = mkDefault true;
|
||||||
|
LockPersonality = mkDefault true;
|
||||||
|
RemoveIPC = mkDefault true;
|
||||||
|
|
||||||
|
ProtectSystem = mkDefault "strict";
|
||||||
|
ProtectHome = mkDefault true;
|
||||||
|
|
||||||
|
PrivateTmp = mkDefault true;
|
||||||
|
PrivateDevices = mkDefault true;
|
||||||
|
ProtectHostname = mkDefault true;
|
||||||
|
ProtectKernelTunables = mkDefault true;
|
||||||
|
ProtectKernelModules = mkDefault true;
|
||||||
|
ProtectControlGroups = mkDefault true;
|
||||||
|
|
||||||
|
ProtectProc = mkDefault "invisible";
|
||||||
|
ProcSubset = mkDefault "pid";
|
||||||
|
|
||||||
|
RestrictNamespaces = mkDefault true;
|
||||||
|
RestrictRealtime = mkDefault true;
|
||||||
|
RestrictSUIDSGID = mkDefault true;
|
||||||
|
|
||||||
|
SystemCallFilter = mkDefault ["@system-service" "@network-io"];
|
||||||
|
SystemCallArchitectures = ["native"];
|
||||||
|
SystemCallErrorNumber = mkDefault "EPERM";
|
||||||
|
|
||||||
|
ExecPaths = ["/nix/store"];
|
||||||
|
NoExecPaths = ["/"];
|
||||||
|
|
||||||
|
ExecStartPost = "${pkgs.coreutils}/bin/sleep 0.2";
|
||||||
|
|
||||||
|
ExecStart = "${pkg}/bin/cs-firewall-bouncer -c ${configFile}";
|
||||||
|
ExecStartPre = ["${pkg}/bin/cs-firewall-bouncer -t -c ${configFile}"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
264
modules/crowdsec/default.nix
Normal file
264
modules/crowdsec/default.nix
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.services.crowdsec;
|
||||||
|
format = pkgs.formats.yaml {};
|
||||||
|
configFile = format.generate "crowdsec.yaml" cfg.settings;
|
||||||
|
|
||||||
|
pkg = cfg.package.overrideAttrs (old: {
|
||||||
|
ldflags =
|
||||||
|
(old.ldflags or [])
|
||||||
|
++ [
|
||||||
|
"-X github.com/crowdsecurity/go-cs-lib/version.Version=v${old.version}"
|
||||||
|
];
|
||||||
|
patches =
|
||||||
|
(old.patches or [])
|
||||||
|
++ [
|
||||||
|
(
|
||||||
|
pkgs.fetchpatch
|
||||||
|
{
|
||||||
|
url = "https://patch-diff.githubusercontent.com/raw/crowdsecurity/crowdsec/pull/2868.patch";
|
||||||
|
hash = "sha256-RSfLhNZ3JVvHoW/BNca9Hs4lpjcDtE1vsBDjJeaHqvc=";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
defaultPatterns = lib.mapAttrs (name: value: lib.mkDefault "${pkg}/share/crowdsec/config/patterns/${name}") (builtins.readDir "${pkg}/share/crowdsec/config/patterns");
|
||||||
|
|
||||||
|
patternsDir = pkgs.runCommandNoCC "crowdsec-patterns" {} ''
|
||||||
|
mkdir -p $out
|
||||||
|
${lib.concatStringsSep "\n" (lib.attrValues (lib.mapAttrs (
|
||||||
|
k: v: ''
|
||||||
|
ln -sf ${v} $out/${k}
|
||||||
|
''
|
||||||
|
)
|
||||||
|
cfg.patterns))}
|
||||||
|
'';
|
||||||
|
|
||||||
|
consoleSettings = {
|
||||||
|
share_manual_decisions = false;
|
||||||
|
share_custom = true;
|
||||||
|
share_tainted = true;
|
||||||
|
share_context = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultSettings = with lib; {
|
||||||
|
common = {
|
||||||
|
daemonize = mkForce false;
|
||||||
|
log_media = mkForce "stdout";
|
||||||
|
};
|
||||||
|
config_paths = {
|
||||||
|
config_dir = mkDefault "/var/lib/crowdsec/config";
|
||||||
|
data_dir = mkDefault dataDir;
|
||||||
|
hub_dir = mkDefault hubDir;
|
||||||
|
index_path = mkDefault "${hubDir}/.index.json";
|
||||||
|
simulation_path = mkDefault "${pkg}/share/crowdsec/config/simulation.yaml";
|
||||||
|
pattern_dir = mkDefault patternsDir;
|
||||||
|
};
|
||||||
|
db_config = {
|
||||||
|
type = mkDefault "sqlite";
|
||||||
|
db_path = mkDefault "${dataDir}/crowdsec.db";
|
||||||
|
use_wal = true;
|
||||||
|
};
|
||||||
|
crowdsec_service = {
|
||||||
|
enable = mkDefault true;
|
||||||
|
};
|
||||||
|
api = {
|
||||||
|
client = {
|
||||||
|
credentials_path = mkDefault "${stateDir}/local_api_credentials.yaml";
|
||||||
|
};
|
||||||
|
server = {
|
||||||
|
enable = mkDefault (cfg.enrollKeyFile != null);
|
||||||
|
listen_uri = mkDefault "127.0.0.1:8080";
|
||||||
|
|
||||||
|
console_path = mkDefault "${stateDir}/console.yaml";
|
||||||
|
profiles_path = mkDefault "${pkg}/share/crowdsec/config/profiles.yaml";
|
||||||
|
|
||||||
|
online_client.credentials_path = mkDefault "${stateDir}/online_api_credentials.yaml";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
user = "crowdsec";
|
||||||
|
group = "crowdsec";
|
||||||
|
stateDir = "/var/lib/crowdsec";
|
||||||
|
dataDir = "${stateDir}/data";
|
||||||
|
hubDir = "${stateDir}/hub";
|
||||||
|
in {
|
||||||
|
options.services.crowdsec = with lib; {
|
||||||
|
enable = mkEnableOption "CrowSec Security Engine";
|
||||||
|
package = mkPackageOption pkgs "crowdsec" {};
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = mdDoc ''
|
||||||
|
Name of the machine when registering it at the central or loal api.
|
||||||
|
'';
|
||||||
|
default = config.networking.hostName;
|
||||||
|
};
|
||||||
|
enrollKeyFile = mkOption {
|
||||||
|
description = mdDoc ''
|
||||||
|
The file containing the enrollment key used to enroll the engine at the central api console.
|
||||||
|
See <https://docs.crowdsec.net/docs/next/console/enrollment/#where-can-i-find-my-enrollment-key> for details.
|
||||||
|
'';
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
patterns = mkOption {
|
||||||
|
description = mdDoc ''
|
||||||
|
A set of pattern files for parsing logs, in the form "type" to file containing the corresponding GROK patterns.
|
||||||
|
All default patterns are automatically included.
|
||||||
|
See <https://github.com/crowdsecurity/crowdsec/tree/master/config/patterns>.
|
||||||
|
'';
|
||||||
|
type = types.attrsOf types.pathInStore;
|
||||||
|
default = {};
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{ ssh = ./patterns/ssh;}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
settings = mkOption {
|
||||||
|
description = mdDoc ''
|
||||||
|
Settings for MediaMTX. Refer to the defaults at
|
||||||
|
<https://github.com/bluenviron/mediamtx/blob/main/mediamtx.yml>.
|
||||||
|
'';
|
||||||
|
type = format.type;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = let
|
||||||
|
cscli = pkgs.writeScriptBin "cscli" ''
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
set -eu
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
exec ${pkg}/bin/cscli -c=${configFile} "''${@}"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
lib.mkIf (cfg.enable) {
|
||||||
|
services.crowdsec.settings = defaultSettings;
|
||||||
|
services.crowdsec.patterns = defaultPatterns;
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
systemPackages = [cscli];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.packages = [pkg];
|
||||||
|
systemd.timers.crowdsec-update-hub = {
|
||||||
|
description = "Update the crowdsec hub index";
|
||||||
|
wantedBy = ["timers.target"];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "daily";
|
||||||
|
Persistent = "yes";
|
||||||
|
Unit = "crowdsec-update-hub.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.services = let
|
||||||
|
sudo_doas =
|
||||||
|
if config.security.doas.enable == true
|
||||||
|
then "${pkgs.doas}/bin/doas"
|
||||||
|
else "${pkgs.sudo}/bin/sudo";
|
||||||
|
in {
|
||||||
|
crowdsec-update-hub = {
|
||||||
|
description = "Update the crowdsec hub index";
|
||||||
|
path = [cscli];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${sudo_doas} -u crowdsec ${cscli}/bin/cscli --error hub upgrade";
|
||||||
|
ExecStartPost = " systemctl restart crowdsec.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
crowdsec = {
|
||||||
|
description = "CrowdSec is a free, modern & collaborative behavior detection engine, coupled with a global IP reputation network.";
|
||||||
|
|
||||||
|
path = [cscli];
|
||||||
|
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
serviceConfig = with lib; {
|
||||||
|
User = "crowdsec";
|
||||||
|
Group = "crowdsec";
|
||||||
|
Restart = "on-failure";
|
||||||
|
|
||||||
|
LimitNOFILE = mkDefault 65536;
|
||||||
|
|
||||||
|
CapabilityBoundingSet = mkDefault [];
|
||||||
|
|
||||||
|
NoNewPrivileges = mkDefault true;
|
||||||
|
LockPersonality = mkDefault true;
|
||||||
|
RemoveIPC = mkDefault true;
|
||||||
|
|
||||||
|
ReadWritePaths = [stateDir];
|
||||||
|
ProtectSystem = mkDefault "strict";
|
||||||
|
|
||||||
|
PrivateUsers = mkDefault true;
|
||||||
|
ProtectHome = mkDefault true;
|
||||||
|
PrivateTmp = mkDefault true;
|
||||||
|
|
||||||
|
PrivateDevices = mkDefault true;
|
||||||
|
ProtectHostname = mkDefault true;
|
||||||
|
ProtectKernelTunables = mkDefault true;
|
||||||
|
ProtectKernelModules = mkDefault true;
|
||||||
|
ProtectControlGroups = mkDefault true;
|
||||||
|
|
||||||
|
ProtectProc = mkDefault "invisible";
|
||||||
|
ProcSubset = mkDefault "pid";
|
||||||
|
|
||||||
|
RestrictNamespaces = mkDefault true;
|
||||||
|
RestrictRealtime = mkDefault true;
|
||||||
|
RestrictSUIDSGID = mkDefault true;
|
||||||
|
|
||||||
|
SystemCallFilter = mkDefault ["@system-service" "@network-io"];
|
||||||
|
SystemCallArchitectures = ["native"];
|
||||||
|
SystemCallErrorNumber = mkDefault "EPERM";
|
||||||
|
|
||||||
|
ExecPaths = ["/nix/store"];
|
||||||
|
NoExecPaths = ["/"];
|
||||||
|
|
||||||
|
ExecStart = "${pkg}/bin/crowdsec -c ${configFile}";
|
||||||
|
ExecStartPre = let
|
||||||
|
script = pkgs.writeScriptBin "crowdsec-setup" ''
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
set -eu
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
if [ ! -s "${cfg.settings.api.client.credentials_path}" ]; then
|
||||||
|
cscli machine add "${cfg.name}" --auto
|
||||||
|
fi
|
||||||
|
|
||||||
|
${lib.optionalString cfg.settings.api.server.enable ''
|
||||||
|
if ! grep -q password "${cfg.settings.api.server.online_client.credentials_path}" ]; then
|
||||||
|
cscli capi register
|
||||||
|
fi
|
||||||
|
|
||||||
|
cscli hub update
|
||||||
|
|
||||||
|
${lib.optionalString (cfg.enrollKeyFile != null) ''
|
||||||
|
if [ ! -e "${cfg.settings.api.server.console_path}" ]; then
|
||||||
|
cscli console enroll "$(cat ${cfg.enrollKeyFile})" --name ${cfg.name}
|
||||||
|
fi
|
||||||
|
''}
|
||||||
|
''}
|
||||||
|
'';
|
||||||
|
in ["${script}/bin/crowdsec-setup"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d '${stateDir}' 0750 ${user} ${group} - -"
|
||||||
|
"d '${dataDir}' 0750 ${user} ${group} - -"
|
||||||
|
"d '${hubDir}' 0750 ${user} ${group} - -"
|
||||||
|
"f '${cfg.settings.api.server.online_client.credentials_path}' 0750 ${user} ${group} - -"
|
||||||
|
"f '${cfg.settings.config_paths.index_path}' 0750 ${user} ${group} - -"
|
||||||
|
];
|
||||||
|
users.users.${user} = lib.mapAttrs (name: lib.mkDefault) {
|
||||||
|
description = "Cowdsec service user";
|
||||||
|
isSystemUser = true;
|
||||||
|
inherit group;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups.${group} = lib.mapAttrs (name: lib.mkDefault) {};
|
||||||
|
};
|
||||||
|
}
|
||||||
28
packages/bouncer-firewall/default.nix
Normal file
28
packages/bouncer-firewall/default.nix
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
buildGoModule,
|
||||||
|
fetchFromGitHub,
|
||||||
|
}:
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "cs-firewall-bouncer";
|
||||||
|
version = "0.0.28";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "crowdsecurity";
|
||||||
|
repo = pname;
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-Y1pCupCtYkOD6vKpcmM8nPlsGbO0kYhc3PC9YjJHeMw=";
|
||||||
|
};
|
||||||
|
|
||||||
|
vendorHash = "sha256-BA7OHvqIRck3LVgtx7z8qhgueaJ6DOMU8clvWKUCdqE=";
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
homepage = "https://crowdsec.net/";
|
||||||
|
changelog = "https://github.com/crowdsecurity/${pname}/releases/tag/v${version}";
|
||||||
|
description = "Crowdsec bouncer for firewalls.";
|
||||||
|
longDescription = ''
|
||||||
|
crowdsec-firewall-bouncer will fetch new and old decisions from a CrowdSec API to add them in a blocklist used by supported firewalls.
|
||||||
|
'';
|
||||||
|
license = licenses.mit;
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user