{ config, lib, pkgs, ... }: let svc_domain = "nextcloud.${config.mine.shared.settings.domain}"; # place data into own zfs dataset stateDir = config.mine.zfsMounts."rpool/safe/svcs/nextcloud"; # helper variables nextcloud_user = config.users.users.nextcloud.name; nextcloud_group = config.users.groups.nextcloud.name; AUTHELIA_AUTH_NAME = "nextcloud"; # occ bin occ = config.services.nextcloud.occ + "/bin/nextcloud-occ"; # script for setting up ldap for nextcloud # - ensure user_ldap app is intalled # - ensure user_ldap app is enabled # - delete all ldap configurations # - add new configuration with our settings nextcloudSetupLdap = let ldap_settings = { ldapHost = "localhost"; ldapPort = 3890; ldapAgentName = config.mine.shared.settings.ldap.bind_dn; # ldapAgentPassword = "n$dYTi7@!3v#sTbF2AV7mW7szS2Z$oFV"; # EDIT: Base DN ldapBase = config.mine.shared.settings.ldap.dc; ldapBaseUsers = config.mine.shared.settings.ldap.dc; ldapBaseGroups = config.mine.shared.settings.ldap.dc; ldapLoginFilter = "(&(objectclass=person)(${config.mine.shared.settings.ldap.attr.uid}=%uid))"; # EDIT: nextcloud_users group, contains the users who can login to Nextcloud ldapUserFilter = "(&(objectclass=person)${config.mine.shared.settings.ldap.user_filter})"; ldapUserFilterObjectclass = "person"; ldapGroupFilter = "(&(objectclass=groupOfUniqueNames)(|(cn=${config.mine.shared.settings.ldap.groups.admin})(cn=${config.mine.shared.settings.ldap.groups.member})))"; ldapGroupFilterGroups = "admin;user"; ldapGroupFilterObjectclass = "groupOfUniqueNames"; ldapGroupMemberAssocAttr = "uniqueMember"; ldapEmailAttribute = config.mine.shared.settings.ldap.attr.email; ldapUserFilterMode = 1; ldapExpertUsernameAttr = config.mine.shared.settings.ldap.attr.uid; ldapConfigurationActive = 1; }; ldap_commands = lib.mapAttrsToList (n: v: "${occ} ldap:set-config $NEW_CONFIG_ID ${n} '${builtins.toString v}'") ldap_settings; in pkgs.writeShellScript "nextcloud-add-ldap.sh" '' # enable ldap service, remove config and create new empty one ${occ} app:install user_ldap ${occ} app:enable user_ldap # create new empty config NEW_CONFIG_ID="$(${occ} ldap:create-empty-config -p)" # setup ldap password BIND_USERPASS="$(cat $CREDENTIALS_DIRECTORY/lldap-bind-user-pass)" ${occ} ldap:set-config $NEW_CONFIG_ID ldapAgentPassword "$BIND_USERPASS" # set settings ${builtins.concatStringsSep "\n" ldap_commands} # delete all other configurations CONFIGS=$(${occ} ldap:show-config --output=json | ${pkgs.jq}/bin/jq -r '. | keys | .[]') echo "$CONFIGS" | while read CONFIG_ID; do if [[ "$NEW_CONFIG_ID" != "$CONFIG_ID" ]]; then ${occ} ldap:delete-config "$CONFIG_ID" fi done # promote ldap admin group to admins ${occ} ldap:promote-group ${config.mine.shared.settings.ldap.groups.admin} || true ''; # script for resetting nextcloud admin password on each startup nextcloudSetupAdmin = pkgs.writeShellScript "nextcloud-setup-admin.sh" '' # reset admin password on each start export OC_PASS="$(cat $CREDENTIALS_DIRECTORY/nextcloud-admin-pass)" ${occ} user:resetpassword --password-from-env -- ${config.services.nextcloud.config.adminuser} ''; # script for setting encryption for nextcloud # - ensure encryption app is intalled # - ensure encryption app is enabled # - enable encryption # - ensure all files are encrypted nextcloudSetupEncryption = pkgs.writeShellScript "nextcloud-setup-encryption.sh" '' # enable encryption ${occ} app:install encryption ${occ} app:enable encryption ${occ} encryption:enable echo "Ensuring everything is encrypted!" printf "y\n" | ${occ} encryption:encrypt-all ''; in { services.nextcloud = { enable = true; package = pkgs.nextcloud29; datadir = stateDir; config.adminpassFile = config.age.secrets.nextcloud-admin-pass.path; hostName = svc_domain; https = true; configureRedis = true; # apps extraAppsEnable = true; extraApps = { inherit (config.services.nextcloud.package.packages.apps) contacts calendar tasks; oidc_login = let version = "3.1.1"; # TODO(eyJhb): add to niv in pkgs.fetchNextcloudApp { sha256 = "sha256-EVHDDFtz92lZviuTqr+St7agfBWok83HpfuL6DFCoTE="; url = "https://github.com/pulsejet/nextcloud-oidc-login/releases/download/v${version}/oidc_login.tar.gz"; license = "agpl3Only"; }; }; # secrets secretFile = config.age.secrets.nextcloud-secrets.path; # settings settings = { # open connect/oidc oidc_login_provider_url = "https://auth.fricloud.dk"; oidc_login_client_id = AUTHELIA_AUTH_NAME; # oidc_login_client_secret = ""; oidc_login_proxy_ldap = true; # SUPER IMPORTANT! oidc_login_auto_redirect = true; oidc_login_button_text = "Log in with Authelia"; oidc_login_use_id_token = true; oidc_login_attributes = { id = "preferred_username"; ldap_uid = "preferred_username"; name = "name"; mail = "email"; groups = "groups"; }; oidc_login_scope = "openid profile email groups"; oidc_login_code_challenge_method = "S256"; }; }; systemd.services.nextcloud-setup = { # runs this after all the main nextcloud-setup stuff postStop = lib.mkAfter '' ${nextcloudSetupLdap} ${nextcloudSetupAdmin} ${nextcloudSetupEncryption} ''; # setup credentials for service serviceConfig.LoadCredential = [ "lldap-bind-user-pass:${config.age.secrets.lldap-bind-user-pass.path}" "nextcloud-admin-pass:${config.age.secrets.nextcloud-admin-pass.path}" ]; }; # ensure that nextcloud can access stateDir systemd.tmpfiles.rules = [ "Z ${stateDir} 6770 ${nextcloud_user} ${nextcloud_group} -" ]; # setup authelia for nextcloud services.authelia.instances.main.settings.identity_providers.oidc.clients = [{ id = AUTHELIA_AUTH_NAME; description = "Nextcloud"; # authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986 secret = "$pbkdf2-sha512$310000$kLNQ/1A.uasSN4g8q94jUQ$8OKNUNNumHCh8dVG5/QWys7u.y1guqFXlrL.bMm7/HKTsWhpib/W.8qlU6VU7V1Be/h14Y.fJi3RLvbkEdo2kA"; public = false; authorization_policy = "one_factor"; # require_pkce = true; # pkce_challenge_method = "S256"; redirect_uris = [ "https://${svc_domain}/apps/oidc_login/oidc" ]; scopes = [ "openid" "profile" "email" "groups" ]; userinfo_signing_algorithm = "none"; }]; services.nginx.virtualHosts."${svc_domain}" = { forceSSL = true; enableACME = true; }; age.secrets = { nextcloud-admin-pass.owner = nextcloud_user; nextcloud-secrets.owner = nextcloud_user; }; mine.shared.meta.nextcloud = { name = "Nextcloud"; description = "We host our own Nextcloud for having a central place for personal files, etc. (all files are encrypted using the standard encryption module)! Login using your credentials."; url = "https://${svc_domain}"; package = let pkg = config.services.nextcloud.package; in { name = pkg.pname; version = pkg.version; meta = pkg.meta; }; }; }