Compare commits

..

2 commits

11 changed files with 145 additions and 3 deletions

View file

@ -6,7 +6,7 @@ in
virtualisation.oci-containers.containers = { virtualisation.oci-containers.containers = {
mealie = { mealie = {
autoStart = true; autoStart = true;
image = "ghcr.io/mealie-recipes/mealie:v1.8.0"; image = "ghcr.io/mealie-recipes/mealie:v1.9.0";
ports = [ ports = [
"${port}:9000" "${port}:9000"
]; ];
@ -21,6 +21,8 @@ in
MAX_WORKERS = "1"; MAX_WORKERS = "1";
WEB_CONCURRENCY = "1"; WEB_CONCURRENCY = "1";
BASE_URL = "https://mealie.kanto.dev"; BASE_URL = "https://mealie.kanto.dev";
SECURITY_MAX_LOGIN_ATTEMPTS = "3";
SECRURITY_USER_LOCKOUT_TIME = "72";
}; };
}; };
}; };
@ -32,7 +34,8 @@ in
"websecure" "websecure"
]; ];
middlewares = [ middlewares = [
"internal" "crowdsec"
"geoblock"
]; ];
service = "mealie"; service = "mealie";
}; };

View file

@ -0,0 +1,6 @@
listen_addr: 127.0.0.1:7422
appsec_config: crowdsecurity/appsec-default
name: traefik
source: appsec
labels:
type: appsec

View file

@ -0,0 +1,94 @@
{ config, inputs, pkgs, ... }:
let
port = "8080";
in
{
imports = [
inputs.crowdsec.nixosModules.crowdsec
inputs.crowdsec.nixosModules.crowdsec-firewall-bouncer
];
nixpkgs.overlays = [
inputs.crowdsec.overlays.default
];
age.secrets.enrollment-key = {
rekeyFile = ./enrollment_key.age;
owner = "crowdsec";
group = "crowdsec";
};
services.crowdsec = let
yaml = (pkgs.formats.yaml {}).generate;
acquisitions_file = yaml "acquisitions.yaml" {
source = "journalctl";
journalctl_filter = ["_SYSTEMD_UNIT=sshd.service"];
labels.type = "syslog";
};
in {
enable = true;
allowLocalJournalAccess = true;
enrollKeyFile = config.age.secrets.enrollment-key.path;
settings = {
api.server = {
listen_uri = "127.0.0.1:${port}";
};
crowdsec_service.acquisition_path = acquisitions_file;
crowdsec_service.acquisition_dir = ./acquis.d;
};
};
services.crowdsec-firewall-bouncer = {
enable = true;
settings = {
api_key = "2025f0be-35ca-406c-8737-810321c918c2";
api_url = "http://localhost:${port}";
};
};
systemd.services.crowdsec.serviceConfig = {
ExecStartPre = let
bouncer = pkgs.writeScriptBin "register-bouncer" ''
#!${pkgs.runtimeShell}
set -eu
set -o pipefail
if ! cscli bouncers list | grep -q "firewall-bouncer"; then
cscli bouncers add "firewall-bouncer" --key "2025f0be-35ca-406c-8737-810321c918c2"
fi
if ! cscli bouncers list | grep -q "traefik-bouncer"; then
cscli bouncers add "traefik-bouncer" --key "18c725d5-3a22-4331-a8e8-abfd3018a7c0"
fi
'';
scenario = pkgs.writeScriptBin "install-scenario" ''
#!${pkgs.runtimeShell}
set -eu
set -o pipefail
if ! cscli collections list | grep -q "crowdsecurity/linux"; then
cscli collections install "crowdsecurity/linux"
fi
if ! cscli collections list | grep -q "crowdsecurity/appsec-virtual-patching"; then
cscli collections install "crowdsecurity/appsec-virtual-patching"
fi
if ! cscli collections list | grep -q "crowdsecurity/appsec-generic-rules"; then
cscli collections install "crowdsecurity/appsec-generic-rules"
fi
'';
in [
"${bouncer}/bin/register-bouncer"
"${scenario}/bin/install-scenario"
];
};
environment.persistence."/persist" = {
directories = [
{ directory = "/var/lib/crowdsec"; user = "crowdsec"; group = "crowdsec"; }
];
hideMounts = true;
};
}

