diff --git a/options/services.nix b/options/services.nix
index 1a331a7..7a14828 100644
--- a/options/services.nix
+++ b/options/services.nix
@@ -9,5 +9,6 @@ in
forgejo.enable = mkEnableOption "forgejo";
kavita.enable = mkEnableOption "kavita";
samba.enable = mkEnableOption "samba";
+ nextcloud.enable = mkEnableOption "nextcloud";
};
}
diff --git a/profiles/homeserver.nix b/profiles/homeserver.nix
index b5c184e..eb6a956 100644
--- a/profiles/homeserver.nix
+++ b/profiles/homeserver.nix
@@ -33,6 +33,7 @@
forgejo.enable = true;
kavita.enable = true;
samba.enable = true;
+ nextcloud.enable = true;
};
};
}
diff --git a/secrets/nextcloud.yaml b/secrets/nextcloud.yaml
new file mode 100644
index 0000000..aaf73a1
--- /dev/null
+++ b/secrets/nextcloud.yaml
@@ -0,0 +1,22 @@
+nextcloud:
+ homeserver: ENC[AES256_GCM,data:ZX0LfvXQ1h1LInuvRajKpyhAP8AbyGbTs40=,iv:gpADsYG655zUdDC+j0idtVdMCmwjWSFhSJ2Us0BgBkM=,tag:JPHrZ0YZSEI+AqUm6Bb48Q==,type:str]
+sops:
+ kms: []
+ gcp_kms: []
+ azure_kv: []
+ hc_vault: []
+ age:
+ - recipient: age1kruum2varzua7w5n6n52vhwyek2arc685rhcwt0u7k2jf5mecsjslkl9ll
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPeWIrNllOYlhnd3FYZGls
+ TUdDaSs0RHhVMHBxRFJwSWFxS0N5WVUxYXhNCmRTdnNMQy9TMDd1MUxLYSt0OEs5
+ VTg4TnpqVUdEY1Ywa280NktQYnVVUDgKLS0tIExtUjZXdTViMklLYVFqOHBkQnBV
+ bDN6M1ZEK3ZsdW5ydHNIc282TVo4YVkKeOgUwUJanhOn034l2B6Xp0UxogGP0/US
+ Bl+Mt+MYolkkNo3CT6w1bmsDXEDOb1Za8lmsMM1OH13bVpSQ/ygVzQ==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2024-06-13T16:14:14Z"
+ mac: ENC[AES256_GCM,data:QbHr/M2lcH0RFMHA5w+cdft803ZeDLgD7k45/gtM+09PmmQG4UMoHns4PoSxBtHUQkjNtHo53jet+sduC5fuO4Rek8xNsgmoIk7Zd46UU8I4QLxGcXLLWyOvddvR2LH5JpQCXbfHULJBynZskBtTntPg2h9F13PuTehOM0lHykM=,iv:su2ZOOK2v8x9vLgCcGTUJTSREMcxCAQXvhC/FKMk4nU=,tag:t5G+vnMw2KgiWYpSqQN1Cw==,type:str]
+ pgp: []
+ unencrypted_suffix: _unencrypted
+ version: 3.8.1
diff --git a/system/services/default.nix b/system/services/default.nix
index a728034..ce6cefa 100644
--- a/system/services/default.nix
+++ b/system/services/default.nix
@@ -5,5 +5,6 @@
./cockpit.nix
./forgejo.nix
./samba.nix
+ ./nextcloud.nix
];
}
diff --git a/system/services/nextcloud-extras.nix b/system/services/nextcloud-extras.nix
new file mode 100644
index 0000000..9b67aaa
--- /dev/null
+++ b/system/services/nextcloud-extras.nix
@@ -0,0 +1,175 @@
+# Directly ripped from https://github.com/onny/nixos-nextcloud-testumgebung/blob/main/nextcloud-extras.nix
+
+{ config
+, lib
+, ...
+}:
+let
+
+ inherit
+ (lib)
+ optionalString
+ escapeShellArg
+ types
+ concatStringsSep
+ mapAttrsToList
+ mkIf
+ mkOption
+ mkDefault
+ mkForce
+ ;
+
+ cfg = config.services.nextcloud;
+ fpm = config.services.phpfpm.pools.nextcloud;
+ webserver = config.services.${cfg.webserver};
+
+in
+{
+
+ options = {
+ services.nextcloud = {
+
+ ensureUsers = mkOption {
+ default = { };
+ description = lib.mdDoc ''
+ List of user accounts which get automatically created if they don't
+ exist yet. This option does not delete accounts which are not listed
+ anymore.
+ '';
+ example = {
+ user1 = {
+ passwordFile = /secrets/user1-localhost;
+ email = "user1@localhost";
+ };
+ user2 = {
+ passwordFile = /secrets/user2-localhost;
+ email = "user2@localhost";
+ };
+ };
+ type = types.attrsOf (types.submodule {
+ options = {
+ passwordFile = mkOption {
+ type = types.path;
+ example = "/path/to/file";
+ default = null;
+ description = lib.mdDoc ''
+ Specifies the path to a file containing the
+ clear text password for the user.
+ '';
+ };
+ email = mkOption {
+ type = types.str;
+ example = "user1@localhost";
+ default = null;
+ };
+ };
+ });
+ };
+
+ webserver = mkOption {
+ type = types.enum [ "nginx" "caddy" ];
+ default = "nginx";
+ description = ''
+ Whether to use nginx or caddy for virtual host management.
+ Further nginx configuration can be done by adapting services.nginx.virtualHosts.<name>.
+ See for further information.
+ '';
+ };
+
+ };
+ };
+
+ config = mkIf cfg.enable {
+
+ systemd.services.nextcloud-ensure-users = {
+ enable = true;
+ script = ''
+ ${optionalString (cfg.ensureUsers != {}) ''
+ ${concatStringsSep "\n" (mapAttrsToList (name: cfg: ''
+ if ${config.services.nextcloud.occ}/bin/nextcloud-occ user:info "${name}" | grep "user not found"; then
+ export OC_PASS="$(cat ${escapeShellArg cfg.passwordFile})"
+ ${config.services.nextcloud.occ}/bin/nextcloud-occ user:add --password-from-env "${name}"
+ fi
+ if ! ${config.services.nextcloud.occ}/bin/nextcloud-occ user:info "${name}" | grep "user not found"; then
+ ${optionalString (cfg.email != null) ''
+ ${config.services.nextcloud.occ}/bin/nextcloud-occ user:setting "${name}" settings email "${cfg.email}"
+ ''}
+ fi
+ '') cfg.ensureUsers)}
+ ''}
+ '';
+ wantedBy = [ "multi-user.target" ];
+ after = [ "nextcloud-setup.service" ];
+ };
+
+ services.phpfpm.pools.nextcloud.settings = {
+ "listen.owner" = webserver.user;
+ "listen.group" = webserver.group;
+ };
+
+ users.groups.nextcloud.members = [ "nextcloud" webserver.user ];
+
+ services.nginx = lib.mkIf (cfg.webserver == "caddy") {
+ enable = mkForce false;
+ };
+
+ services.caddy = lib.mkIf (cfg.webserver == "caddy") {
+ enable = mkDefault true;
+ virtualHosts."${if cfg.https then "https" else "http"}://${cfg.hostName}" = {
+ extraConfig = ''
+ encode zstd gzip
+
+ root * ${config.services.nginx.virtualHosts.${cfg.hostName}.root}
+
+ redir /.well-known/carddav /remote.php/dav 301
+ redir /.well-known/caldav /remote.php/dav 301
+ redir /.well-known/* /index.php{uri} 301
+ redir /remote/* /remote.php{uri} 301
+
+ header {
+ Strict-Transport-Security max-age=31536000
+ Permissions-Policy interest-cohort=()
+ X-Content-Type-Options nosniff
+ X-Frame-Options SAMEORIGIN
+ Referrer-Policy no-referrer
+ X-XSS-Protection "1; mode=block"
+ X-Permitted-Cross-Domain-Policies none
+ X-Robots-Tag "noindex, nofollow"
+ -X-Powered-By
+ }
+
+ php_fastcgi unix/${fpm.socket} {
+ root ${config.services.nginx.virtualHosts.${cfg.hostName}.root}
+ env front_controller_active true
+ env modHeadersAvailable true
+ }
+
+ @forbidden {
+ path /build/* /tests/* /config/* /lib/* /3rdparty/* /templates/* /data/*
+ path /.* /autotest* /occ* /issue* /indie* /db_* /console*
+ not path /.well-known/*
+ }
+ error @forbidden 404
+
+ @immutable {
+ path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite
+ query v=*
+ }
+ header @immutable Cache-Control "max-age=15778463, immutable"
+
+ @static {
+ path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite
+ not query v=*
+ }
+ header @static Cache-Control "max-age=15778463"
+
+ @woff2 path *.woff2
+ header @woff2 Cache-Control "max-age=604800"
+
+ file_server
+ '';
+ };
+ };
+
+ };
+}
diff --git a/system/services/nextcloud.nix b/system/services/nextcloud.nix
new file mode 100644
index 0000000..a4979d3
--- /dev/null
+++ b/system/services/nextcloud.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+let
+ cfg = config.profile.services.nextcloud;
+in
+{
+ imports = [ ./nextcloud-extras.nix ];
+ config = lib.mkIf cfg.enable {
+ users.groups.nextcloud.members = [ config.profile.user.name ];
+ sops.secrets =
+ let
+ opts = {
+ owner = "nextcloud";
+ sopsFile = ../../secrets/nextcloud.yaml;
+ };
+ in
+ {
+ "nextcloud/homeserver" = opts;
+ };
+
+ # Do not set services.nextcloud.home. Issues with sandboxing nature of NixOS.
+ # Instead uses bind mount and fstab to mount seeked directory to /var/lib/nextcloud.
+ fileSystems."/nas/nextcloud" = {
+ device = "/var/lib/nextcloud";
+ fsType = "none";
+ options = [ "bind" ];
+ };
+ services.nextcloud =
+ let
+ secrets = config.sops.secrets;
+ in
+ {
+ enable = true;
+ https = true;
+ webserver = "caddy";
+ hostName = "nextcloud.tigor.web.id"; # The nextcloud-extras will ensure Caddy to take care of this.
+ config = {
+ adminuser = "homeserver";
+ adminpassFile = secrets."nextcloud/homeserver".path;
+ };
+ };
+ };
+}