diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 89677970dd9..460c74f857c 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -847,6 +847,7 @@ ./services/web-apps/matomo.nix ./services/web-apps/moinmoin.nix ./services/web-apps/restya-board.nix + ./services/web-apps/sogo.nix ./services/web-apps/tt-rss.nix ./services/web-apps/trac.nix ./services/web-apps/trilium.nix diff --git a/nixos/modules/services/web-apps/sogo.nix b/nixos/modules/services/web-apps/sogo.nix new file mode 100644 index 00000000000..5f30124dd68 --- /dev/null +++ b/nixos/modules/services/web-apps/sogo.nix @@ -0,0 +1,272 @@ +{ config, pkgs, lib, ... }: with lib; let + cfg = config.services.sogo; + + preStart = pkgs.writeShellScriptBin "sogo-prestart" '' + touch /etc/sogo/sogo.conf + chown sogo:sogo /etc/sogo/sogo.conf + chmod 640 /etc/sogo/sogo.conf + + ${if (cfg.configReplaces != {}) then '' + # Insert secrets + ${concatStringsSep "\n" (mapAttrsToList (k: v: ''export ${k}="$(cat "${v}" | tr -d '\n')"'') cfg.configReplaces)} + + ${pkgs.perl}/bin/perl -p ${concatStringsSep " " (mapAttrsToList (k: v: '' -e 's/${k}/''${ENV{"${k}"}}/g;' '') cfg.configReplaces)} /etc/sogo/sogo.conf.raw > /etc/sogo/sogo.conf + '' else '' + cp /etc/sogo/sogo.conf.raw /etc/sogo/sogo.conf + ''} + ''; + +in { + options.services.sogo = with types; { + enable = mkEnableOption "SOGo groupware"; + + vhostName = mkOption { + description = "Name of the nginx vhost"; + type = str; + default = "sogo"; + }; + + timezone = mkOption { + description = "Timezone of your SOGo instance"; + type = str; + example = "America/Montreal"; + }; + + language = mkOption { + description = "Language of SOGo"; + type = str; + default = "English"; + }; + + ealarmsCredFile = mkOption { + description = "Optional path to a credentials file for email alarms"; + type = nullOr str; + default = null; + }; + + configReplaces = mkOption { + description = '' + Replacement-filepath mapping for sogo.conf. + Every key is replaced with the contents of the file specified as value. + + In the example, every occurence of LDAP_BINDPW will be replaced with the text of the + specified file. + ''; + type = attrsOf str; + default = {}; + example = { + LDAP_BINDPW = "/var/lib/secrets/sogo/ldappw"; + }; + }; + + extraConfig = mkOption { + description = "Extra sogo.conf configuration lines"; + type = lines; + default = ""; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.sogo ]; + + environment.etc."sogo/sogo.conf.raw".text = '' + { + // Mandatory parameters + SOGoTimeZone = "${cfg.timezone}"; + SOGoLanguage = "${cfg.language}"; + // Paths + WOSendMail = "/run/wrappers/bin/sendmail"; + SOGoMailSpoolPath = "/var/lib/sogo/spool"; + SOGoZipPath = "${pkgs.zip}/bin/zip"; + // Enable CSRF protection + SOGoXSRFValidationEnabled = YES; + // Remove dates from log (jornald does that) + NGLogDefaultLogEventFormatterClass = "NGLogEventFormatter"; + // Extra config + ${cfg.extraConfig} + } + ''; + + systemd.services.sogo = { + description = "SOGo groupware"; + after = [ "postgresql.service" "mysql.service" "memcached.service" "openldap.service" "dovecot2.service" ]; + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ config.environment.etc."sogo/sogo.conf.raw".source ]; + + environment.LDAPTLS_CACERT = "/etc/ssl/certs/ca-certificates.crt"; + + serviceConfig = { + Type = "forking"; + ExecStartPre = "+" + preStart + "/bin/sogo-prestart"; + ExecStart = "${pkgs.sogo}/bin/sogod -WOLogFile - -WOPidFile /run/sogo/sogo.pid"; + + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RuntimeDirectory = "sogo"; + StateDirectory = "sogo/spool"; + + User = "sogo"; + Group = "sogo"; + + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + PrivateUsers = true; + MemoryDenyWriteExecute = true; + SystemCallFilter = "@basic-io @file-system @network-io @system-service @timer"; + SystemCallArchitectures = "native"; + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + }; + }; + + systemd.services.sogo-tmpwatch = { + description = "SOGo tmpwatch"; + + startAt = [ "hourly" ]; + script = '' + SOGOSPOOL=/var/lib/sogo/spool + + find "$SOGOSPOOL" -type f -user sogo -atime +23 -delete > /dev/null + find "$SOGOSPOOL" -mindepth 1 -type d -user sogo -empty -delete > /dev/null + ''; + + serviceConfig = { + Type = "oneshot"; + + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + StateDirectory = "sogo/spool"; + + User = "sogo"; + Group = "sogo"; + + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + PrivateUsers = true; + PrivateNetwork = true; + SystemCallFilter = "@basic-io @file-system @system-service"; + SystemCallArchitectures = "native"; + RestrictAddressFamilies = ""; + }; + }; + + systemd.services.sogo-ealarms = { + description = "SOGo email alarms"; + + after = [ "postgresql.service" "mysqld.service" "memcached.service" "openldap.service" "dovecot2.service" "sogo.service" ]; + restartTriggers = [ config.environment.etc."sogo/sogo.conf.raw".source ]; + + startAt = [ "minutely" ]; + + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.sogo}/bin/sogo-ealarms-notify${optionalString (cfg.ealarmsCredFile != null) " -p ${cfg.ealarmsCredFile}"}"; + + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + StateDirectory = "sogo/spool"; + + User = "sogo"; + Group = "sogo"; + + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + PrivateUsers = true; + MemoryDenyWriteExecute = true; + SystemCallFilter = "@basic-io @file-system @network-io @system-service"; + SystemCallArchitectures = "native"; + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + }; + }; + + # nginx vhost + services.nginx.virtualHosts."${cfg.vhostName}" = { + locations."/".extraConfig = '' + rewrite ^ https://$server_name/SOGo; + allow all; + ''; + + # For iOS 7 + locations."/principals/".extraConfig = '' + rewrite ^ https://$server_name/SOGo/dav; + allow all; + ''; + + locations."^~/SOGo".extraConfig = '' + proxy_pass http://127.0.0.1:20000; + proxy_redirect http://127.0.0.1:20000 default; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header x-webobjects-server-protocol HTTP/1.0; + proxy_set_header x-webobjects-remote-host 127.0.0.1; + proxy_set_header x-webobjects-server-port $server_port; + proxy_set_header x-webobjects-server-name $server_name; + proxy_set_header x-webobjects-server-url $scheme://$host; + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + client_max_body_size 50m; + client_body_buffer_size 128k; + break; + ''; + + locations."/SOGo.woa/WebServerResources/".extraConfig = '' + alias ${pkgs.sogo}/lib/GNUstep/SOGo/WebServerResources/; + allow all; + ''; + + locations."/SOGo/WebServerResources/".extraConfig = '' + alias ${pkgs.sogo}/lib/GNUstep/SOGo/WebServerResources/; + allow all; + ''; + + locations."~ ^/SOGo/so/ControlPanel/Products/([^/]*)/Resources/(.*)$".extraConfig = '' + alias ${pkgs.sogo}/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; + ''; + + locations."~ ^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\\.(jpg|png|gif|css|js)$".extraConfig = '' + alias ${pkgs.sogo}/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; + ''; + }; + + # User and group + users.groups.sogo = {}; + users.users.sogo = { + group = "sogo"; + isSystemUser = true; + description = "SOGo service user"; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 0acded892c7..942cee04abc 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -292,6 +292,7 @@ in slurm = handleTest ./slurm.nix {}; smokeping = handleTest ./smokeping.nix {}; snapper = handleTest ./snapper.nix {}; + sogo = handleTest ./sogo.nix {}; solr = handleTest ./solr.nix {}; spacecookie = handleTest ./spacecookie.nix {}; spike = handleTest ./spike.nix {}; diff --git a/nixos/tests/sogo.nix b/nixos/tests/sogo.nix new file mode 100644 index 00000000000..016331a9eed --- /dev/null +++ b/nixos/tests/sogo.nix @@ -0,0 +1,58 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "sogo"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ajs124 das_j ]; + }; + + nodes = { + sogo = { config, pkgs, ... }: { + services.nginx.enable = true; + + services.mysql = { + enable = true; + package = pkgs.mysql; + ensureDatabases = [ "sogo" ]; + ensureUsers = [{ + name = "sogo"; + ensurePermissions = { + "sogo.*" = "ALL PRIVILEGES"; + }; + }]; + }; + + services.sogo = { + enable = true; + timezone = "Europe/Berlin"; + extraConfig = '' + WOWorkersCount = 1; + + SOGoUserSources = ( + { + type = sql; + userPasswordAlgorithm = md5; + viewURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_users"; + canAuthenticate = YES; + id = users; + } + ); + + SOGoProfileURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_user_profile"; + OCSFolderInfoURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_folder_info"; + OCSSessionsFolderURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_sessions_folder"; + OCSEMailAlarmsFolderURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_alarms_folder"; + OCSStoreURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_store"; + OCSAclURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_acl"; + OCSCacheFolderURL = "mysql://sogo@%2Frun%2Fmysqld%2Fmysqld.sock/sogo/sogo_cache_folder"; + ''; + }; + }; + }; + + testScript = '' + start_all() + sogo.wait_for_unit("multi-user.target") + sogo.wait_for_open_port(20000) + sogo.wait_for_open_port(80) + sogo.succeed("curl -sSfL http://sogo/SOGo") + ''; +}) diff --git a/pkgs/desktops/gnustep/libobjc2/default.nix b/pkgs/desktops/gnustep/libobjc2/default.nix index 10c10c33a95..0124e91a9b7 100644 --- a/pkgs/desktops/gnustep/libobjc2/default.nix +++ b/pkgs/desktops/gnustep/libobjc2/default.nix @@ -2,13 +2,13 @@ stdenv.mkDerivation rec { pname = "libobjc2"; - version = "2.0"; + version = "1.9"; src = fetchFromGitHub { owner = "gnustep"; repo = "libobjc2"; rev = "v${version}"; - sha256 = "1b4h0a4pqr8j6300qr2wmi33r7ysvp705gs0ypx69hbmifln0mlf"; + sha256 = "00pscl3ly3rv6alf9vk70kxnnxq2rfgpc1ylcv6cgjs9jxdnrqmn"; }; nativeBuildInputs = [ cmake ]; diff --git a/pkgs/development/libraries/sope/default.nix b/pkgs/development/libraries/sope/default.nix new file mode 100644 index 00000000000..9d0b3bf76b3 --- /dev/null +++ b/pkgs/development/libraries/sope/default.nix @@ -0,0 +1,46 @@ +{ gnustep, lib, fetchFromGitHub , libxml2, openssl_1_1 +, openldap, mysql, libmysqlclient, postgresql }: with lib; gnustep.stdenv.mkDerivation rec { + pname = "sope"; + version = "4.3.2"; + + src = fetchFromGitHub { + owner = "inverse-inc"; + repo = pname; + rev = "SOPE-${version}"; + sha256 = "0ny1ihx38gd25w8f3dfybyswvyjfljvb2fhfmkajgg6hhjrkfar2"; + }; + + nativeBuildInputs = [ gnustep.make ]; + buildInputs = flatten ([ gnustep.base libxml2 openssl_1_1 ] + ++ optional (openldap != null) openldap + ++ optionals (mysql != null) [ libmysqlclient mysql ] + ++ optional (postgresql != null) postgresql); + + postPatch = '' + # Exclude NIX_ variables + sed -i 's/grep GNUSTEP_/grep ^GNUSTEP_/g' configure + ''; + + preConfigure = '' + export DESTDIR="$out" + ''; + + configureFlags = [ "--prefix=" "--disable-debug" "--enable-xml" "--with-ssl=ssl" ] + ++ optional (openldap != null) "--enable-openldap" + ++ optional (mysql != null) "--enable-mysql" + ++ optional (postgresql != null) "--enable-postgresql"; + + # Yes, this is ugly. + preFixup = '' + cp -rlPa $out/nix/store/*/* $out + rm -rf $out/nix/store + ''; + + meta = { + description = "SOPE is an extensive set of frameworks which form a complete Web application server environment"; + license = licenses.publicDomain; + homepage = "https://github.com/inverse-inc/sope"; + platforms = platforms.linux; + maintainers = with maintainers; [ ajs124 das_j ]; + }; +} diff --git a/pkgs/servers/web-apps/sogo/default.nix b/pkgs/servers/web-apps/sogo/default.nix new file mode 100644 index 00000000000..3e78b5d9d43 --- /dev/null +++ b/pkgs/servers/web-apps/sogo/default.nix @@ -0,0 +1,76 @@ +{ gnustep, lib, fetchFromGitHub, fetchpatch, makeWrapper, python2, lndir +, openssl_1_1, openldap, sope, libmemcached, curl }: with lib; gnustep.stdenv.mkDerivation rec { + pname = "SOGo"; + version = "4.3.2"; + + src = fetchFromGitHub { + owner = "inverse-inc"; + repo = pname; + rev = "SOGo-${version}"; + sha256 = "1xxad23a8zy6w850x5nrrf54db0x73lc9drmc5kpfk870fk2lmr0"; + }; + + nativeBuildInputs = [ gnustep.make makeWrapper python2 ]; + buildInputs = [ gnustep.base sope openssl_1_1 libmemcached (curl.override { openssl = openssl_1_1; }) ] + ++ optional (openldap != null) openldap; + + patches = [ + # TODO: take a closer look at other patches in https://sources.debian.org/patches/sogo/ and https://github.com/Skrupellos/sogo-patches + (fetchpatch { + url = "https://sources.debian.org/data/main/s/sogo/4.3.0-1/debian/patches/0005-Remove-build-date.patch"; + sha256 = "0lrh3bkfj3r0brahfkyb0g7zx7r2jjd5cxzjl43nqla0fs09wsh8"; + }) + ]; + + postPatch = '' + # Exclude NIX_ variables + sed -i 's/grep GNUSTEP_/grep ^GNUSTEP_/g' configure + + # Disable argument verification because $out is not a GNUStep prefix + sed -i 's/^validateArgs$//g' configure + + # Patch exception-generating python scripts + patchShebangs . + + # Move all GNUStep makefiles to a common directory + mkdir -p makefiles + cp -r {${gnustep.make},${sope}}/share/GNUstep/Makefiles/* makefiles + + # Modify the search path for GNUStep makefiles + find . -type f -name GNUmakefile -exec sed -i "s:\\$.GNUSTEP_MAKEFILES.:$PWD/makefiles:g" {} + + ''; + + configureFlags = [ "--disable-debug" "--with-ssl=ssl" ]; + + preFixup = '' + # Create gnustep.conf + mkdir -p $out/share/GNUstep + cp ${gnustep.make}/etc/GNUstep/GNUstep.conf $out/share/GNUstep/ + sed -i "s:${gnustep.make}:$out:g" $out/share/GNUstep/GNUstep.conf + + # Link in GNUstep base + ${lndir}/bin/lndir ${gnustep.base}/lib/GNUstep/ $out/lib/GNUstep/ + + # Link in sope + ${lndir}/bin/lndir ${sope}/ $out/ + + # sbin fixup + mkdir -p $out/bin + mv $out/sbin/* $out/bin + rmdir $out/sbin + + # Make sogo find its files + for bin in $out/bin/*; do + wrapProgram $bin --prefix LD_LIBRARY_PATH : $out/lib/sogo --prefix GNUSTEP_CONFIG_FILE : $out/share/GNUstep/GNUstep.conf + done + ''; + + meta = { + description = "SOGo is a very fast and scalable modern collaboration suite (groupware)"; + license = with licenses; [ gpl2 lgpl21 ]; + homepage = "https://sogo.nu/"; + platforms = platforms.linux; + maintainers = with maintainers; [ ajs124 das_j ]; + }; +} + diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 8c89ed18d83..9f979731a84 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -14612,6 +14612,8 @@ in sonic = callPackage ../development/libraries/sonic { }; + sope = callPackage ../development/libraries/sope { }; + soprano = callPackage ../development/libraries/soprano { }; soqt = callPackage ../development/libraries/soqt { }; @@ -16222,6 +16224,8 @@ in smcroute = callPackage ../servers/smcroute { }; + sogo = callPackage ../servers/web-apps/sogo { }; + spawn_fcgi = callPackage ../servers/http/spawn-fcgi { }; spring-boot-cli = callPackage ../development/tools/spring-boot-cli { };