Compare commits

...

2 commits

Author SHA1 Message Date
Forgejo Bot
2f72b0894a feat: automated changes 2025-07-17 06:01:16 +00:00
032072374b moved mail
Some checks failed
Test / tests (push) Has been cancelled
2025-07-17 00:35:44 +02:00
35 changed files with 514 additions and 327 deletions

127
flake.lock generated
View file

@ -1,5 +1,21 @@
{ {
"nodes": { "nodes": {
"blobs": {
"flake": false,
"locked": {
"lastModified": 1604995301,
"narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=",
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"type": "gitlab"
}
},
"colmena": { "colmena": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
@ -38,6 +54,22 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1747046372,
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": { "flake-utils": {
"locked": { "locked": {
"lastModified": 1659877975, "lastModified": 1659877975,
@ -53,6 +85,54 @@
"type": "github" "type": "github"
} }
}, },
"git-hooks": {
"inputs": {
"flake-compat": [
"simple-nixos-mailserver",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"simple-nixos-mailserver",
"nixpkgs"
]
},
"locked": {
"lastModified": 1750779888,
"narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"simple-nixos-mailserver",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nix-github-actions": { "nix-github-actions": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -90,13 +170,29 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-25_05": {
"locked": {
"lastModified": 1751741127,
"narHash": "sha256-t75Shs76NgxjZSgvvZZ9qOmz5zuBE8buUaYD28BMTxg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "29e290002bfff26af1db6f64d070698019460302",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1752624097, "lastModified": 1752731184,
"narHash": "sha256-mQCof2VccFzF7cmXy43n3GCwSN2+m8TVhZpGLx9sxVc=", "narHash": "sha256-gxXbkDyWNqXH0M2HLmNhP51yY7PSZkXoshXSCh+DVIk=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d7c8095791ce3aafe97d9c16c1dc2f4e3d69a3ba", "rev": "558243f9250d98ab34959cff321fa3bfd58ca6dd",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -109,9 +205,34 @@
"inputs": { "inputs": {
"colmena": "colmena", "colmena": "colmena",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_2",
"simple-nixos-mailserver": "simple-nixos-mailserver",
"sops-nix": "sops-nix" "sops-nix": "sops-nix"
} }
}, },
"simple-nixos-mailserver": {
"inputs": {
"blobs": "blobs",
"flake-compat": "flake-compat_2",
"git-hooks": "git-hooks",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-25_05": "nixpkgs-25_05"
},
"locked": {
"lastModified": 1752060039,
"narHash": "sha256-MqcbN/PgfXOv8S4q6GcmlORd6kJZ3UlFNhzCvLOEe4I=",
"owner": "simple-nixos-mailserver",
"repo": "nixos-mailserver",
"rev": "80d21ed7a1ab8007597f7cd9adc26ebc98b9611f",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"repo": "nixos-mailserver",
"type": "gitlab"
}
},
"sops-nix": { "sops-nix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [

View file

@ -8,6 +8,11 @@
url = "github:Mic92/sops-nix"; url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
simple-nixos-mailserver = {
url = "gitlab:simple-nixos-mailserver/nixos-mailserver";
inputs.nixpkgs.follows = "nixpkgs";
};
# home-manager = { # home-manager = {
# url = "home-manager"; # url = "home-manager";
# inputs.nixpkgs.follows = "nixpkgs"; # inputs.nixpkgs.follows = "nixpkgs";
@ -22,6 +27,7 @@
sops-nix, sops-nix,
# home-manager, # home-manager,
colmena, colmena,
simple-nixos-mailserver,
... ...
} @ inputs: let } @ inputs: let
overlays = [ overlays = [

View file

@ -2,6 +2,7 @@ inputs @ {
self, self,
nixpkgs, nixpkgs,
sops-nix, sops-nix,
simple-nixos-mailserver,
# home-manager, # home-manager,
overlays, overlays,
... ...
@ -14,11 +15,8 @@ inputs @ {
}; };
defaults = { defaults = {
pkgs,
lib, lib,
name, name,
nodes,
meta,
config, config,
... ...
}: { }: {
@ -50,4 +48,12 @@ inputs @ {
imports = [./machines/${name}/definition.nix]; imports = [./machines/${name}/definition.nix];
deployment.tags = ["zitadel" "sso" "ldap"]; deployment.tags = ["zitadel" "sso" "ldap"];
}; };
mail = {name, ...}: {
imports = [
./machines/${name}/definition.nix
simple-nixos-mailserver.nixosModule
];
deployment.tags = ["mail"];
};
} }

View file

@ -14,21 +14,91 @@ in {
enable = true; enable = true;
settings = { settings = {
theme = "auto"; theme = "auto";
server = {
buffers = {
read = 16384;
write = 16384;
};
};
authentication_backend.ldap = { authentication_backend.ldap = {
implementation = "lldap";
address = "ldap://localhost:3890"; address = "ldap://localhost:3890";
base_dn = "dc=procopius,dc=dk"; base_dn = "dc=procopius,dc=dk";
users_filter = "(&({username_attribute}={input})(objectClass=person))";
groups_filter = "(member={dn})";
user = "uid=authelia,ou=people,dc=procopius,dc=dk"; user = "uid=authelia,ou=people,dc=procopius,dc=dk";
}; };
definitions = {
network = {
internal = [
"192.168.1.0/24"
];
};
};
access_control = { access_control = {
default_policy = "deny"; default_policy = "deny";
# We want this rule to be low priority so it doesn't override the others # We want this rule to be low priority so it doesn't override the others
rules = lib.mkAfter [ rules = lib.mkAfter [
{ {
domain = "*.procopius.dk"; domain = [
"proxmox.procopius.dk"
"traefik.procopius.dk"
"prometheus.procopius.dk"
"alertmanager.procopius.dk"
];
policy = "one_factor";
subject = [
["group:server-admin"]
];
}
# bypass /api and /ping
{
domain = ["*.procopius.dk"];
policy = "bypass";
resources = [
"^/api$"
"^/api/"
"^/ping$"
];
}
# media
{
domain = [
"sonarr.procopius.dk"
"radarr.procopius.dk"
"readarr.procopius.dk"
"lidarr.procopius.dk"
"bazarr.procopius.dk"
"prowlarr.procopius.dk"
];
policy = "one_factor";
subject = [
["group:media-admin"]
];
}
# authenticated
{
domain = [
"gatus.procopius.dk"
];
policy = "one_factor"; policy = "one_factor";
} }
# bypass auth internally
# {
# domain = [
# "gatus.procopius.dk"
# "prometheus.procopius.dk"
# "alertmanager.procopius.dk"
# "sonarr.procopius.dk"
# "radarr.procopius.dk"
# "readarr.procopius.dk"
# "lidarr.procopius.dk"
# "bazarr.procopius.dk"
# "prowlarr.procopius.dk"
# ];
# policy = "bypass";
# networks = [
# "internal"
# ];
# }
]; ];
}; };
storage.postgres = { storage.postgres = {
@ -58,8 +128,8 @@ in {
}; };
notifier.smtp = { notifier.smtp = {
address = "smtp://mail.procopius.dk"; address = "smtp://mail.procopius.dk";
username = "admin@procopius.dk"; username = "authelia@procopius.dk";
sender = "auth@procopius.dk"; sender = "authelia@procopius.dk";
}; };
log.level = "info"; log.level = "info";
# identity_providers.oidc = { # identity_providers.oidc = {
@ -97,24 +167,9 @@ in {
environmentVariables = with config.sops; { environmentVariables = with config.sops; {
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE =
secrets."authelia/lldap_authelia_password".path; secrets."authelia/lldap_authelia_password".path;
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = secrets."authelia/smtp_authelia_password".path; AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = secrets.smtp-password_authelia.path;
}; };
}; };
# caddy = {
# virtualHosts."auth.procopius.cc".extraConfig = ''
# reverse_proxy :9091
# '';
# # A Caddy snippet that can be imported to enable Authelia in front of a service
# # Taken from https://www.authelia.com/integration/proxies/caddy/#subdomain
# extraConfig = ''
# (auth) {
# forward_auth :9091 {
# uri /api/authz/forward-auth
# copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
# }
# }
# '';
# };
}; };
# Give Authelia access to the Redis socket # Give Authelia access to the Redis socket
@ -142,7 +197,6 @@ in {
"authelia/storage_encryption_key".owner = authelia; "authelia/storage_encryption_key".owner = authelia;
# The password for the `authelia` LLDAP user # The password for the `authelia` LLDAP user
"authelia/lldap_authelia_password".owner = authelia; "authelia/lldap_authelia_password".owner = authelia;
"authelia/smtp_authelia_password".owner = authelia;
smtp-password_authelia = { smtp-password_authelia = {
owner = authelia; owner = authelia;
key = "service_accounts/authelia/password"; key = "service_accounts/authelia/password";

View file

@ -1,39 +0,0 @@
{
pkgs,
config,
...
}: {
systemd.services.lldap-bootstrap = {
description = "Bootstraps LLDAP users";
requires = ["lldap.service"];
serviceConfig = {
DynamicUser = true;
Type = "oneshot";
ProtectSystem = "strict";
ProtectHome = true;
PrivateUsers = true;
PrivateTmp = true;
LoadCredential = "inadyn.conf:${config.sops.templates."inadyn.conf".path}";
CacheDirectory = "inadyn";
ExecStart = ''
export LLDAP_URL=http://localhost:8080
export LLDAP_ADMIN_USERNAME=admin
export LLDAP_ADMIN_PASSWORD=changeme
export USER_CONFIGS_DIR="$(realpath ./configs/user)"
export GROUP_CONFIGS_DIR="$(realpath ./configs/group)"
export USER_SCHEMAS_DIR="$(realpath ./configs/user-schema)"
export GROUP_SCHEMAS_DIR="$(realpath ./configs/group-schema)"
export LLDAP_SET_PASSWORD_PATH="$(realpath ./lldap_set_password)"
export DO_CLEANUP=false
./bootstrap.sh
${pkgs.inadyn}/bin/inadyn \
--foreground \
--syslog \
--once \
--cache-dir ''${CACHE_DIRECTORY} \
--config ''${CREDENTIALS_DIRECTORY}/inadyn.conf
'';
};
};
}

View file

@ -1,12 +1,24 @@
{config, ...}: { {
sops.templates."default-groups.json" = { sops.templates."default-groups.json" = {
content = '' content = ''
{
"name": "mail"
}
{ {
"name": "git-user" "name": "git-user"
} }
{ {
"name": "git-admin" "name": "git-admin"
} }
{
"name": "media-user"
}
{
"name": "media-admin"
}
{
"name": "server-admin"
}
''; '';
path = "/bootstrap/group-configs/default-groups.json"; path = "/bootstrap/group-configs/default-groups.json";
owner = "lldap"; owner = "lldap";

View file

@ -7,6 +7,7 @@
cfg = config.services.lldapBootstrap; cfg = config.services.lldapBootstrap;
in { in {
imports = [ imports = [
./service-accounts.nix
./user-configs.nix ./user-configs.nix
./group-configs.nix ./group-configs.nix
]; ];

View file

@ -0,0 +1,51 @@
{config, ...}: {
sops.secrets."service_accounts/authelia/password" = {};
sops.secrets."service_accounts/forgejo/password" = {};
sops.secrets."service_accounts/jellyfin/password" = {};
sops.secrets."service_accounts/mail/password" = {};
sops.templates."service-accounts.json" = {
content = ''
{
"id": "authelia",
"email": "authelia@procopius.dk",
"password": "${config.sops.placeholder."service_accounts/authelia/password"}",
"displayName": "Authelia",
"groups": [
"lldap_password_manager",
"mail"
]
}
{
"id": "forgejo",
"email": "forgejo@procopius.dk",
"password": "${config.sops.placeholder."service_accounts/forgejo/password"}",
"displayName": "Forgejo",
"groups": [
"lldap_password_manager",
"mail"
]
}
{
"id": "jellyfin",
"email": "jellyfin@procopius.dk",
"password": "${config.sops.placeholder."service_accounts/jellyfin/password"}",
"displayName": "Jellyfin",
"groups": [
"lldap_password_manager"
]
}
{
"id": "mail",
"email": "mail@procopius.dk",
"password": "${config.sops.placeholder."service_accounts/mail/password"}",
"displayName": "NixOS Mailserver",
"groups": [
"lldap_password_manager",
"mail"
]
}
'';
path = "/bootstrap/user-configs/service-accounts.json";
owner = "lldap";
};
}

View file

@ -1,28 +1,47 @@
{config, ...}: { {
sops.secrets."service_accounts/authelia/password" = {}; sops.templates."user-configs.json" = {
sops.secrets."service_accounts/forgejo/password" = {};
sops.templates."service-accounts.json" = {
content = '' content = ''
{ {
"id": "authelia", "id": "plasmagoat",
"email": "authelia@procopius.dk", "email": "david.mikael@proton.me",
"password": "${config.sops.placeholder."service_accounts/authelia/password"}", "displayName": "David",
"displayName": "Authelia",
"groups": [ "groups": [
"lldap_password_manager" "media-user",
"media-admin",
"git-user",
"git-admin",
"server-admin"
] ]
} }
{ {
"id": "forgejo", "id": "kurisudanoda",
"email": "forgejo@procopius.dk", "email": "iluvmizuki@gmail.com",
"password": "${config.sops.placeholder."service_accounts/forgejo/password"}", "displayName": "Noda",
"displayName": "Forgejo",
"groups": [ "groups": [
"lldap_password_manager" "media-user",
"git-user"
]
}
{
"id": "dannydannydanny",
"email": "powerhouseplayer@gmail.com",
"displayName": "Danny",
"groups": [
"media-user",
"git-user"
]
}
{
"id": "stocksking",
"email": "ethanstocks9@gmail.com",
"displayName": "Ethan",
"groups": [
"media-user",
"git-user"
] ]
} }
''; '';
path = "/bootstrap/user-configs/service-accounts.json"; path = "/bootstrap/user-configs/user-configs.json";
owner = "lldap"; owner = "lldap";
}; };
} }

View file

@ -0,0 +1,19 @@
{
imports = [
./mailserver.nix
];
networking = {
interfaces.eth0.ipv4.addresses = [
{
address = "192.168.1.25";
prefixLength = 24;
}
];
nameservers = ["192.168.1.53"];
defaultGateway = "192.168.1.1";
};
deployment.targetHost = "192.168.1.25";
system.stateVersion = "25.05";
}

View file

@ -0,0 +1,37 @@
{config, ...}: {
sops.secrets."service_accounts/mail/password" = {};
mailserver = {
enable = true;
stateVersion = 3;
fqdn = "mail.procopius.dk";
domains = ["procopius.dk"];
localDnsResolver = false;
ldap = {
enable = true;
uris = [
"ldap://auth.lab:3890"
];
bind = {
dn = "cn=mail,ou=people,dc=procopius,dc=dk";
passwordFile = config.sops.secrets."service_accounts/mail/password".path;
};
postfix = {
filter = "(&(objectClass=person)(memberOf=cn=mail,ou=groups,dc=procopius,dc=dk)(|(mail=%s)(mail-alias=%s)))"; # Will require MR!351 for aliases to work properly
mailAttribute = "mail";
};
dovecot = {
userFilter = "(&(objectClass=person)(memberOf=cn=mail,ou=groups,dc=procopius,dc=dk)(mail=%u))";
passFilter = "(&(objectClass=person)(memberOf=cn=mail,ou=groups,dc=procopius,dc=dk)(mail=%u))";
};
searchBase = "ou=people,dc=procopius,dc=dk";
};
# Use Let's Encrypt certificates. Note that this needs to set up a stripped
# down nginx and opens port 80.
certificateScheme = "acme-nginx";
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "david.mikael@proton.me";
}

View file

@ -1,7 +1,7 @@
{ {
lib, lib,
config, config,
nodes, # nodes,
# name, # name,
# meta, # meta,
... ...
@ -21,7 +21,7 @@ in {
}; };
clientUrl = mkOption { clientUrl = mkOption {
type = types.string; type = types.str;
default = "http://monitor.lab:3100/loki/api/v1/push"; default = "http://monitor.lab:3100/loki/api/v1/push";
}; };

View file

@ -18,11 +18,51 @@
} }
{ {
name = "sonarr"; name = "sonarr";
url = "https://sonarr.procopius.dk/health"; url = "https://sonarr.procopius.dk/ping";
interval = "5m"; interval = "5m";
conditions = [ conditions = [
"[STATUS] == 200" "[STATUS] == 200"
"[BODY] == Healthy" "[BODY].status == OK"
"[RESPONSE_TIME] < 300"
];
}
{
name = "radarr";
url = "https://radarr.procopius.dk/ping";
interval = "5m";
conditions = [
"[STATUS] == 200"
"[BODY].status == OK"
"[RESPONSE_TIME] < 300"
];
}
{
name = "lidarr";
url = "https://lidarr.procopius.dk/ping";
interval = "5m";
conditions = [
"[STATUS] == 200"
"[BODY].status == OK"
"[RESPONSE_TIME] < 300"
];
}
{
name = "readarr";
url = "https://readarr.procopius.dk/ping";
interval = "5m";
conditions = [
"[STATUS] == 200"
"[BODY].status == OK"
"[RESPONSE_TIME] < 300"
];
}
{
name = "prowlarr";
url = "https://prowlarr.procopius.dk/ping";
interval = "5m";
conditions = [
"[STATUS] == 200"
"[BODY].status == OK"
"[RESPONSE_TIME] < 300" "[RESPONSE_TIME] < 300"
]; ];
} }

View file

@ -1,10 +1,4 @@
{ {config, ...}: {
config,
pkgs,
modulesPath,
lib,
...
}: {
# Add grafana user to the inlfuxdb2 group (for secret) # Add grafana user to the inlfuxdb2 group (for secret)
users.users.grafana.extraGroups = ["influxdb2"]; users.users.grafana.extraGroups = ["influxdb2"];
services.grafana.enable = true; services.grafana.enable = true;

View file

@ -1,8 +1,4 @@
{ {config, ...}: let
config,
pkgs,
...
}: let
influxdbPassword = config.sops.secrets."influxdb/password".path; influxdbPassword = config.sops.secrets."influxdb/password".path;
influxdbToken = config.sops.secrets."influxdb/token".path; influxdbToken = config.sops.secrets."influxdb/token".path;
in { in {

View file

@ -1,10 +1,4 @@
{ {pkgs, ...}: let
config,
pkgs,
modulesPath,
lib,
...
}: let
monitor_hostname = "monitor.lab"; monitor_hostname = "monitor.lab";
traefik_hostname = "traefik.lab"; traefik_hostname = "traefik.lab";
sandbox_hostname = "sandbox.lab"; sandbox_hostname = "sandbox.lab";

View file

@ -1,8 +1,4 @@
{ {config, ...}: let
config,
pkgs,
...
}: let
promtail_port = 9080; promtail_port = 9080;
in { in {
networking.firewall.allowedTCPPorts = [promtail_port]; networking.firewall.allowedTCPPorts = [promtail_port];

View file

@ -1,4 +1,4 @@
nixos-rebuild switch --flake .#traefik --target-host root@192.168.1.171 --verbose nixos-rebuild switch --flake .#traefik --target-host root@traefik.lab --verbose
nixos-rebuild switch --flake .#proxmox --target-host root@192.168.1.205 --verbose nixos-rebuild switch --flake .#proxmox --target-host root@192.168.1.205 --verbose
nixos-rebuild switch --flake .#sandbox --target-host root@sandbox.lab --verbose nixos-rebuild switch --flake .#sandbox --target-host root@sandbox.lab --verbose
nixos-rebuild switch --flake .#monitoring --target-host root@monitor.lab --verbose nixos-rebuild switch --flake .#monitoring --target-host root@monitor.lab --verbose
@ -6,6 +6,7 @@ nixos-rebuild switch --flake .#forgejo --target-host root@forgejo.lab --verbose
nixos-rebuild switch --flake .#dns --target-host root@192.168.1.140 --verbose nixos-rebuild switch --flake .#dns --target-host root@192.168.1.140 --verbose
nixos-rebuild switch --flake .#keycloak --target-host root@keycloak.lab --verbose nixos-rebuild switch --flake .#keycloak --target-host root@keycloak.lab --verbose
nixos-rebuild switch --flake .#mail --target-host root@mail.lab --verbose nixos-rebuild switch --flake .#mail --target-host root@mail.lab --verbose
nixos-rebuild switch --flake .#media --target-host root@media.lab --verbose
nixos-rebuild switch --flake .#runner01 --target-host root@forgejo-runner-01.lab --verbose nixos-rebuild switch --flake .#runner01 --target-host root@forgejo-runner-01.lab --verbose

View file

@ -1,6 +1,10 @@
{ config, pkgs, modulesPath, lib, ... }:
{ {
config,
pkgs,
modulesPath,
lib,
...
}: {
######################################################################## ########################################################################
# IMPORTS & PROFILE # IMPORTS & PROFILE
# #
@ -30,7 +34,6 @@
services.qemuGuest.enable = lib.mkDefault true; services.qemuGuest.enable = lib.mkDefault true;
# GRUB on the “boot drive” # GRUB on the “boot drive”
# Both live and template should install a bootloader on /dev/disk/by-label/nixos.
boot.loader.grub.enable = lib.mkDefault true; boot.loader.grub.enable = lib.mkDefault true;
boot.loader.grub.devices = ["nodev"]; boot.loader.grub.devices = ["nodev"];

View file

@ -30,6 +30,7 @@
# Static IPs # Static IPs
"/dns.lab/192.168.1.53" "/dns.lab/192.168.1.53"
"/traefik.lab/192.168.1.80" "/traefik.lab/192.168.1.80"
"/mail.lab/192.168.1.25"
# "/proxmox-01.lab/192.168.1.205" # "/proxmox-01.lab/192.168.1.205"
# "/nas-01.lab/192.168.1.226" # "/nas-01.lab/192.168.1.226"

View file

@ -1,14 +0,0 @@
{
config,
pkgs,
modulesPath,
lib,
...
}: {
imports = [
../../templates/base.nix
./networking.nix
./sops.nix
./mailserver.nix
];
}

View file

@ -1,39 +0,0 @@
{
config,
pkgs,
...
}: {
imports = [
(builtins.fetchTarball {
# Pick a release version you are interested in and set its hash, e.g.
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-25.05/nixos-mailserver-nixos-25.05.tar.gz";
# To get the sha256 of the nixos-mailserver tarball, we can use the nix-prefetch-url command:
# release="nixos-25.05"; nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack
sha256 = "0jpp086m839dz6xh6kw5r8iq0cm4nd691zixzy6z11c4z2vf8v85";
})
];
mailserver = {
enable = true;
fqdn = "mail.procopius.dk";
domains = ["procopius.dk"];
# A list of all login accounts. To create the password hashes, use
# nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'
loginAccounts = {
"admin@procopius.dk" = {
hashedPasswordFile = config.sops.secrets.mailserver-admin-pass.path;
aliases = [
"@procopius.dk"
"postmaster@procopius.dk"
];
};
};
# Use Let's Encrypt certificates. Note that this needs to set up a stripped
# down nginx and opens port 80.
certificateScheme = "acme-nginx";
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "david.mikael@proton.me";
}

View file

@ -1,8 +0,0 @@
{
config,
lib,
pkgs,
...
}: {
networking.hostName = "mail";
}

View file

@ -1,8 +0,0 @@
{...}: let
mailserverSops = ../../secrets/mailserver/secrets.yml;
in {
sops.secrets.mailserver-admin-pass = {
sopsFile = mailserverSops;
mode = "0440";
};
}

View file

@ -1,7 +1,9 @@
{config, ...}: { {config, ...}: {
services.sonarr.settings = { services.sonarr.settings.auth.method = "External";
auth.method = "External"; services.radarr.settings.auth.method = "External";
}; services.lidarr.settings.auth.method = "External";
services.readarr.settings.auth.method = "External";
services.prowlarr.settings.auth.method = "External";
nixarr = { nixarr = {
enable = true; enable = true;

View file

@ -13,19 +13,10 @@
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
oauth2proxy = { lldap = {
rule = "Host(`radarr.procopius.dk`) && PathPrefix(`/oauth2/`)"; rule = "Host(`lldap.procopius.dk`)";
service = "oauth2proxy"; service = "lldap";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = ["auth-headers"];
tls.certResolver = "letsencrypt";
};
oauth2route = {
rule = "Host(`oauth.procopius.dk`)";
service = "oauth2proxy";
entryPoints = ["websecure"];
middlewares = ["auth-headers"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
} }

View file

@ -1,6 +1,6 @@
{ {
keycloak.loadBalancer.servers = [{url = "http://keycloak.lab:8080";}]; keycloak.loadBalancer.servers = [{url = "http://keycloak.lab:8080";}];
oauth2proxy.loadBalancer.servers = [{url = "http://localhost:4180";}];
authelia.loadBalancer.servers = [{url = "http://auth.lab:9091";}]; authelia.loadBalancer.servers = [{url = "http://auth.lab:9091";}];
lldap.loadBalancer.servers = [{url = "http://auth.lab:17170";}];
} }

View file

@ -3,7 +3,7 @@
rule = "Host(`traefik.procopius.dk`)"; rule = "Host(`traefik.procopius.dk`)";
service = "traefik"; service = "traefik";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = ["oauth-auth"]; middlewares = ["authelia"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
@ -25,7 +25,7 @@
rule = "Host(`proxmox.procopius.dk`)"; rule = "Host(`proxmox.procopius.dk`)";
service = "proxmox"; service = "proxmox";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = ["oauth-auth"]; middlewares = ["authelia"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
nas = { nas = {

View file

@ -6,14 +6,18 @@
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
jellyseerr = {
rule = "Host(`jellyseerr.procopius.dk`)";
service = "jellyseerr";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
radarr = { radarr = {
rule = "Host(`radarr.procopius.dk`)"; rule = "Host(`radarr.procopius.dk`)";
service = "radarr"; service = "radarr";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = [ middlewares = ["authelia"];
"oauth-auth"
"restrict-admin"
];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
@ -21,15 +25,39 @@
rule = "Host(`sonarr.procopius.dk`)"; rule = "Host(`sonarr.procopius.dk`)";
service = "sonarr"; service = "sonarr";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = ["oauth-auth"]; middlewares = ["authelia"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
jellyseerr = { prowlarr = {
rule = "Host(`jellyseerr.procopius.dk`)"; rule = "Host(`prowlarr.procopius.dk`)";
service = "jellyseerr"; service = "prowlarr";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
# middlewares = ["oauth-auth"]; middlewares = ["authelia"];
tls.certResolver = "letsencrypt";
};
bazarr = {
rule = "Host(`bazarr.procopius.dk`)";
service = "bazarr";
entryPoints = ["websecure"];
middlewares = ["authelia"];
tls.certResolver = "letsencrypt";
};
lidarr = {
rule = "Host(`lidarr.procopius.dk`)";
service = "lidarr";
entryPoints = ["websecure"];
middlewares = ["authelia"];
tls.certResolver = "letsencrypt";
};
readarr = {
rule = "Host(`readarr.procopius.dk`)";
service = "readarr";
entryPoints = ["websecure"];
middlewares = ["authelia"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
} }

View file

@ -1,6 +1,11 @@
{ {
jellyfin.loadBalancer.servers = [{url = "http://media.lab:8096";}]; jellyfin.loadBalancer.servers = [{url = "http://media.lab:8096";}];
jellyseerr.loadBalancer.servers = [{url = "http://media.lab:5055";}];
radarr.loadBalancer.servers = [{url = "http://media.lab:7878";}]; radarr.loadBalancer.servers = [{url = "http://media.lab:7878";}];
sonarr.loadBalancer.servers = [{url = "http://media.lab:8989";}]; sonarr.loadBalancer.servers = [{url = "http://media.lab:8989";}];
jellyseerr.loadBalancer.servers = [{url = "http://media.lab:5055";}]; readarr.loadBalancer.servers = [{url = "http://media.lab:8787";}];
lidarr.loadBalancer.servers = [{url = "http://media.lab:8686";}];
bazarr.loadBalancer.servers = [{url = "http://media.lab:6767";}];
prowlarr.loadBalancer.servers = [{url = "http://media.lab:9696";}];
} }

View file

@ -19,25 +19,16 @@ in {
}; };
}; };
oauth-auth = { authelia = {
forwardAuth = { forwardAuth = {
address = "http://localhost:4180/"; address = "http://auth.lab:9091/api/authz/forward-auth";
trustForwardHeader = true; trustForwardHeader = true;
authResponseHeaders = [ authResponseHeaders = [
"Authorization" "Remote-User"
"X-Auth-Request-Access-Token" "Remote-Groups"
"X-Auth-Request-User" "Remote-Email"
"X-Auth-Request-Email" "Remote-Name"
"X-Auth-Request-Preferred-Username" # Recommended
"X-Auth-Request-Access-Token" # If you want to pass the token
"X-Auth-Request-Groups" # If you configured a mapper in Keycloak to emit groups
]; ];
}; };
}; };
restrict-admin = {
forwardAuth = {
address = "http://localhost:4180/oauth2/auth?allowed_groups=role:admin";
};
};
} }

View file

@ -3,7 +3,7 @@
rule = "Host(`prometheus.procopius.dk`)"; rule = "Host(`prometheus.procopius.dk`)";
service = "prometheus"; service = "prometheus";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = ["oauth-auth"]; middlewares = ["authelia"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
grafana = { grafana = {
@ -16,14 +16,14 @@
rule = "Host(`alertmanager.procopius.dk`)"; rule = "Host(`alertmanager.procopius.dk`)";
service = "alertmanager"; service = "alertmanager";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = ["oauth-auth"]; middlewares = ["authelia"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
gatus = { gatus = {
rule = "Host(`gatus.procopius.dk`)"; rule = "Host(`gatus.procopius.dk`)";
service = "gatus"; service = "gatus";
entryPoints = ["websecure"]; entryPoints = ["websecure"];
middlewares = ["oauth-auth"]; middlewares = ["authelia"];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
umami = { umami = {

View file

@ -11,6 +11,5 @@
./traefik.nix ./traefik.nix
./promtail.nix ./promtail.nix
./sops.nix ./sops.nix
./oauth2proxy.nix
]; ];
} }

View file

@ -1,76 +0,0 @@
# /etc/nixos/configuration.nix
{
config,
lib,
pkgs,
...
}: let
oauth2ProxyKeyFile = config.sops.secrets."oauth2-proxy-env".path;
in {
services.oauth2-proxy = {
enable = true;
package = pkgs.oauth2-proxy;
keyFile = oauth2ProxyKeyFile;
provider = "keycloak-oidc"; # Use "oidc" for standard OIDC providers like Keycloak
oidcIssuerUrl = "https://keycloak.procopius.dk/realms/homelab";
clientID = "oauth2-proxy"; # Matches the client ID in Keycloak
# Public URL for oauth2-proxy itself, where Keycloak redirects back to
redirectURL = "https://oauth.procopius.dk/oauth2/callback";
upstream = ["static://202"];
extraConfig = {
code-challenge-method = "S256";
# email-domain = "*";
auth-logging = true;
request-logging = true;
whitelist-domain = ".procopius.dk";
pass-host-header = true;
skip-provider-button = true;
};
# Cookie configuration
cookie = {
name = "_oauth2_proxy_homelab";
domain = ".procopius.dk";
secure = true;
httpOnly = true;
expire = "24h";
refresh = "1h";
};
# Listen address for oauth2-proxy internally. Traefik will forward to this.
httpAddress = "http://127.0.0.1:4180"; # Ensure this port is not blocked by your firewall internally
# Reverse proxy settings for headers
reverseProxy = true; # Set to true because it's behind Traefik
# Headers to set for the upstream applications after successful authentication
setXauthrequest = true; # Set X-Auth-Request-User, X-Auth-Request-Email etc.
passBasicAuth = true; # Pass HTTP Basic Auth headers
passHostHeader = true; # Pass the original Host header to the upstream
# Authorization rules for who can access
# You can restrict by email domain (allows everyone from that domain)
email.domains = ["*"]; # Allows any authenticated user from Keycloak
# Or restrict by specific email addresses (if you want tighter control):
# email.addresses = allowedOauth2ProxyEmails;
# Logging
requestLogging = true;
# Optional: If you use specific scopes for Keycloak (e.g., if you want groups claim)
# scope = "openid profile email";
# If you specifically added a 'groups' claim in Keycloak:
scope = "openid profile email";
# You can add extra command-line flags here if needed, e.g., for debug logging
# extraConfig = {
#
# };
};
# Expose the internal port for oauth2-proxy if needed for debugging or direct access (less common)
networking.firewall.allowedTCPPorts = [4180];
}

View file

@ -21,6 +21,10 @@ service_accounts:
password: ENC[AES256_GCM,data:r4Qy09FOhUgD48SHSkWKtrlrMptvXYdScCL8h7gjJNs=,iv:IzsDdV4o35hnS/F2S713cJ5pQ+PGiaVTmTWe6YXgfYc=,tag:OisvmY7QI2Ph7R3g3Xy/Ww==,type:str] password: ENC[AES256_GCM,data:r4Qy09FOhUgD48SHSkWKtrlrMptvXYdScCL8h7gjJNs=,iv:IzsDdV4o35hnS/F2S713cJ5pQ+PGiaVTmTWe6YXgfYc=,tag:OisvmY7QI2Ph7R3g3Xy/Ww==,type:str]
forgejo: forgejo:
password: ENC[AES256_GCM,data:HLEoGjx++9fkiJQoLWQvAjgg58mIs1vk1hvUJvr6TiA=,iv:mPIx9cSlHEK+0wLs1/1bjYcsxgdwgLReUoI5JrA4E1k=,tag:TdyznTIGiAFFq8D3Irb0rA==,type:str] password: ENC[AES256_GCM,data:HLEoGjx++9fkiJQoLWQvAjgg58mIs1vk1hvUJvr6TiA=,iv:mPIx9cSlHEK+0wLs1/1bjYcsxgdwgLReUoI5JrA4E1k=,tag:TdyznTIGiAFFq8D3Irb0rA==,type:str]
jellyfin:
password: ENC[AES256_GCM,data:PpUHEhNfnR1eg7DmnO7tyNciNE4Tsx/Y4uL92gqiods=,iv:DNKQfymvgEu/iEW8t79m0ZmKTU0Ffasu+gp2KOIAK3o=,tag:lGKw5dbXqImDJNVX6p8kLg==,type:str]
mail:
password: ENC[AES256_GCM,data:6lfziq1zXlFxCAFWv5co3MkBgwaWixjHHX9riQXCbe0=,iv:/t4CnW3bKUDxfpE/qGf1LPs0ciivRMkfgJ1nMseruy4=,tag:TWApzLsm2HV+JMaZLG/Kig==,type:str]
sops: sops:
age: age:
- recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y - recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y
@ -32,7 +36,7 @@ sops:
QzNYRk5ERmR4aGtLQ3dwQ1lPeDZyaEkKJMLXqv6tBBql7VVnWDIwAh24SfQ2O6Ca QzNYRk5ERmR4aGtLQ3dwQ1lPeDZyaEkKJMLXqv6tBBql7VVnWDIwAh24SfQ2O6Ca
CEOQTGEonbqr5doWqTsXUXrdQAS0amL45UdT6ITFtfNAjaHwCMfhZg== CEOQTGEonbqr5doWqTsXUXrdQAS0amL45UdT6ITFtfNAjaHwCMfhZg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-07-16T00:07:58Z" lastmodified: "2025-07-16T15:33:06Z"
mac: ENC[AES256_GCM,data:kKSjCmLGbr7WaLb+Z1KZL/bjBszNgCCAb67CENaKKpFbqbCk3o5QFok/kVTs3k3JwKmODqbTe0ebP6uMENN/t85+1n4ofnMq5ire/NqCyoE1EJFDmG9xyys24CB+NJJZ2trdxm5CYutme7FfG4bQY0/2OgflmjiZeBsMZcxRxtI=,iv:md6qX+WlFFgMFbDs8MTTKXEOPWKFoVYAUMehWvGF5wk=,tag:Y3AelrfrsBMIJ0wYzFYtLQ==,type:str] mac: ENC[AES256_GCM,data:nZm7N8qfANzHadtW+3eTJljpmYejJdKGFO44iw40CnwlGgb454us9LZbQIAYkNiS7UkivoWa5BqvgLcpObHNAn3tVi+ha0jySIrAmp43y5ilmg76fvL4znel4Nk7eRiGoF3t3xiCR39/3l7PPffx2RJ6PerEyGBpiUZ6mBcWoTE=,iv:UmhSynpMdTnY0R6jwDJts13b0rKsaRFlCizdM2oargE=,tag:Q2xh/QXFOQYqqkxKs7nujA==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.10.2 version: 3.10.2