Merge pull request #1 from ediblerope/claude/create-claude-md-Je6Ea
Claude/create claude md je6 ea
This commit is contained in:
commit
fbf817fff0
5 changed files with 183 additions and 46 deletions
30
CLAUDE.md
Normal file
30
CLAUDE.md
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# FredOS NixOS Configuration
|
||||||
|
|
||||||
|
This is a NixOS flake-based configuration for multiple hosts:
|
||||||
|
- **FredOS-Gaming** — gaming desktop
|
||||||
|
- **FredOS-Mediaserver** — home media server
|
||||||
|
- **FredOS-Macbook** — MacBook laptop
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
- `flake.nix` — flake inputs/outputs; all hosts use `nixpkgs` unstable
|
||||||
|
- `common.nix` — shared configuration across all hosts
|
||||||
|
- `hosts/` — per-host NixOS configuration modules
|
||||||
|
- `hosts/hardware/` — hardware-specific configuration
|
||||||
|
- `home-manager/` — Home Manager configuration (via NixOS module)
|
||||||
|
- `services/` — modular service definitions imported by hosts
|
||||||
|
- `settings/` — shared settings/variables
|
||||||
|
|
||||||
|
## Code Evaluation
|
||||||
|
|
||||||
|
Always validate Nix expressions with `nix eval` before committing. For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Evaluate a specific attribute to check for syntax/type errors
|
||||||
|
nix eval .#nixosConfigurations.FredOS-Gaming.config.system.stateVersion
|
||||||
|
|
||||||
|
# Evaluate the full flake outputs to catch top-level errors
|
||||||
|
nix eval .#nixosConfigurations --apply builtins.attrNames
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `nix flake check` for a broader check of the flake.
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
./services/jellyfin.nix
|
./services/jellyfin.nix
|
||||||
./services/bazarr.nix
|
./services/bazarr.nix
|
||||||
./services/cloudflare-ddns.nix
|
./services/cloudflare-ddns.nix
|
||||||
./services/crowdsec.nix
|
./services/fail2ban.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
### Make build time quicker
|
### Make build time quicker
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@
|
||||||
yt-dlp
|
yt-dlp
|
||||||
];
|
];
|
||||||
|
|
||||||
services.fail2ban.enable = true;
|
|
||||||
|
|
||||||
# Enable Docker
|
# Enable Docker
|
||||||
virtualisation.docker.enable = true;
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
{
|
|
||||||
config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") {
|
|
||||||
services.crowdsec = {
|
|
||||||
enable = true;
|
|
||||||
autoUpdateService = true;
|
|
||||||
|
|
||||||
# Install detection collections on first boot
|
|
||||||
hub.collections = [ "crowdsecurity/linux" "crowdsecurity/sshd" ];
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
# Enable the Local API server (required for bouncer registration)
|
|
||||||
general.api.server.enable = true;
|
|
||||||
# Where the LAPI client credentials will be written on first boot
|
|
||||||
lapi.credentialsFile = "/var/lib/crowdsec/state/lapi-credentials.yaml";
|
|
||||||
};
|
|
||||||
|
|
||||||
localConfig.acquisitions = [
|
|
||||||
# SSH brute-force detection
|
|
||||||
{
|
|
||||||
source = "journalctl";
|
|
||||||
journalctl_filter = [ "-u" "sshd" ];
|
|
||||||
labels.type = "syslog";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# The bouncer-register service uses raw cscli (no -c flag), so it looks for
|
|
||||||
# config at /etc/crowdsec/config.yaml. Symlink the Nix-generated config there.
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"L+ /etc/crowdsec/config.yaml - - - - ${(pkgs.formats.yaml { }).generate "crowdsec.yaml" config.services.crowdsec.settings.general}"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Ensure /var/lib/crowdsec exists before crowdsec starts (race with tmpfiles-resetup)
|
|
||||||
systemd.services.crowdsec.after = [ "systemd-tmpfiles-resetup.service" ];
|
|
||||||
|
|
||||||
# Firewall bouncer — auto-registers to local CrowdSec LAPI
|
|
||||||
services.crowdsec-firewall-bouncer = {
|
|
||||||
enable = true;
|
|
||||||
settings.api_url = "http://127.0.0.1:8080";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
152
services/fail2ban.nix
Normal file
152
services/fail2ban.nix
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
{
|
||||||
|
config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") {
|
||||||
|
|
||||||
|
services.fail2ban = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Default ban settings (overridable per jail)
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "1h";
|
||||||
|
|
||||||
|
# Progressively longer bans for repeat offenders, up to 1 week
|
||||||
|
bantime-increment = {
|
||||||
|
enable = true;
|
||||||
|
multiplier = "1 2 4 8 16 32 64";
|
||||||
|
maxtime = "168h";
|
||||||
|
overalljails = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Never ban local network traffic
|
||||||
|
ignoreIP = [
|
||||||
|
"127.0.0.1/8"
|
||||||
|
"::1"
|
||||||
|
"192.168.0.0/16"
|
||||||
|
"10.0.0.0/8"
|
||||||
|
];
|
||||||
|
|
||||||
|
jails = {
|
||||||
|
|
||||||
|
# SSH brute force — built-in sshd filter via journald
|
||||||
|
sshd = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
filter = "sshd";
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Nginx Proxy Manager — watches Docker-mounted log files for 401/403s
|
||||||
|
nginx-proxy-manager = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
filter = "nginx-http-auth";
|
||||||
|
logpath = "/home/fred/docker/nginx-proxy-manager/data/logs/*.log";
|
||||||
|
maxretry = 10;
|
||||||
|
bantime = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Jellyfin auth failures — journald
|
||||||
|
jellyfin = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
backend = "systemd";
|
||||||
|
journalmatch = "_SYSTEMD_UNIT=jellyfin.service";
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "2h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Sonarr — log files at dataDir/logs/
|
||||||
|
sonarr = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
filter = "arr-apps";
|
||||||
|
logpath = "/var/lib/sonarr/logs/*.txt";
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Radarr — log files at dataDir/logs/
|
||||||
|
radarr = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
filter = "arr-apps";
|
||||||
|
logpath = "/var/lib/radarr/logs/*.txt";
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Prowlarr — log files at dataDir/logs/
|
||||||
|
prowlarr = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
filter = "arr-apps";
|
||||||
|
logpath = "/var/lib/prowlarr/logs/*.txt";
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Bazarr — log files at dataDir/log/
|
||||||
|
bazarr = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
filter = "bazarr";
|
||||||
|
logpath = "/var/lib/bazarr/log/*.txt";
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# qBittorrent-nox — watches journald for web UI login failures
|
||||||
|
qbittorrent = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
filter = "qbittorrent";
|
||||||
|
backend = "systemd";
|
||||||
|
journalmatch = "_SYSTEMD_UNIT=qbittorrent-nox.service";
|
||||||
|
maxretry = 5;
|
||||||
|
bantime = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Shared filter for Sonarr, Radarr, Prowlarr — they all use the same *arr codebase
|
||||||
|
environment.etc."fail2ban/filter.d/arr-apps.conf".text = ''
|
||||||
|
[Definition]
|
||||||
|
failregex = .*Auth-Failure ip <HOST>
|
||||||
|
ignoreregex =
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Bazarr (Python/Flask) auth failure filter
|
||||||
|
environment.etc."fail2ban/filter.d/bazarr.conf".text = ''
|
||||||
|
[Definition]
|
||||||
|
failregex = .*login attempt.*<HOST>
|
||||||
|
.*unauthorized.*<HOST>
|
||||||
|
ignoreregex =
|
||||||
|
'';
|
||||||
|
|
||||||
|
# qBittorrent web UI login failure filter
|
||||||
|
environment.etc."fail2ban/filter.d/qbittorrent.conf".text = ''
|
||||||
|
[Definition]
|
||||||
|
failregex = .*WebAPI login failure.*remote IP: <HOST>
|
||||||
|
ignoreregex =
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Jellyfin filter
|
||||||
|
environment.etc."fail2ban/filter.d/jellyfin.conf".text = ''
|
||||||
|
[Definition]
|
||||||
|
failregex = ^.*Authentication request for .* has been denied \(IP: "<HOST>"\).*$
|
||||||
|
^.*Error processing request from remote IP Address <HOST>.*$
|
||||||
|
ignoreregex =
|
||||||
|
'';
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue