2 Commits

Author SHA1 Message Date
3d1e0991ae big revert 2025-10-04 20:49:00 +02:00
99b18de995 flake lock 2025-10-04 20:42:31 +02:00
10 changed files with 115 additions and 133 deletions

30
flake.lock generated
View File

@@ -212,11 +212,11 @@
"rust-analyzer-src": "rust-analyzer-src" "rust-analyzer-src": "rust-analyzer-src"
}, },
"locked": { "locked": {
"lastModified": 1759646430, "lastModified": 1759560021,
"narHash": "sha256-V8mjmGzi9nS7BZfhpzYAOUg3BcCsC6MrEh9xlKq3+7s=", "narHash": "sha256-J/rtMKVUAEqOFj0ogvcHKK8HbaKhw+tiNrDOpEM+ZDY=",
"owner": "nix-community", "owner": "nix-community",
"repo": "fenix", "repo": "fenix",
"rev": "b326bea4d58c9a58b346f17c710538eac00f71d1", "rev": "6ffcbf59c119b0c6384c7d98f18cea06a9af7e9c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -590,11 +590,11 @@
"rust-overlay": "rust-overlay_2" "rust-overlay": "rust-overlay_2"
}, },
"locked": { "locked": {
"lastModified": 1759605748, "lastModified": 1759201995,
"narHash": "sha256-qALSaIE4fbTo0wbPjEp7RZKbtFk1cDhRZ0BYOHW0JwQ=", "narHash": "sha256-3STv6fITv8Ar/kl0H7vIA7VV0d2gyLh8UL0BOiVacXg=",
"owner": "helix-editor", "owner": "helix-editor",
"repo": "helix", "repo": "helix",
"rev": "6fffaf6a7ded9a12fb2d5715a4eb83787a5e6402", "rev": "bfcbef10c513108c7b43317569416c2eefc4ed44",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -824,11 +824,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1759613406, "lastModified": 1759238633,
"narHash": "sha256-PzgQJydp+RlKvwDi807pXPlURdIAVqLppZDga3DwPqg=", "narHash": "sha256-4/AtRCQKXuU49ozZZouWuC+T7vCjQh9HAz3N8Tt5OZE=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "contrib", "repo": "contrib",
"rev": "32e1a75b65553daefb419f0906ce19e04815aa3a", "rev": "513d71d3f42c05d6a38e215382c5a6ce971bd77d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1278,11 +1278,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1759629535, "lastModified": 1759455985,
"narHash": "sha256-VIXcJ2ahRgoqIUySwAz3r5mtITO2dp6tXGCVKVW6FmA=", "narHash": "sha256-8qDv7NXH3fj1CDXed7c7vJLtrRKDZSo0x6TaWSfelVg=",
"owner": "fufexan", "owner": "fufexan",
"repo": "nix-gaming", "repo": "nix-gaming",
"rev": "df388c42b54714bd121796a9cec9322b7fa2894e", "rev": "eb5ab503cbd3cb386e8d85a55a9faed73ec7dc37",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1626,11 +1626,11 @@
"rust-analyzer-src": { "rust-analyzer-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1759601486, "lastModified": 1759301569,
"narHash": "sha256-ZywfLIFtRr907us1tONwUJLeg3ssO4D01XBFHx7RdAo=", "narHash": "sha256-7StxDed3v2fAWLkl+Hse9FlpjT7Dk7Cn/4vxTFyEhIg=",
"owner": "rust-lang", "owner": "rust-lang",
"repo": "rust-analyzer", "repo": "rust-analyzer",
"rev": "4ae99f0150c94f4bdf7192b4447f512ece3546fd", "rev": "472037b789cf593172d6adf3b8d9f7a429f6cd9b",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -216,7 +216,7 @@
flags = "--performance"; flags = "--performance";
}; };
tailscale = { tailscale = {
enable = false; enable = true;
}; };
udisks = { udisks = {
enable = true; enable = true;

View File

@@ -214,7 +214,7 @@
flags = "--performance"; flags = "--performance";
}; };
tailscale = { tailscale = {
enable = false; enable = true;
}; };
udisks = { udisks = {
enable = true; enable = true;

View File

@@ -1,5 +1,4 @@
{ config, ... }: {config, ...}: {
{
server = { server = {
enable = true; enable = true;
email = "adam@cnst.dev"; email = "adam@cnst.dev";
@@ -44,10 +43,6 @@
}; };
jellyfin = { jellyfin = {
enable = true; enable = true;
cloudflared = {
tunnelId = "234811e2-bc86-44b2-9abd-493686e25704";
credentialsFile = config.age.secrets.jellyfinCloudflared.path;
};
}; };
uptime-kuma = { uptime-kuma = {
enable = true; enable = true;
@@ -94,7 +89,7 @@
gluetun.enable = true; gluetun.enable = true;
qbittorrent = { qbittorrent = {
enable = true; enable = true;
port = 8387; port = 8080;
}; };
slskd = { slskd = {
enable = true; enable = true;

View File

@@ -4,13 +4,11 @@
pkgs, pkgs,
self, self,
... ...
}: }: let
let
unit = "authentik"; unit = "authentik";
cfg = config.server.${unit}; cfg = config.server.${unit};
srv = config.server; srv = config.server;
in in {
{
options.server.${unit} = { options.server.${unit} = {
enable = lib.mkEnableOption { enable = lib.mkEnableOption {
description = "Enable ${unit}"; description = "Enable ${unit}";
@@ -55,9 +53,11 @@ in
age.secrets = { age.secrets = {
authentikEnv = { authentikEnv = {
file = "${self}/secrets/authentikEnv.age"; file = "${self}/secrets/authentikEnv.age";
owner = "authentik";
}; };
authentikCloudflared = { authentikCloudflared = {
file = "${self}/secrets/authentikCloudflared.age"; file = "${self}/secrets/authentikCloudflared.age";
owner = "authentik";
}; };
}; };
@@ -65,8 +65,8 @@ in
fail2ban = lib.mkIf cfg.enable { fail2ban = lib.mkIf cfg.enable {
jails = { jails = {
authentik = { authentik = {
serviceName = "authentik"; serviceName = "${cfg.url}";
failRegex = "^.*Username or password is incorrect.*IP:\s*<HOST>"; failRegex = "^.*Username or password is incorrect. Try again. IP: <HOST>. Username: <F-USER>.*</F-USER>.$";
}; };
}; };
}; };
@@ -99,23 +99,22 @@ in
middlewares = { middlewares = {
authentik = { authentik = {
forwardAuth = { forwardAuth = {
# tls.insecureSkipVerify = true; tls.insecureSkipVerify = true;
address = "https://localhost:9443/outpost.goauthentik.io/auth/traefik"; address = "https://localhost:9443/outpost.goauthentik.io/auth/traefik";
trustForwardHeader = true; trustForwardHeader = true;
authResponseHeaders = [ authResponseHeaders = [
"X-authentik-username" "X-authentik-username"
"X-authentik-groups" "X-authentik-groups"
"X-authentik-email" "X-authentik-email"
# "X-authentik-name" "X-authentik-name"
# "X-authentik-uid" "X-authentik-uid"
"X-authentik-jwt" "X-authentik-jwt"
# "X-authentik-meta-jwks" "X-authentik-meta-jwks"
# "X-authentik-meta-outpost" "X-authentik-meta-outpost"
# "X-authentik-meta-provider" "X-authentik-meta-provider"
# "X-authentik-meta-app" "X-authentik-meta-app"
# "X-authentik-meta-version" "X-authentik-meta-version"
]; ];
timeout = "10s";
}; };
}; };
}; };
@@ -130,7 +129,7 @@ in
routers = { routers = {
auth = { auth = {
entryPoints = [ "websecure" ]; entryPoints = ["websecure"];
rule = "Host(`${cfg.url}`) || HostRegexp(`{subdomain:[a-z0-9]+}.${srv.www.url}`) && PathPrefix(`/outpost.goauthentik.io/`)"; rule = "Host(`${cfg.url}`) || HostRegexp(`{subdomain:[a-z0-9]+}.${srv.www.url}`) && PathPrefix(`/outpost.goauthentik.io/`)";
service = "auth"; service = "auth";
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";

View File

@@ -4,9 +4,11 @@
config, config,
pkgs, pkgs,
... ...
}: let }:
let
cfg = config.server.fail2ban; cfg = config.server.fail2ban;
in { in
{
options.server.fail2ban = { options.server.fail2ban = {
enable = lib.mkEnableOption { enable = lib.mkEnableOption {
description = "Enable cloudflare fail2ban"; description = "Enable cloudflare fail2ban";
@@ -15,7 +17,7 @@ in {
description = "File containing your API key, scoped to Firewall Rules: Edit"; description = "File containing your API key, scoped to Firewall Rules: Edit";
type = lib.types.str; type = lib.types.str;
example = lib.literalExpression '' example = lib.literalExpression ''
Authorization: Bearer vH6-p0y=i4w3n7TjKqZ@x8D_lR!A9b2cOezXgUuJdE5F Authorization: Bearer Qj06My1wXJEzcW46QCyjFbSMgVtwIGfX63Ki3NOj79o=
''' '''
''; '';
}; };
@@ -55,54 +57,54 @@ in {
pkgs.jq pkgs.jq
]; ];
jails = jails = lib.attrsets.mapAttrs (name: value: {
lib.attrsets.mapAttrs (name: value: { settings = {
settings = { bantime = "30d";
bantime = "24h"; findtime = "1h";
findtime = "10m"; enabled = true;
enabled = true; backend = "systemd";
backend = "systemd"; journalmatch = "_SYSTEMD_UNIT=${value.serviceName}.service";
journalmatch = "_SYSTEMD_UNIT=${value.serviceName}.service"; port = "http,https";
port = "http,https"; filter = "${name}";
filter = "${name}"; maxretry = 3;
maxretry = 3; action = "cloudflare-token-agenix";
action = "cloudflare-token-agenix"; };
}; }) cfg.jails;
})
cfg.jails;
}; };
environment.etc = lib.attrsets.mergeAttrsList [ environment.etc = lib.attrsets.mergeAttrsList [
(lib.attrsets.mapAttrs' ( (lib.attrsets.mapAttrs' (
name: value: (lib.nameValuePair "fail2ban/filter.d/${name}.conf" { name: value:
text = '' (lib.nameValuePair "fail2ban/filter.d/${name}.conf" {
[Definition] text = ''
failregex = ${value.failRegex} [Definition]
ignoreregex = ${value.ignoreRegex} failregex = ${value.failRegex}
''; ignoreregex = ${value.ignoreRegex}
}) '';
) })
cfg.jails) ) cfg.jails)
{ {
"fail2ban/action.d/cloudflare-token-agenix.conf".text = let "fail2ban/action.d/cloudflare-token-agenix.conf".text =
notes = "Fail2Ban on ${config.networking.hostName}"; let
cfapi = "https://api.cloudflare.com/client/v4/zones/${cfg.zoneId}/firewall/access_rules/rules"; notes = "Fail2Ban on ${config.networking.hostName}";
in '' cfapi = "https://api.cloudflare.com/client/v4/zones/${cfg.zoneId}/firewall/access_rules/rules";
[Definition] in
actionstart = ''
actionstop = [Definition]
actioncheck = actionstart =
actionunban = id=$(curl -s -X GET "${cfapi}" \ actionstop =
-H @${cfg.apiKeyFile} -H "Content-Type: application/json" \ actioncheck =
| jq -r '.result[] | select(.notes == "${notes}" and .configuration.target == "ip" and .configuration.value == "<ip>") | .id') actionunban = id=$(curl -s -X GET "${cfapi}" \
if [ -z "$id" ]; then echo "id for <ip> cannot be found"; exit 0; fi; \ -H @${cfg.apiKeyFile} -H "Content-Type: application/json" \
curl -s -X DELETE "${cfapi}/$id" \ | jq -r '.result[] | select(.notes == "${notes}" and .configuration.target == "ip" and .configuration.value == "<ip>") | .id')
-H @${cfg.apiKeyFile} -H "Content-Type: application/json" \ if [ -z "$id" ]; then echo "id for <ip> cannot be found"; exit 0; fi; \
--data '{"cascade": "none"}' curl -s -X DELETE "${cfapi}/$id" \
actionban = curl -X POST "${cfapi}" -H @${cfg.apiKeyFile} -H "Content-Type: application/json" --data '{"mode":"block","configuration":{"target":"ip","value":"<ip>"},"notes":"${notes}"}' -H @${cfg.apiKeyFile} -H "Content-Type: application/json" \
[Init] --data '{"cascade": "none"}'
name = cloudflare-token-agenix actionban = curl -X POST "${cfapi}" -H @${cfg.apiKeyFile} -H "Content-Type: application/json" --data '{"mode":"block","configuration":{"target":"ip","value":"<ip>"},"notes":"${notes}"}'
''; [Init]
name = cloudflare-token-agenix
'';
} }
]; ];
}; };

View File

@@ -4,18 +4,11 @@
pkgs, pkgs,
self, self,
... ...
}: }: let
let inherit (lib) mkOption mkEnableOption mkIf types;
inherit (lib)
mkOption
mkEnableOption
mkIf
types
;
cfg = config.server.www; cfg = config.server.www;
srv = config.server; srv = config.server;
in in {
{
options.server.www = { options.server.www = {
enable = mkEnableOption { enable = mkEnableOption {
description = "Enable personal website"; description = "Enable personal website";
@@ -51,11 +44,9 @@ in
server = { server = {
fail2ban = lib.mkIf config.server.www.enable { fail2ban = lib.mkIf config.server.www.enable {
jails = { jails = {
nginx-404 = { www = {
serviceName = "nginx"; serviceName = "cnst.dev";
failRegex = ''^.*\[error\].*directory index of.* is forbidden.*client: <HOST>.*$''; failRegex = "^.*Username or password is incorrect. Try again. IP: <HOST>. Username: <F-USER>.*</F-USER>.$";
ignoreRegex = "";
maxRetry = 5;
}; };
}; };
}; };
@@ -73,23 +64,14 @@ in
virtualHosts."webfinger" = { virtualHosts."webfinger" = {
forceSSL = false; forceSSL = false;
serverName = cfg.url; serverName = cfg.url;
root = "/var/www/webfinger"; root = "/etc/webfinger";
locations."= /.well-known/webfinger" = { locations."= /.well-known/webfinger" = {
root = "/var/www/webfinger"; root = "/etc/webfinger";
extraConfig = '' extraConfig = ''
default_type application/jrd+json; default_type application/jrd+json;
try_files /.well-known/webfinger =404; try_files /.well-known/webfinger =404;
''; '';
}; };
locations."= /robots.txt" = {
root = "/var/www/webfinger";
extraConfig = ''
default_type text/plain;
try_files /robots.txt =404;
'';
};
}; };
}; };
@@ -103,35 +85,28 @@ in
}; };
}; };
environment.etc = { environment.etc."webfinger/.well-known/webfinger".text = ''
"webfinger/.well-known/webfinger".text = '' {
{ "subject": "acct:adam@${cfg.url}",
"subject": "acct:adam@${cfg.url}", "links": [
"links": [ {
{ "rel": "http://openid.net/specs/connect/1.0/issuer",
"rel": "http://openid.net/specs/connect/1.0/issuer", "href": "https://auth.${cfg.url}/application/o/tailscale/"
"href": "https://auth.${cfg.url}/application/o/tailscale/" }
} ]
] }
} '';
'';
"webfinger/robots.txt".text = ''
User-agent: *
Disallow: /
'';
};
services.traefik.dynamicConfigOptions.http = { services.traefik.dynamicConfigOptions.http = {
routers.webfinger = { routers.webfinger = {
entryPoints = [ "websecure" ]; entryPoints = ["websecure"];
rule = "Host(`${cfg.url}`) && Path(`/.well-known/webfinger`)"; rule = "Host(`${cfg.url}`) && Path(`/.well-known/webfinger`)";
service = "webfinger"; service = "webfinger";
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
services.webfinger.loadBalancer.servers = [ services.webfinger.loadBalancer.servers = [
{ url = "http://127.0.0.1:8283"; } {url = "http://127.0.0.1:8283";}
]; ];
}; };
}; };

11
secrets/nginxEnv.age Normal file
View File

@@ -0,0 +1,11 @@
age-encryption.org/v1
-> ssh-ed25519 t9iOEg pfPhWigjvnJ5tfVv8qPpk3VYvLH9I01HVVbpu+r2NjY
Kaj8aZv+9pSYjwoE7EHWGHfsZIPFZOgUVaKf8VxWKcQ
-> ssh-ed25519 KUYMFA 9Xy82Cl3HUQcFDcJMxxnnIfLOngW8xLfVE0S1wRliGg
mOOcyJp5+ZqFwdkZkHC63+cMA0ToGcuI6kqMjAJ9jJk
-> ssh-ed25519 76RhUQ +OvUSQwpy6+xxlom8bJFn8CBdSKECa9YY0U+YYNYdGM
MWfmfGzd6/lOPvggUG8uJgBAp1CTqSdk+NDkk7vSQEQ
-> ssh-ed25519 Jf8sqw jQR/wT/+f63cJdFzR/Ogw6pdiYXoyVNu1+UCni2BYSM
Iicwg/XJJskvWFmAbxFDh3gSJyjid5fw9JXmDJPhzkU
--- xK8vBWioTgSDPHkKh7SJxstCzYtUSmTz6QuN/+niFME
<08><>f<<3C>`VR<56><52>p<><70>)>|<7C>+aئI<D8A6>g<08> <0B><><EFBFBD><EFBFBD><EFBFBD><19><>x<EFBFBD><78>HH+<2B><EFBFBD><E7B7AD>o>$4H<><48><EFBFBD><EFBFBD>B?<3F>l6TSqμ<71>Ǿ<EFBFBD><C7BE>Kj-l

Binary file not shown.

View File

@@ -11,7 +11,7 @@
enable = true; enable = true;
}; };
chromium = { chromium = {
enable = false; enable = true;
}; };
discord = { discord = {
enable = true; enable = true;