Binary file not shown.

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> piv-p256 hdSnGw A6O6zvEq05hpB3GxDsrj2rUxr0P031TKreOe3ZAfUpJs
Ww8Qg1MV5dJoCYQEGSNLUnZdX7dO1cGu3XaQTyn97PA
-> 0(D-grease b? xbW Qg ~cDE0j!
s5z0LGzRiWS6lMMphO19nB7qmvXkto4RJrcTSrOtPHbY9Iam2aeYA0qN4faK40Zs
XPc
--- q1PoY78SatX6wOKNW549+ndCCrNhveA8dHcHQpF+slk
l <0A>¾ß`òŠæ¨=(¡¾è;>Y[)Pfwú.§…óQ²¹¸W5áòØØL©Ã—K£DˆTœY$’µŸ
ý’Ù¿zñ¨Ã]

View file

@ -3,6 +3,7 @@
{ {
imports = [ imports = [
./traefik ./traefik
./crowdsec
./minecraft ./minecraft
./borgbackup.nix ./borgbackup.nix
./forgejo.nix ./forgejo.nix

View file

@ -25,6 +25,7 @@
"websecure" "websecure"
]; ];
middlewares = [ middlewares = [
"crowdsec"
"geoblock" "geoblock"
]; ];
service = "forgejo"; service = "forgejo";

View file

@ -14,6 +14,7 @@
"websecure" "websecure"
]; ];
middlewares = [ middlewares = [
"crowdsec"
"geoblock" "geoblock"
]; ];
service = "lighttpd"; service = "lighttpd";

View file

@ -53,10 +53,17 @@
# Install plugins # Install plugins
experimental.plugins = { experimental.plugins = {
# Block or allow requests based on their country of origin.
geoblock = { geoblock = {
moduleName = "github.com/PascalMinder/geoblock"; moduleName = "github.com/PascalMinder/geoblock";
version = "v0.2.7"; version = "v0.2.7";
}; };
# Authorize or block requests from IPs based on there reputation and behaviour.
bouncer = {
moduleName = "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin";
version = "v1.3.2";
};
}; };
# Network entry points into Traefik # Network entry points into Traefik

View file

@ -1,6 +1,13 @@
{ ... }: { config, ... }:
{ {
# Crowdsec Local API key for the bouncer.
age.secrets.traefik-bouncer-key = {
rekeyFile = ../crowdsec/traefik-bouncer-key.age;
owner = "traefik";
group = "traefik";
};
# Attached to the routers, pieces of middleware are a means of tweaking the requests before they are sent to your service # Attached to the routers, pieces of middleware are a means of tweaking the requests before they are sent to your service
services.traefik.dynamicConfigOptions.http.middlewares = { services.traefik.dynamicConfigOptions.http.middlewares = {
# Restrict access to admin devices only # Restrict access to admin devices only
@ -9,12 +16,14 @@
"192.168.1.101" # fuchsia "192.168.1.101" # fuchsia
"10.100.0.2" # Pixel 6 Pro "10.100.0.2" # Pixel 6 Pro
]; ];
# Restrict access to internal networks # Restrict access to internal networks
internal.ipwhitelist.sourcerange = [ internal.ipwhitelist.sourcerange = [
"127.0.0.1/32" # localhost "127.0.0.1/32" # localhost
"192.168.1.1/24" # lan "192.168.1.1/24" # lan
"10.100.0.0/24" # wireguard clients "10.100.0.0/24" # wireguard clients
]; ];
# Restrict access based on geo-location # Restrict access based on geo-location
geoblock.plugin.geoblock = { geoblock.plugin.geoblock = {
silentStartUp = "false"; silentStartUp = "false";
@ -42,6 +51,17 @@
# Even if an IP stays in the cache for a period of a month, it must be fetch again after a month. # Even if an IP stays in the cache for a period of a month, it must be fetch again after a month.
forceMonthlyUpdate = "true"; forceMonthlyUpdate = "true";
}; };
# Disable Crowdsec IP checking but apply Crowdsec Appsec checking. This mode is intended to be used when Crowdsec IP checking is applied at the Firewall Level.
crowdsec.plugin.bouncer = {
enabled = "true";
crowdsecMode = "appsec";
crowdsecLapiKeyFile = config.age.secrets.traefik-bouncer-key.path;
crowdsecLapiScheme = "http";
crowdsecLapiHost = "127.0.0.1:8080";
crowdsecAppsecEnabled = "true";
crowdsecAppsecHost = "127.0.0.1:7422";
};
}; };
} }