diff --git a/hosts/glyph/secrets/filebrowser-env.age b/hosts/glyph/secrets/filebrowser-env.age new file mode 100644 index 00000000..6db6fa1e Binary files /dev/null and b/hosts/glyph/secrets/filebrowser-env.age differ diff --git a/hosts/glyph/services/filebrowser.nix b/hosts/glyph/services/filebrowser.nix index 62bfccba..b96a3ad2 100644 --- a/hosts/glyph/services/filebrowser.nix +++ b/hosts/glyph/services/filebrowser.nix @@ -4,22 +4,47 @@ lib, ... }: let - cfg = config.services.filebrowser; - address = ""; - port = 8080; - dataDir = "/var/lib/filebrowser"; - rootDir = "${dataDir}/files"; - settings = { - inherit address port; - database = "${dataDir}/filebrowser.db"; - root = rootDir; - noauth = true; - }; + cfg = config.services.filebrowser-quantum; in { - services.filebrowser = { + age.secrets.filebrowser-env = { + file = ./../secrets/filebrowser-env.age; + mode = "550"; + owner = cfg.user; + inherit (cfg) group; + }; + + services.filebrowser-quantum = { enable = true; openFirewall = false; - inherit settings; + settings = { + server = { + port = 8080; + sources = [ + { + path = "/mnt/media"; + config = { + defaultEnabled = true; + }; + } + ]; + }; + auth = { + methods = { + password.enabled = false; + oidc = { + enabled = true; + # N.B.: clientId and clientSecret supplied via environment variable + issuerUrl = "https://id.zx.dev"; + scopes = "email openid profile groups"; + userIdentifier = "preferred_username"; + disableVerifyTLS = false; + createUser = true; + adminGroup = "admins"; + }; + }; + }; + }; + environmentFile = config.age.secrets.filebrowser-env.path; }; users.users.${cfg.user}.extraGroups = ["media"]; } diff --git a/lib/secrets/glyph.nix b/lib/secrets/glyph.nix index b0406805..dc25202e 100644 --- a/lib/secrets/glyph.nix +++ b/lib/secrets/glyph.nix @@ -1,6 +1,7 @@ let keys = with (import ../keys.nix); [glyph Rhizome]; in { + "hosts/glyph/secrets/filebrowser-env.age".publicKeys = keys; "hosts/glyph/secrets/pushover-app-token.age".publicKeys = keys; "hosts/glyph/secrets/pushover-user-token.age".publicKeys = keys; } diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index 79beb510..29e27779 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -2,6 +2,7 @@ { imports = [ ./web + ./filebrowser-quantum.nix ./users.nix ./ssh.nix ./sudo.nix diff --git a/modules/nixos/filebrowser-quantum.nix b/modules/nixos/filebrowser-quantum.nix new file mode 100644 index 00000000..c3d43c95 --- /dev/null +++ b/modules/nixos/filebrowser-quantum.nix @@ -0,0 +1,209 @@ +{ + config, + pkgs, + lib, + utils, + ... +}: let + cfg = config.services.filebrowser-quantum; + format = pkgs.formats.yaml {}; + inherit (lib) types; + dataDir = "/var/lib/filebrowser-quantum"; +in { + options = { + services.filebrowser-quantum = { + enable = lib.mkEnableOption "FileBrowser Quantum"; + + package = lib.mkPackageOption pkgs "filebrowser-quantum" {}; + + user = lib.mkOption { + type = types.str; + default = "filebrowser-quantum"; + description = "User account under which FileBrowser Quantum runs."; + }; + + group = lib.mkOption { + type = types.str; + default = "filebrowser-quantum"; + description = "Group under which FileBrowser Quantum runs."; + }; + + openFirewall = lib.mkEnableOption "opening firewall ports for FileBrowser Quantum"; + + settings = lib.mkOption { + default = {}; + description = '' + Settings for FileBrowser Quantum. + Refer to for all supported values. + ''; + type = types.submodule { + freeformType = format.type; + + options = { + server = { + port = lib.mkOption { + default = 8080; + description = '' + The port to listen on. + ''; + type = types.port; + }; + + baseURL = lib.mkOption { + default = "/"; + description = '' + Base URL, primarily for reverse proxy. + ''; + type = types.str; + }; + + database = lib.mkOption { + default = "/var/lib/filebrowser-quantum/database.db"; + description = '' + The path to FileBrowser Quantum's database. + ''; + type = types.path; + }; + + cacheDir = lib.mkOption { + default = "/var/cache/filebrowser-quantum"; + description = '' + The directory where FileBrowser Quantum stores its cache. + ''; + type = types.path; + readOnly = true; + }; + + sources = lib.mkOption { + description = '' + A list of file system locations accessible to users. + ''; + type = types.listOf (types.submodule { + options = { + path = lib.mkOption { + description = '' + The filesystem path to the directory you want to serve. + ''; + type = types.path; + }; + + name = lib.mkOption { + description = '' + Display name shown in the UI. If not specified, uses the base folder name from the path. Useful for providing user-friendly names like “Company Files” instead of just “documents”. + ''; + type = types.nullOr types.str; + default = null; + }; + + config = lib.mkOption { + description = '' + Configuration options for the source. + ''; + type = types.submodule { + options = { + defaultEnabled = lib.mkOption { + default = false; + description = '' + Whether new users automatically get access to this source. Defaults to `false`. Set to `true` for shared sources that all users should see. + ''; + type = types.bool; + }; + }; + }; + }; + }; + }); + }; + }; + }; + }; + }; + + environmentFile = lib.mkOption { + type = types.nullOr types.str; + default = null; + description = '' + file containing the credentials to access the repository, in the + format of an EnvironmentFile as described by {manpage}`systemd.exec(5)` + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd = { + services.filebrowser-quantum = { + after = ["network.target"]; + description = "FileBrowser Quantum"; + wantedBy = ["multi-user.target"]; + serviceConfig = + { + ExecStart = let + args = [ + (lib.getExe cfg.package) + "-c" + (format.generate "config.yaml" cfg.settings) + ]; + in + utils.escapeSystemdExecArgs args; + + StateDirectory = "filebrowser-quantum"; + CacheDirectory = "filebrowser-quantum"; + WorkingDirectory = dataDir; + + User = cfg.user; + Group = cfg.group; + UMask = "0077"; + + NoNewPrivileges = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + MemoryDenyWriteExecute = true; + LockPersonality = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + DevicePolicy = "closed"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + } + // lib.optionalAttrs (cfg.environmentFile != null) { + EnvironmentFile = cfg.environmentFile; + }; + }; + + tmpfiles.settings.filebrowser = { + "${dataDir}".d = { + inherit (cfg) user group; + mode = "0700"; + }; + "${cfg.settings.server.cacheDir}".d = { + inherit (cfg) user group; + mode = "0700"; + }; + }; + }; + + users.users = lib.mkIf (cfg.user == "filebrowser-quantum") { + filebrowser-quantum = { + inherit (cfg) group; + isSystemUser = true; + }; + }; + + users.groups = lib.mkIf (cfg.group == "filebrowser-quantum") { + filebrowser-quantum = {}; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [cfg.settings.server.port]; + }; + + meta.maintainers = [ + lib.maintainers.stackptr + ]; +} diff --git a/overlays/custom-packages.nix b/overlays/custom-packages.nix index a09aed7c..c8694841 100644 --- a/overlays/custom-packages.nix +++ b/overlays/custom-packages.nix @@ -10,6 +10,9 @@ self: super: { # FastScripts automation tool for macOS fastscripts = super.callPackage ./../packages/fastscripts/package.nix {}; + # FileBrowser quantum fork + filebrowser-quantum = super.callPackage ./../packages/filebrowser-quantum/package.nix {}; + # Mochi spaced repetition software mochi = super.callPackage ./../packages/mochi/package.nix {}; diff --git a/packages/filebrowser-quantum/package.nix b/packages/filebrowser-quantum/package.nix new file mode 100644 index 00000000..fd0ab58d --- /dev/null +++ b/packages/filebrowser-quantum/package.nix @@ -0,0 +1,75 @@ +# TODO: Upstream to NixOS/nixpkgs +{ + lib, + fetchFromGitHub, + buildGoModule, + buildNpmPackage, + nix-update-script, +}: let + version = "1.0.1-dev"; + + src = fetchFromGitHub { + owner = "stackptr"; + repo = "filebrowser"; + rev = "v${version}"; + hash = "sha256-BE+WQwRFHvGakGNPl84eVkkQMqKqF31CG0Y3E1nJkNk="; + }; + + frontend = buildNpmPackage rec { + pname = "filebrowser-quantum-frontend"; + inherit version src; + + sourceRoot = "${src.name}/frontend"; + + npmDepsHash = "sha256-Ro58WpzrreDb23sHiI9/ZekQBDl+VO271SImtEFgvYg="; + + npmBuildScript = "build:docker"; # Default build target requires writing outside frontend dir + + installPhase = '' + runHook preInstall + + mkdir $out + mv dist $out + + runHook postInstall + ''; + }; +in + buildGoModule { + pname = "filebrowser-quantum"; + inherit version src; + + modRoot = "./backend"; + + vendorHash = "sha256-urJZMOkZzoN//kecpJ47ldZk+H2qvMGTr/Pw90bMpDc="; + + preBuild = '' + rm -rf http/dist/* + rm -rf http/embed/* + cp -r ${frontend}/dist http/ + cp -r ${frontend}/dist/* http/embed + ''; + + postInstall = '' + mv $out/bin/backend $out/bin/filebrowser + ''; + + ldflags = [ + "-w" + "-s" + "-X 'github.com/gtsteffaniak/filebrowser/backend/version.CommitSHA=testingCommit'" + "-X 'github.com/gtsteffaniak/filebrowser/backend/version.Version=testing'" + ]; + + passthru = { + updateScript = nix-update-script {}; + inherit frontend; + }; + + meta = with lib; { + description = "Fork of filebrowser project"; + homepage = "https://github.com/gtsteffaniak/filebrowser"; + license = licenses.asl20; + mainProgram = "filebrowser"; + }; + }