Compare commits

...

7 commits

Author SHA1 Message Date
goeranh
26d56a1dfe
update dovecot ldap credentials 2026-03-21 18:23:28 +01:00
goeranh
e3a9a391c2
use secret files 2026-03-21 18:04:08 +01:00
goeranh
2fa576a302
init sops for mail and auth 2026-03-21 18:03:50 +01:00
goeranh
43cfebcec6
disable dhcp 2026-03-20 17:18:22 +01:00
goeranh
e6442b2442
add auth and mail as containers 2026-03-20 17:09:23 +01:00
goeranh
9e3fa025cd
redirect bbb 2026-03-20 17:09:01 +01:00
goeranh
5bed1bbba1
remove git hooks 2026-03-20 16:24:09 +01:00
10 changed files with 421 additions and 114 deletions

View file

@ -1 +0,0 @@
/nix/store/1w2s62i701n28sj08gn1445qr4v3vijp-pre-commit-config.json

View file

@ -1,38 +1,17 @@
# SOPS configuration for StuRa HTW Dresden infrastructure
#
# This file defines which keys can decrypt which secrets.
# Add GPG public keys (.asc files) or age keys to keys/hosts/ and keys/users/
# to grant decryption access to hosts and users respectively.
keys:
# Admin/user keys - add GPG public keys here
# Example:
# - &user_admin_key age1... or pgp fingerprint
- &goeranh age1qp7w80k3qtj79xsl0gwsfrkm037xrlnhm6th7tcyrvufh3szzp6s2pe7ra
- &mail age156ak7kc79tuwpv0hk9atl5dg27jqs6ddfqxvr9m4twqgsr23lgvsdmyfpr
- &auth age1njnkkr489hfmpn337zna2k3z66y9086t7cpcmz2vn68p4x43aujs6wh0g5
# Host keys - add host-specific keys here
# Example:
# - &host_proxy_key age1... or pgp fingerprint
# - &host_git_key age1... or pgp fingerprint
# Define which keys can access which files
creation_rules:
# Default rule: all secrets can be decrypted by admin keys
- path_regex: secrets/.*\.yaml$
# key_groups:
# - pgp:
# - *user_admin_key
# - age:
# - *user_admin_key
# Host-specific secrets (example)
# - path_regex: secrets/proxy/.*\.yaml$
# key_groups:
# - pgp:
# - *user_admin_key
# - *host_proxy_key
# - path_regex: secrets/git/.*\.yaml$
# key_groups:
# - pgp:
# - *user_admin_key
# - *host_git_key
- path_regex: hosts/mail/secrets.sops.yml$
key_groups:
- age:
- *mail
- *goeranh
- path_regex: hosts/auth/secrets.sops.yml$
key_groups:
- age:
- *auth
- *goeranh

66
flake.lock generated
View file

