Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions nixos/modules/services/security/crowdsec-firewall-bouncer.nix
Original file line number Diff line number Diff line change
Expand Up @@ -230,25 +230,41 @@ in
wantedBy = [ "multi-user.target" ];
after = [ "crowdsec.service" ];
wants = after;
path = [ config.services.crowdsec.configuredCscli ];
script = ''
cscli=${lib.getExe config.services.crowdsec.configuredCscli}
if $cscli bouncers list --output json | ${lib.getExe pkgs.jq} -e -- ${lib.escapeShellArg "any(.[]; .name == \"${cfg.registerBouncer.bouncerName}\")"} >/dev/null; then
# Bouncer already registered. Verify the API key is still present
# Ensure the directory exists
mkdir -p "$(dirname ${apiKeyFile})" || true

echo "Checking bouncer registration..."
if cscli bouncers list --output json | ${lib.getExe pkgs.jq} -e -- ${lib.escapeShellArg "any(.[]; .name == \"${cfg.registerBouncer.bouncerName}\")"} >/dev/null; then

echo "Bouncer already registered. Verify the API key is still present"
if [ ! -f ${apiKeyFile} ]; then
echo "Bouncer registered but API key is not present"
exit 1
echo "Unregistering bouncer..."
cscli bouncers delete ${cfg.registerBouncer.bouncerName} || true
else
echo "API key file exists, nothing to do"
exit 0
fi
else
# Bouncer not registered
# Remove any previously saved API key
echo "Bouncer not registered"
echo "Remove any previously saved API key"
rm -f '${apiKeyFile}'
# Register the bouncer and save the new API key
if ! $cscli bouncers add --output raw -- ${lib.escapeShellArg cfg.registerBouncer.bouncerName} >${apiKeyFile}; then
# Failed to register the bouncer
rm ${apiKeyFile}
fi

echo "Register the bouncer and save the new API key"
if ! cscli bouncers add --output raw -- ${lib.escapeShellArg cfg.registerBouncer.bouncerName} > ${apiKeyFile} 2>&1; then
echo "Failed to register the bouncer"
cat ${apiKeyFile} || true # Show error message
rm -f ${apiKeyFile}
exit 1
fi
fi

chmod 0440 ${apiKeyFile} || true
echo "Successfully registered bouncer and saved API key"

cscli bouncers list
'';
serviceConfig = {
Type = "oneshot";
Expand Down Expand Up @@ -384,6 +400,8 @@ in
"~@resources"
];
UMask = "0077";

Restart = "always";
};
};
};
Expand Down
119 changes: 96 additions & 23 deletions nixos/modules/services/security/crowdsec.nix
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,12 @@ in
online_client.credentials_path = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Path to a file containing credentials for the Central API.";
example = "\${config_paths.data_dir}/online_api_credentials.yaml";
description = ''
Path to a file containing credentials for the Central API.
To automatically register with `crowdsec-setup`, set this option (typically to ''${config_paths.data_dir}/online_api_credentials.yaml).
The file will be automatically created, unless it already exists.
'';
};
};
};
Expand All @@ -343,6 +348,18 @@ in
};

prometheus = {
enabled = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable or disable the CrowdSec prometheus exporter.";
};

listen_addr = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "Prometheus listen address.";
};

