diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 120000 index ca60d86..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1 +0,0 @@ -/nix/store/1w2s62i701n28sj08gn1445qr4v3vijp-pre-commit-config.json \ No newline at end of file diff --git a/.sops.yaml b/.sops.yaml index fd76726..66644ad 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -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 diff --git a/flake.lock b/flake.lock index c4d6b5e..9ed7807 100644 --- a/flake.lock +++ b/flake.lock @@ -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" diff --git a/flake.nix b/flake.nix index c4a2649..53bdc81 100644 --- a/flake.nix +++ b/flake.nix @@ -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; }; } diff --git a/hosts/auth/authentik.nix b/hosts/auth/authentik.nix new file mode 100644 index 0000000..4ae125b --- /dev/null +++ b/hosts/auth/authentik.nix @@ -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; + ''; + }; + }; + }; + }; +} + diff --git a/hosts/auth/default.nix b/hosts/auth/default.nix new file mode 100644 index 0000000..0d135dc --- /dev/null +++ b/hosts/auth/default.nix @@ -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"; +} diff --git a/hosts/auth/secrets.sops.yml b/hosts/auth/secrets.sops.yml new file mode 100644 index 0000000..45c456a --- /dev/null +++ b/hosts/auth/secrets.sops.yml @@ -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 diff --git a/hosts/mail/default.nix b/hosts/mail/default.nix new file mode 100644 index 0000000..c4bf5f3 --- /dev/null +++ b/hosts/mail/default.nix @@ -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"; + +} + diff --git a/hosts/mail/secrets.sops.yml b/hosts/mail/secrets.sops.yml new file mode 100644 index 0000000..b9d0b6a --- /dev/null +++ b/hosts/mail/secrets.sops.yml @@ -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 diff --git a/hosts/proxy/default.nix b/hosts/proxy/default.nix index 897e27e..3a727f3 100644 --- a/hosts/proxy/default.nix +++ b/hosts/proxy/default.nix @@ -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'