diff --git a/.github/workflows/fmt.yaml b/.github/workflows/fmt.yaml new file mode 100644 index 0000000..93d16c5 --- /dev/null +++ b/.github/workflows/fmt.yaml @@ -0,0 +1,27 @@ +name: main + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check-flake: + name: Nixpkgs Formatting + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install Nix + uses: cachix/install-nix-action@v18 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + - run: nix-channel --add https://nixos.org/channels/nixos-22.11 nixos + - run: nix-channel --update + - run: nix shell nixpkgs#nixpkgs-fmt -c nixpkgs-fmt . --check diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..bfb606f --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,33 @@ +name: main + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check-flake: + name: Check Flake + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install Nix + uses: cachix/install-nix-action@v18 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + - uses: cachix/cachix-action@v12 + with: + name: fruitbasket + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + extraPullNames: nix-community + + - run: nix build + + - run: nix flake check diff --git a/.gitignore b/.gitignore index 8cb727e..ee0c388 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -.qcow2 +*.qcow2 result diff --git a/.sops.yaml b/.sops.yaml index 13849d7..3738915 100755 --- a/.sops.yaml +++ b/.sops.yaml @@ -44,3 +44,16 @@ creation_rules: - *jonas age: - *test + - path_regex: secrets/admin\.yaml$ + key_groups: + - pgp: + - *bennofs + - *revol-xut + - *felix + - *simon + - *rouven + - *helene + - *fugi + - *emmanuel + - *joachim + - *jonas diff --git a/config/portunus_seeds.json b/config/portunus_seeds.json new file mode 100644 index 0000000..b73bf07 --- /dev/null +++ b/config/portunus_seeds.json @@ -0,0 +1,54 @@ +{ + "groups": [ + { + "name": "admins", + "long_name": "Portunus Admins", + "members": ["admin"], + "permissions": { + "portunus": { "is_admin": true }, + "ldap": { "can_read": true } + } + }, + { + "name": "ifsr", + "long_name": "Mitglieder des ifsr", + "members": [], + "permissions": { + "portunus": { "is_admin": false }, + "ldap": { "can_read": false } + } + }, + { + "name": "strukturer", + "long_name": "Strukturer des ifsr", + "members": [], + "permissions": { + "portunus": { "is_admin": false }, + "ldap": { "can_read": false } + } + }, + { + "name": "search", + "long_name": "LDAP search group", + "members": ["search"], + "permissions": { + "portunus": { "is_admin": false }, + "ldap": { "can_read": true } + } + } + ], + "users": [ + { + "login_name": "admin", + "given_name": "admin", + "family_name": "admin", + "password": { "from_command": ["/usr/bin/env", "cat", "/run/secrets/portunus_admin"] } + }, + { + "login_name": "search", + "given_name": "search", + "family_name": "search", + "password": { "from_command": ["/usr/bin/env", "cat", "/run/secrets/portunus_search"] } + } + ] +} diff --git a/flake.lock b/flake.lock index c2baba1..3650034 100644 --- a/flake.lock +++ b/flake.lock @@ -71,11 +71,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1673740915, - "narHash": "sha256-MMH8zONfqahgHly3K8/A++X34800rajA/XgZ2DzNL/M=", + "lastModified": 1676162277, + "narHash": "sha256-GK3cnvKNo1l0skGYXXiLJ/TLqdKyIYXd7jOlo0gN+Qw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7c65528c3f8462b902e09d1ccca23bb9034665c2", + "rev": "d863ca850a06d91365c01620dcac342574ecf46f", "type": "github" }, "original": { @@ -87,16 +87,16 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1672580127, - "narHash": "sha256-3lW3xZslREhJogoOkjeZtlBtvFMyxHku7I/9IVehhT8=", + "lastModified": 1676375384, + "narHash": "sha256-6HI3jZiuJX+KLz05cocYy2mBAWlISEKHU84ftYfxHZ8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0874168639713f547c05947c76124f78441ea46c", + "rev": "c43f676c938662072772339be6269226c77b51b8", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-22.05", + "ref": "nixos-22.11", "repo": "nixpkgs", "type": "github" } @@ -116,11 +116,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1673752321, - "narHash": "sha256-EFfXY1ZHJq4FNaNQA9x0djtu/jiOhBbT0Xi+BT06cJw=", + "lastModified": 1676171095, + "narHash": "sha256-2laeSjBAAJ9e/C3uTIPb287iX8qeVLtWiilw1uxqG+A=", "owner": "Mic92", "repo": "sops-nix", - "rev": "e18eefd2b133a58309475298052c341c08470717", + "rev": "c5dab21d8706afc7ceb05c23d4244dcb48d6aade", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 48f499a..ef4e809 100755 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = github:nixos/nixpkgs/nixos-22.05; + nixpkgs.url = github:nixos/nixpkgs/nixos-22.11; sops-nix.url = github:Mic92/sops-nix; sops-nix.inputs.nixpkgs.follows = "nixpkgs"; fsr-infoscreen.url = github:fsr/infoscreen; @@ -56,15 +56,21 @@ modules = [ inputs.sops-nix.nixosModules.sops ./hosts/quitte/configuration.nix + ./modules/options.nix ./modules/base.nix ./modules/sops.nix - ./modules/keycloak.nix + ./modules/ldap.nix + # ./modules/keycloak.nix replaced by portunus + ./modules/mail.nix ./modules/nginx.nix - #./modules/hedgedoc.nix + ./modules/hedgedoc.nix ./modules/wiki.nix ./modules/stream.nix ./modules/nextcloud.nix + ./modules/matrix.nix { + fsr.enable_office_bloat = false; + fsr.domain = "staging.ifsr.de"; sops.defaultSopsFile = ./secrets/quitte.yaml; } ]; @@ -74,10 +80,11 @@ modules = [ inputs.sops-nix.nixosModules.sops ./hosts/quitte/configuration.nix + ./modules/options.nix ./modules/base.nix - ./modules/keycloak.nix + # ./modules/keycloak.nix replaced by portunus ./modules/nginx.nix - #./modules/hedgedoc.nix + ./modules/hedgedoc.nix ./modules/wiki.nix ./modules/stream.nix ./modules/vm.nix diff --git a/keys/ssh/joachim b/keys/ssh/joachim index dcf3458..218443f 100644 --- a/keys/ssh/joachim +++ b/keys/ssh/joachim @@ -1 +1 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC9akyIw74vwJ+8NM7um8NhTcMO7okD46jYkodHTLYe6Oj7QR/kHZD3VEu5Wy7mN33av0Pr6gqeqzPBmn1kxa+J0FnD83rjm989Z9mKbpjt1EtAd9WSlx1MmixHCSgTvHezaiUHmikWdUK3XLITZeGOM3fyDDV5Nqm1rvg5PMV2mAMP9j8T2RsWweGUgcoE8RIfH4WA6t1ZhP4Px01UWozFadeQ6qRG8hMfq3gWKC9lGLAlgUHqpUuNVJ8I45znR/UhrR+h4VF0mkCYFM3JP0z+o/KwDTfcLlRCMxcY3rpRvbrhmgkqHuPTqwQfiBxTyZvWb2IeCkKVFAk0WmFb1M4Elnk+tJrF2G7yzpyRNilin8/P7pQk7M3BAY2m1iZA6u7cBUxWFbzRqRluRR8R+HAk9hqjtr0XmMifq0K7ZX31u48Ss/lgNXa2KHHYeI79++mfELhIhNXAznkhIAT1PhRKeL12RtDVjzfEd1JbHR8hE1L/n0K3Hyfzw/bJXBAwJJ8= joach@DESKTOP-FOASM6G +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0FSJ1IFmAMaeuO/OcY56koVTO68DZOJkUpZvHKkGttCtSmio2w3lXoJG+X+950DdwZcgF9vf4/h8VjsmQdNg/ACQQM6gNwVk8p2arIbYy9M3s0FfHKoMkfMDlZ91Md0/9BtogFRQWCP9Og3vV7q63cPJldqk+gljp10OruxawjAc+myz2xUppjk3BWzoHX86lAtF2ggqY1HW9rloMqj0j1zdqyMkHzy4akJbE2NAekNsdz0dWKC3tTGuEXlisZiC1Q51S5JrZIsr8hZXdL2u/nThsveSPC/jW3tfQxthu7TFyLr5n5ms9S3s47TGdtUTKmkTXWvspAHD4EP2nHTniqnesVO3TE9lHiI++TBQAtrTp+Ivb6Fwv55fUH1V36tkaFfAuTJq0zHv7tYedqMdzfH99jsHb4hej6LWMJPaH1R/UyNOzJr+ac50C7vFO+j4UKiu0vFRebnID7nLBY7vl3n5bmX1FjfD9axHncIranI4Lyt5RMxueR11IHIDqEEE= joachim@nixos diff --git a/modules/hedgedoc.nix b/modules/hedgedoc.nix index f85d2a7..3c8b776 100644 --- a/modules/hedgedoc.nix +++ b/modules/hedgedoc.nix @@ -1,6 +1,6 @@ { config, pkgs, lib, ... }: let - domain = "pad.quitte.tassilo-tanneberger.de"; + domain = "pad.${config.fsr.domain}"; in { services = { @@ -19,7 +19,7 @@ in hedgedoc = { enable = true; - settings = { + configuration = { port = 3002; domain = "${domain}"; protocolUseSSL = true; @@ -44,7 +44,7 @@ in enableACME = true; forceSSL = true; locations."/" = { - proxyPass = "http://127.0.0.1:${toString config.services.hedgedoc.settings.port}"; + proxyPass = "http://127.0.0.1:${toString config.services.hedgedoc.configuration.port}"; proxyWebsockets = true; }; }; diff --git a/modules/ldap.nix b/modules/ldap.nix new file mode 100644 index 0000000..dd459e0 --- /dev/null +++ b/modules/ldap.nix @@ -0,0 +1,103 @@ +{ config, ... }: +let + domain = "auth.${config.fsr.domain}"; + + portunusUser = "portunus"; + portunusGroup = "portunus"; + + ldapUser = "openldap"; + ldapGroup = "openldap"; +in +{ + sops.secrets.unix_ldap_search = { + key = "portunus_search"; + owner = config.systemd.services.nslcd.serviceConfig.User; + }; + + + users.users."${portunusUser}" = { + isSystemUser = true; + group = "${portunusGroup}"; + }; + + users.groups."${portunusGroup}" = { + name = "${portunusGroup}"; + members = [ "${portunusUser}" ]; + }; + + users.users."${ldapUser}" = { + isSystemUser = true; + group = "${ldapGroup}"; + }; + + users.groups."${ldapGroup}" = { + name = "${ldapGroup}"; + members = [ "${ldapUser}" ]; + }; + + sops.secrets = { + "portunus_admin" = { + owner = "${portunusUser}"; + group = "${portunusGroup}"; + }; + "portunus_search" = { + owner = "${portunusUser}"; + group = "${portunusGroup}"; + }; + }; + + services.portunus = { + enable = true; + user = "${portunusUser}"; + group = "${portunusGroup}"; + domain = "${domain}"; + port = 8081; + + ldap = { + user = "${ldapUser}"; + group = "${ldapGroup}"; + + suffix = "dc=ifsr,dc=de"; + searchUserName = "search"; + + # disables port 389, use 636 with tls + # `portunus.domain` resolves to localhost + #tls = true; + }; + + seedPath = ../config/portunus_seeds.json; + }; + + #users.ldap = { + #enable = true; + #server = "ldap://localhost"; + #base = "${config.services.portunus.ldap.suffix}"; + #}; + users.ldap = + let + portunus = config.services.portunus; + base = "ou=users,${portunus.ldap.suffix}"; + in + { + enable = true; + server = "ldap://localhost"; + base = base; + bind = { + distinguishedName = "uid=${portunus.ldap.searchUserName},${base}"; + passwordFile = config.sops.secrets.unix_ldap_search.path; + }; + daemon.enable = true; + }; + + + services.nginx = { + enable = true; + virtualHosts."${config.services.portunus.domain}" = { + forceSSL = true; + enableACME = true; + locations = { + "/".proxyPass = "http://localhost:${toString config.services.portunus.port}"; + }; + }; + }; +} diff --git a/modules/mail.nix b/modules/mail.nix new file mode 100644 index 0000000..14c009c --- /dev/null +++ b/modules/mail.nix @@ -0,0 +1,165 @@ +{ config, pkgs, ... }: +let + hostname = "mail.${config.fsr.domain}"; + domain = config.fsr.domain; + rspamd-domain = "rspamd.${config.fsr.domain}"; + # brauchen wir das überhaupt? + #ldap-aliases = pkgs.writeText "ldap-aliases.cf" '' + #server_host = ldap://localhost + #search_base = ou=mail, dc=ifsr, dc=de + #''; + dovecot-ldap-args = pkgs.writeText "ldap-args" '' + uris = ldap://localhost + dn = uid=search, ou=users, dc=ifsr, dc=de + auth_bind = yes + dnpass = $(${pkgs.coreutils}/bin/cat ${config.sops.secrets."portunus_search".path}) + + ldap_version = 3 + scope = subtree + base = dc=ifsr, dc=de + user_filter = (&(ou=mail)(uid=%n)) + pass_filter = (&(ou=mail)(uid=%n)) + ''; +in +{ + sops.secrets."rspamd-password".owner = config.users.users.rspamd.name; + + networking.firewall.allowedTCPPorts = [ 25 465 993 ]; + + services = { + postfix = { + enable = true; + hostname = "${hostname}"; + domain = "${domain}"; + relayHost = ""; + origin = "${domain}"; + destination = [ "${hostname}" "${domain}" "localhost" ]; + sslCert = "/var/lib/acme/${hostname}/fullchain.pem"; + sslKey = "/var/lib/acme/${hostname}/key.pem"; + config = { + smtpd_recipient_restrictions = [ + "reject_unauth_destination" + "permit_sasl_authenticated" + "permit_mynetworks" + ]; + #alias_maps = [ "ldap:${ldap-aliases}" ]; + smtpd_sasl_auth_enable = true; + smtpd_sasl_path = "/var/lib/postfix/auth"; + virtual_mailbox_base = "/var/lib/mail"; + }; + }; + dovecot2 = { + enable = true; + enableImap = true; + enableQuota = false; + sslServerCert = "/var/lib/acme/${hostname}/fullchain.pem"; + sslServerKey = "/var/lib/acme/${hostname}/key.pem"; + mailboxes = { + Spam = { + auto = "create"; + specialUse = "Junk"; + }; + Sent = { + auto = "create"; + specialUse = "Sent"; + }; + Drafts = { + auto = "create"; + specialUse = "Drafts"; + }; + Trash = { + auto = "create"; + specialUse = "Trash"; + }; + }; + extraConfig = '' + mail_location = maildir:/var/lib/mail/%u + passdb { + driver = ldap + args = ${dovecot-ldap-args} + } + userdb { + driver = ldap + args = ${dovecot-ldap-args} + } + service auth { + unix_listener /var/lib/postfix/auth { + group = postfix + mode = 0660 + user = postfix + } + } + ''; + }; + rspamd = { + enable = true; + postfix.enable = true; + locals = { + "worker-controller.inc".source = config.sops.secrets."rspamd-password".path; + "redis.conf".text = '' + read_servers = "127.0.0.1"; + write_servers = "127.0.0.1"; + ''; + "dkim_signing.conf".text = '' + path = "/var/lib/rspamd/dkim/$domain.$selector.key"; + selector = "quitte"; + sign_authenticated = true; + use_domain = "header"; + ''; + }; + }; + redis = { + vmOverCommit = true; + servers.rspamd = { + enable = true; + port = 6379; + }; + }; + nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + virtualHosts."${hostname}" = { + forceSSL = true; + enableACME = true; + }; + virtualHosts."${rspamd-domain}" = { + forceSSL = true; + enableACME = true; + locations = { + "/" = { + proxyPass = "http://127.0.0.1:11334"; + proxyWebsockets = true; + }; + }; + }; + }; + }; +} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/matrix.nix b/modules/matrix.nix new file mode 100644 index 0000000..82cfa0f --- /dev/null +++ b/modules/matrix.nix @@ -0,0 +1,141 @@ +{ config, pkgs, lib, ... }: +let + domainServer = "matrix.${config.fsr.domain}"; + domainClient = "chat.${config.fsr.domain}"; + + clientConfig = { + "m.homeserver" = { + base_url = "https://${domainServer}:443"; + server_name = domainServer; + }; + }; + serverConfig = { + "m.server" = "${domainServer}:443"; + }; + + mkWellKnown = data: '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON data}'; + ''; + + # build ldap3 plugin from git because it's very outdated in nixpkgs + matrix-synapse-ldap3 = pkgs.python3.pkgs.callPackage ../pkgs/matrix-synapse-ldap3.nix { }; + # matrix-synapse-ldap3 = config.services.matrix-synapse.package.plugins.matrix-synapse-ldap3; +in +{ + sops.secrets.matrix_ldap_search = { + key = "portunus_search"; + owner = config.systemd.services.matrix-synapse.serviceConfig.User; + }; + + services = { + postgresql = { + enable = true; + ensureUsers = [{ + name = "matrix-synapse"; + }]; + }; + + nginx = { + recommendedProxySettings = true; + virtualHosts = { + # synapse + "${domainServer}" = { + enableACME = true; + forceSSL = true; + + # homeserver discovery + locations."= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig; + locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig; + + # 404 on / + locations."/".extraConfig = "return 404;"; + + # proxy to synapse + locations."/_matrix".proxyPass = "http://[::1]:8008"; + locations."/_synapse/client".proxyPass = "http://[::1]:8008"; + }; + + # element + "${domainClient}" = { + enableACME = true; + forceSSL = true; + + root = pkgs.element-web.override { + conf = { + default_server_config = clientConfig; + disable_3pid_login = true; + }; + }; + }; + }; + }; + + matrix-synapse = { + enable = true; + + plugins = [ matrix-synapse-ldap3 ]; + + settings = { + server_name = domainServer; + + listeners = [{ + port = 8008; + bind_addresses = [ "::1" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = [{ + names = [ "client" "federation" ]; + compress = false; + }]; + }]; + }; + + extraConfigFiles = [ + (pkgs.writeTextFile { + name = "matrix-synapse-extra-config.yml"; + text = let portunus = config.services.portunus; in '' + modules: + - module: ldap_auth_provider.LdapAuthProviderModule + config: + enabled: true + # have to use fqdn here for tls (still connects to localhost) + uri: ldaps://${portunus.domain}:636 + base: ou=users,${portunus.ldap.suffix} + # taken from kaki config + attributes: + uid: uid + mail: uid + name: cn + bind_dn: uid=search,ou=users,${portunus.ldap.suffix} + bind_password_file: ${config.sops.secrets.matrix_ldap_search.path} + ''; + }) + ]; + }; + }; + + systemd.services.matrix-synapse.after = [ "matrix-synapse-pgsetup.service" ]; + + systemd.services.matrix-synapse-pgsetup = { + description = "Prepare Synapse postgres database"; + wantedBy = [ "multi-user.target" ]; + after = [ "networking.target" "postgresql.service" ]; + serviceConfig.Type = "oneshot"; + + path = [ pkgs.sudo config.services.postgresql.package ]; + + # create database for synapse. will silently fail if it already exists + script = '' + sudo -u ${config.services.postgresql.superUser} psql <