listen_port = lib.mkOption {
type = lib.types.port;
default = 6060;
Expand Down Expand Up @@ -627,14 +644,22 @@ in
let
installDir = d: ''install -d -o ${cfg.user} -g ${cfg.group} -m 750 "${d}"'';

setupConfigDirs = lib.concatMapStringsSep "\n" installDir [
cleanConfigDirs = ''
if [ -d ${config_paths.config_dir}/patterns ]; then
rm -rf ${config_paths.config_dir}/patterns
fi
'';

createConfigDirs = lib.concatMapStringsSep "\n" installDir [
config_paths.config_dir
cfg.settings.config.crowdsec_service.acquisition_dir
config_paths.notification_dir
"${config_paths.config_dir}/appsec-configs"
"${config_paths.config_dir}/appsec-rules"
"${config_paths.config_dir}/collections"
"${config_paths.config_dir}/console"
"${config_paths.config_dir}/contexts"
"${config_paths.config_dir}/hub"
"${config_paths.config_dir}/parsers"
"${config_paths.config_dir}/parsers/s00-raw"
"${config_paths.config_dir}/parsers/s01-parse"
Expand All @@ -645,6 +670,11 @@ in
"${config_paths.config_dir}/scenarios"
];

setupConfigDirs = ''
${cleanConfigDirs}
${createConfigDirs}
'';

setupScript = pkgs.writeShellApplication {
name = "crowdsec-setup";
runtimeInputs = [ configuredCscli ];
Expand All @@ -661,21 +691,53 @@ in
install -o ${cfg.user} -g ${cfg.group} -m 0750 -D ${cfg.package}/libexec/crowdsec/plugins/${name} ${cfg.settings.config.config_paths.data_dir}/plugins/${name}
'';

maybeTouchCredPath =
maybeTouchFile =
p:
lib.optionalString (p != null) ''
if [ ! -s ${p} ]; then
touch "${p}"
fi
'';

maybeInstallConfigFile =
p: o:
lib.optionalString (p != null) ''
if [ ! -f ${config_paths.config_dir}/${o} ]; then
cp ${cfg.package}/share/crowdsec/config/${p} ${config_paths.config_dir}/${o}
fi
'';

maybeInstallDataFile =
p: o:
lib.optionalString (p != null) ''
if [ ! -f ${cfg.settings.config.config_paths.data_dir}/${o} ]; then
cp ${cfg.package}/share/crowdsec/config/${p} ${cfg.settings.config.config_paths.data_dir}/${o}
fi
'';

overwriteInstallConfigDir =
p:
lib.optionalString (p != null) ''
cp -a ${cfg.package}/share/crowdsec/config/${p} ${config_paths.config_dir}
'';
in
''
${maybeTouchCredPath cfg.settings.config.api.client.credentials_path}
${maybeTouchCredPath cfg.settings.config.api.server.online_client.credentials_path}
${maybeTouchFile cfg.settings.config.api.client.credentials_path}
${maybeTouchFile cfg.settings.config.api.server.online_client.credentials_path}

${installDir cfg.settings.config.config_paths.hub_dir}
${installDir cfg.settings.config.config_paths.plugin_dir}

# needed by `cscli setup`
${installDir "${cfg.settings.config.config_paths.hub_dir}/.cache"}
${installDir "${cfg.settings.config.config_paths.data_dir}/data"}
${maybeInstallDataFile "detect.yaml" "data/detect.yaml"}

${maybeInstallConfigFile "simulation.yaml" "simulation.yaml"}
${maybeInstallConfigFile "context.yaml" "console/context.yaml"}
${maybeInstallConfigFile "console.yaml" "console.yaml"}
${overwriteInstallConfigDir "patterns"}

echo "Updating hub..."

cscli hub update
Expand Down Expand Up @@ -781,24 +843,35 @@ in
environment = {
systemPackages =
let
cscliWrapper = pkgs.writeShellScriptBin "cscli" ''
exec systemd-run \
--quiet \
--pty \
--wait \
--collect \
--pipe \
--property=ExecPaths="${cfg.settings.config.config_paths.plugin_dir}" \
--property=User=${cfg.user} \
--property=Group=${cfg.group} \
--property=DynamicUser=true \
--property=StateDirectory="crowdsec crowdsec/hub" \
--property=StateDirectoryMode="0750" \
--property=ConfigurationDirectory="crowdsec crowdsec/acquis.d" \
--property=ConfigurationDirectoryMode="0750" \
-- \
${lib.getExe configuredCscli} "$@"
'';
cscliWrapper = pkgs.symlinkJoin {
name = "cscli";
paths = [
(pkgs.writeShellScriptBin "cscli" ''
exec systemd-run \
--quiet \
--pty \
--wait \
--collect \
--pipe \
--property=ExecPaths="${cfg.settings.config.config_paths.plugin_dir}" \
--property=User=${cfg.user} \
--property=Group=${cfg.group} \
--property=DynamicUser=true \
--property=StateDirectory="crowdsec crowdsec/hub" \
--property=StateDirectoryMode="0750" \
--property=ConfigurationDirectory="crowdsec crowdsec/acquis.d" \
--property=ConfigurationDirectoryMode="0750" \
-- \
${lib.getExe configuredCscli} "$@"
'')
(pkgs.runCommand "cscli-completions" { } ''
mkdir -p $out/share
ln -s ${cfg.package}/share/bash-completion $out/share/bash-completion
ln -s ${cfg.package}/share/zsh $out/share/zsh
ln -s ${cfg.package}/share/fish $out/share/fish
'')
Comment on lines +867 to +872
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I understanding this correctly that this also fixes the cscli autocompletion for the different shells? 👀

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that is the purpose of this subset of the change. As already mentioned, it's not perfect because systemd will ask for password on each tab press unless in a sudo su shell. Also nearly all entries (not all though, interestingly) in the first level (subcommands) show up duplicated, on zsh (which is customized to pipe suggestions in fzf). Bash is fine. Fish, I haven't tested as I don't use it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... have you tried to add your user to the crowdsec group? Maybe this helps a bit since the service is running as that user.

];
};
in
[ cscliWrapper ];

Expand Down
Loading