@ -114,22 +114,6 @@
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false,
"locked": {
"lastModified": 1761588595,
@ -185,34 +169,12 @@
}
},
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat_2",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1772893680,
"narHash": "sha256-JDqZMgxUTCq85ObSaFw0HhE+lvdOre1lx9iI6vYyOEs=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "8baab586afc9c9b57645a734c820e4ac0a604af9",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"git-hooks_2": {
"inputs": {
"flake-compat": [
"mailserver",
"flake-compat"
],
"gitignore": "gitignore_2",
"gitignore": "gitignore",
"nixpkgs": [
"mailserver",
"nixpkgs"
@ -233,27 +195,6 @@
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"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"
}
},
"gitignore_2": {
"inputs": {
"nixpkgs": [
"mailserver",
@ -278,8 +219,8 @@
"mailserver": {
"inputs": {
"blobs": "blobs",
"flake-compat": "flake-compat_3",
"git-hooks": "git-hooks_2",
"flake-compat": "flake-compat_2",
"git-hooks": "git-hooks",
"nixpkgs": "nixpkgs_2"
},
"locked": {
@ -440,7 +381,6 @@
"inputs": {
"authentik": "authentik",
"disko": "disko",
"git-hooks": "git-hooks",
"mailserver": "mailserver",
"nixpkgs": "nixpkgs_3",
"sops": "sops"

View file

@ -18,10 +18,6 @@
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
git-hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
@ -32,7 +28,6 @@
mailserver,
disko,
sops,
git-hooks,
}:
let
sshkeys = [
@ -47,12 +42,6 @@
devShells.x86_64-linux.default =
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
pre-commit-check = git-hooks.lib.x86_64-linux.run {
src = ./.;
hooks = {
nixfmt-rfc-style.enable = true;
};
};
in
pkgs.mkShell {
# Import GPG keys from keys directory
@ -64,15 +53,11 @@
# Isolate sops GPG keys to .git/gnupg (optional)
# sopsCreateGPGHome = true;
shellHook = ''
${pre-commit-check.shellHook}
'';
nativeBuildInputs = [
sops.packages.x86_64-linux.sops-import-keys-hook
];
buildInputs = pre-commit-check.enabledPackages ++ [
buildInputs = [
pkgs.sops
];
};
@ -211,6 +196,7 @@
disko.nixosModules.disko
authentik.nixosModules.default
mailserver.nixosModules.mailserver
sops.nixosModules.default
{
_module.args = { inherit self modulesPath; };
}

84
hosts/auth/authentik.nix Normal file
View file

@ -0,0 +1,84 @@
{
config,
lib,
pkgs,
...
}:
{
users.groups.authentik = { };
users.users.authentik = {
isSystemUser = true;
extraGroups = [ "docker" ];
group = "authentik";
};
virtualisation.docker.enable = true;
systemd.services = {
authentik-secrets-setup = {
enable = true;
};
};
users.groups.authentik-ldap = {};
users.users.authentik-ldap = {
isSystemUser = true;
group = "authentik-ldap";
};
systemd.services.authentik-ldap.serviceConfig = {
DynamicUser = lib.mkForce false;
User = "authentik-ldap";
};
services.authentik-ldap = {
enable = true;
environmentFile = config.sops.secrets."auth/ldap-env-file".path;
# environmentFile = "/var/lib/authentik-ldap-env";
};
services.authentik = {
enable = true;
# environmentFile = "/var/lib/authentik_secret";
environmentFile = config.sops.secrets."auth/env-file".path;
settings = {
email = {
host = "mail.${config.networking.domain}";
port = 25;
username = "authentik@${config.networking.domain}";
use_tls = false;
use_ssl = false;
from = "authentik@${config.networking.domain}";
};
disable_startup_analytics = true;
avatars = "initials";
};
};
systemd.services.authentik-secrets-generator = {
enable = true;
requiredBy = [
"authentik-secrets-setup.service"
"authentik-worker.service"
];
script = ''
echo "AUTHENTIK_SECRET_KEY=$(${pkgs.openssl}/bin/openssl rand -hex 32)" > /var/lib/authentik_secret
'';
};
services.nginx = {
enable = true;
virtualHosts = {
"auth.${config.networking.domain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:9000";
proxyWebsockets = true;
recommendedProxySettings = true;
extraConfig = ''
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
};
};
};
}

45
hosts/auth/default.nix Normal file
View file

@ -0,0 +1,45 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
sops = {
defaultSopsFile = ./secrets.sops.yml;
secrets = {
"auth/env-file".owner = "authentik";
"auth/ldap-env-file".owner = "authentik";
};
};
imports = [
"${modulesPath}/virtualisation/proxmox-lxc.nix"
./authentik.nix
];
networking = {
hostName = "auth";
domain = lib.mkForce "test.htw.stura-dresden.de";
useDHCP = false;
interfaces.eth0.ipv4.addresses = [
{
address = "141.56.51.96";
prefixLength = 24;
}
];
defaultGateway = {
address = "141.56.51.254";
interface = "eth0";
};
firewall.allowedTCPPorts = [
80
443
3389
];
};
system.stateVersion = "25.05";
}

View file

@ -0,0 +1,27 @@
auth:
env-file: ENC[AES256_GCM,data:WDJ3daYCxybublm8VWO8W5HHmYYWKOcw81f+fQ0Vz78EOvbYI+SgEwnuAd/0/eeGkTPEJPSCfbymArs+YRTdibgO5y/34jdN0DOVQetZLPXrDbcZ/Sg=,iv:bykKdvkgmxwgptkGHKH4rnFknPA0PTrW+mEqIzIYERk=,tag:8UKhLz/VoPiXckcIEBfrLg==,type:str]
ldap-env-file: ENC[AES256_GCM,data:CpgiiUin3hj8+aykcSU2rasaCFt/CAC5lK3Ek7zxzw6hYCkhwxIc9a4Xfy9SxSQtASJ5dOOrOaa8gA1ahf4Z1g/1981fhxlQPeJd9PlJFgdL4CP5P6ZrPBKZKgygnreUo6HC7Rfc9x2CRmnDhQvMVUmQL9akZRNYasX+9IlRyKmLSFmi35IuryFhVLwfjfECmq51/Xo2WYzjWrayfFuOpS0jHWicQxXvXq6QcLvqmbk5euXiHDkFXOXcwMRr6mAompDAKa9BKXqcRDbxOWqzJ1gflEJvOJi249PeYFo+poTK1CUtBCTejFo=,iv:P1xN6wq5oeba1LSEn6UiArOka37alV/PhI5kOmpfDG0=,tag:Xisd5elHQ8mhvE6YEbCuLg==,type:str]
sops:
age:
- recipient: age1njnkkr489hfmpn337zna2k3z66y9086t7cpcmz2vn68p4x43aujs6wh0g5
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHRmV2dGt1UXZ3M2RKd24y
V0x4MzZyYUh0aDVwQ1NmOCtyOHR6Z1c0R1ZNCi80Nk9PZFVTcVFIQjlZVXJNeXBN
TC9td05ZeWVVTCtFSWhqazN6bFF3akkKLS0tIEV3YzdRUDA5Q2dBd2JWUWNqOTU4
SnZtdVd4Q3lCaStJTnV4U2cvZUZEMlkK85XYSh6VbDFPKPIhKBKtkErGtgsHjXxy
kq14EXwfZnnBlR76JMQgPvSLrDLdj+4tDIVcuE4JplCoSvbGKckGww==
-----END AGE ENCRYPTED FILE-----
- recipient: age1qp7w80k3qtj79xsl0gwsfrkm037xrlnhm6th7tcyrvufh3szzp6s2pe7ra
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkQm5nUHkzZExKOXNzNjk4
Y1JURVJwNXhFUGZJak8vbEtCNnE2dHFuNVVNClJHQ3E2RGJkbUtlMDNwcy9Ib01Q
dG9nRTVJejkySTdlb2IrbHF4Z3ZMTmcKLS0tIHUwNndGdW9EaWwyNmRUb2NQU2Vs
MC9VSmVqVlVHRlJ4NXozUkQ4ZDVEVlkKbfVoBNsral3n7rG7ujUgdQXF68EVB+4G
MKMuOiY05QGBViLYyKh1jioHv6nds1hCuc2vpLNB3J0KT3I2q/a0VQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-21T16:42:34Z"
mac: ENC[AES256_GCM,data:i9hTUqbrmc2mD8PAbCe2gWern4ArMIkTQWN7eaJcsjZ9m6LZjOQFpnrpgPg6fj3hazgnFn86veNvQGe/J50NLnwj2FCyF3jKG3xkc7rKa9fyD0Yz0XnpbNKtDb2YGxwyBmLsnnyl6sdpyvPipZYCfwM+bhB8OERIXVXKwbZOn1A=,iv:dKI/NsMcVBNBOw0kYEQqrgfdvLKDg4NM/yRBYDqXIxU=,tag:xkg0z7IUy2m4ivosB925vQ==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.1

210
hosts/mail/default.nix Normal file
View file

@ -0,0 +1,210 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
let
generatedAliases = pkgs.writeText "generated-aliases" (
lib.concatStringsSep "\n" (
lib.mapCartesianProduct
({ aliases, domain }: "${aliases}@${domain} root@test.htw.stura-dresden.de")
{
aliases = [
"abuse"
"hostmaster"
"noreply"
"postmaster"
"webmaster"
];
domain = config.mailserver.domains;
}
)
);
in
{
sops = {
defaultSopsFile = ./secrets.sops.yml;
secrets = {
"ldap_passwd".owner = "dovecot2";
};
};
imports = [
"${modulesPath}/virtualisation/proxmox-lxc.nix"
];
security.pam.loginLimits = [
{
domain = "*";
type = "soft";
item = "nofile";
value = "8192";
}
];
networking = {
hostName = "mail";
domain = lib.mkForce "test.htw.stura-dresden.de";
useDHCP = false;
interfaces.ens18.ipv4.addresses = [
{
address = "141.56.51.95";
prefixLength = 24;
}
];
defaultGateway = {
address = "141.56.51.254";
interface = "eth0";
};
};
services.nginx.virtualHosts = {
"lists.${config.networking.domain}" = {
enableACME = true;
forceSSL = true;
# locations."/" = {
# proxyPass = "http://127.0.0.1:18507";
# };
};
};
services.automx2 = {
enable = true;
domain = "${config.networking.domain}";
settings = {
automx2 = {
db_uri = "sqlite:////var/lib/automx2/db.sqlite";
proxy_count = 1;
};
};
};
services.mailman = {
enable = true;
hyperkitty = {
enable = true;
};
serve.enable = true;
webHosts = [
"lists.${config.networking.domain}"
];
};
services.mailman.siteOwner = "mailman@${config.networking.domain}";
mailserver = {
enable = true;
fqdn = "mail.${config.networking.domain}";
domains = [
"${config.networking.domain}"
"lists.${config.networking.domain}"
];
ldap = {
enable = true;
bind = {
# dn = "cn=dovecot,ou=users,DC=test,DC=htw,DC=stura-dresden,DC=de";
dn = "cn=dovecot,ou=users,dc=mail,dc=htw,dc=stura-dresden,dc=de";
# passwordFile = "/var/lib/dovecot_ldap_passwd";
passwordFile = config.sops.secrets.ldap_passwd.path;
};
dovecot = {
userFilter = "(&(objectClass=posixAccount)(mail=%u))";
passFilter = "(&(objectClass=posixAccount)(mail=%u))";
userAttrs = "cn";
};
postfix = {
filter = "(|(&(objectClass=posixAccount)(mail=%s))(&(objectClass=posixAccount)(cn=%s)))";
mailAttribute = "mail";
uidAttribute = "cn";
};
#searchBase = "DC=test,DC=htw,DC=stura-dresden,DC=de";
searchBase = "dc=mail,dc=htw,dc=stura-dresden,dc=de";
uris = [
"ldap://auth.test.htw.stura-dresden.de:3389"
];
};
certificateScheme = "acme-nginx";
enableImap = true;
enableImapSsl = true;
enableManageSieve = true;
enableSubmission = true;
enableSubmissionSsl = true;
extraVirtualAliases = { };
lmtpSaveToDetailMailbox = "no"; # DOS potential
mailboxes = {
Drafts = {
auto = "subscribe";
specialUse = "Drafts";
};
Sent = {
auto = "subscribe";
specialUse = "Sent";
};
Spam = {
auto = "subscribe";
specialUse = "Junk";
};
Trash = {
auto = "subscribe";
specialUse = "Trash";
};
};
maxConnectionsPerUser = 10;
messageSizeLimit = 10 * 1000 * 1024; # 10 MiB
stateVersion = 3;
};
# services.dovecot2.mailLocation = lib.mkForce "maildir:/var/vmail/%n";
services.postfix =
let
submissionOptions = {
# hash:/etc/postfix/virtual,
smtpd_sender_login_maps = lib.mkForce "ldap:/run/postfix/ldap-sender-login-map.cf";
smtpd_client_restrictions = "permit_sasl_authenticated,reject";
};
in
{
masterConfig = {
submission = {
args = [ "-v" ];
};
submissions = {
args = [ "-v" ];
};
};
settings.main = {
unknown_local_recipient_reject_code = 550;
relay_domains = [
"hash:/var/lib/mailman/data/postfix_domains"
];
transport_maps = [
"hash:/var/lib/mailman/data/postfix_lmtp"
];
local_recipient_maps = [
"hash:/var/lib/mailman/data/postfix_lmtp"
];
};
# mapFiles = {
# "valias" = lib.mkForce "/var/lib/postfix/valias";
# "virtual" = lib.mkForce "/var/lib/postfix/virtual";
# };
submissionOptions = submissionOptions;
submissionsOptions = submissionOptions;
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "cert@stura.htw-dresden.de";
networking.firewall.allowedTCPPorts = [
25
80
443
597
];
system.stateVersion = "24.11";
}

View file

@ -0,0 +1,25 @@
ldap_passwd: ENC[AES256_GCM,data:adUZCZcYfoxBQm3e4YeeXcQJSZjB3+v2zSNy7q0Ao39aDQMH5H0w4o9MXTREkPHW53JejC2ivo8Zl3yUhkeYRw==,iv:XB25CmtUGf+PeSsHtr+CA/HIfZq1IrOBPPQD3/r6Kc4=,tag:A/WGViM/Ix7n6mhjnbCtZg==,type:str]
sops:
age:
- recipient: age156ak7kc79tuwpv0hk9atl5dg27jqs6ddfqxvr9m4twqgsr23lgvsdmyfpr
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsenVrS0tkTWZRY2xDZklO
WTcwaENIY2I1bTFGMEZZVzBoeUNrT2RESmhFCnZXU2M1SjlGQWo0OEp0TzI0c21u
UkNuNEdQQldQdy9uSzhveEM2eFZrRUkKLS0tIGV4S3lreHJPVS96VUZ6SXRaSklW
MUE4eXN0bkNkU0dCckppdldvV2V4dHcKdKh6ekq6hB5pCUAEPdASqsxqAKZDwzCv
NyS2jitHo9XBtMQVJg4PmNcoRs5XLdqy2tP8upnGelj0B/Q9D+dhag==
-----END AGE ENCRYPTED FILE-----
- recipient: age1qp7w80k3qtj79xsl0gwsfrkm037xrlnhm6th7tcyrvufh3szzp6s2pe7ra
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRVlVrdXhGMDRxMjhPb3Nv
c3lBS25OKzJIaWhHWHpKQXo0N1dFTnZLaHdnClVFVTFPVE1rNFVEclFVc3VjQVhu
SjF6Nnp6dE9oRUJYUVVnOWVpVE11WVkKLS0tIEJ0aVJzejROMHFPK1JQbkJjbUdi
bGU3WWhVMGJ2LzI4N2E1Zy9RNnJ2V2MK4UQPwE5GUVTGvnuZ9knQ+BHmzmRLA1V5
SinlJfHcs+9B7haHzAekDdNqZgEUh2tblabHqq/vNWzd0rWpK31Dww==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-21T17:05:32Z"
mac: ENC[AES256_GCM,data:A2tbQ8obTjzKeISAbDy0Sqi+WvpqZwkKSW2PmV67V2u/svNfC7C/ebNHp6p0I9N97y026WzboXnRwon7M1jaJ8dPn2hqGReZIIHW5w5rBK0uAeEH5wCMnNGslox4D7+L27zc4VZMK4tjFP6F7EKkq7TIjf+HzEYW+kpbqM2erVo=,iv:EKa3JBlDiU/FcF2T/hQtmaFlYiAbFIlytR3fSfJjVBc=,tag:B4N/4XxhdtNYKTRT1O+UyA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.1

View file

@ -143,6 +143,18 @@
httpPort = 80;
httpsPort = 443;
};
bbb = {
dest = "141.56.51.94";
domain = "bbb.htw.stura-dresden.de";
httpPort = 80;
httpsPort = 443;
};
bbb-test = {
dest = "141.56.51.94";
domain = "bbb.test.htw.stura-dresden.de";
httpPort = 80;
httpsPort = 443;
};
}
# zusätzlich zu den oben definierten wird hier noch ein redirect für jeden nginx virtualhost in diese flake generiert
// (builtins.foldl'