Unverified Commit 6a73e025 authored by Ilan Joselevich's avatar Ilan Joselevich Committed by GitHub
Browse files

nixos/tusd: init module (#505412)

parents c056d5e6 52b16dc4
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -16251,6 +16251,12 @@
    github = "M0ustach3";
    githubId = 37956764;
  };
  m1-s = {
    email = "michael@m1-s.com";
    github = "m1-s";
    githubId = 94642227;
    name = "Michael Schneider";
  };
  m1cr0man = {
    email = "lucas+nix@m1cr0man.com";
    github = "m1cr0man";
+1 −0
Original line number Diff line number Diff line
@@ -1833,6 +1833,7 @@
  ./services/web-servers/traefik.nix
  ./services/web-servers/trafficserver/default.nix
  ./services/web-servers/ttyd.nix
  ./services/web-servers/tusd.nix
  ./services/web-servers/unit/default.nix
  ./services/web-servers/uwsgi.nix
  ./services/web-servers/varnish/default.nix
+162 −0
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  ...
}:

let
  cfg = config.services.tusd;
  username = "tusd";
  groupname = "tusd";

  args = [
    "-host=${cfg.host}"
    "-port=${toString cfg.port}"
    "-base-path=${cfg.basePath}"
    "-upload-dir=${cfg.uploadDir}"
  ]
  ++ lib.optional cfg.behindProxy "-behind-proxy"
  ++ lib.optional (cfg.maxSize != null) "-max-size=${toString cfg.maxSize}"
  ++ lib.optional (cfg.networkTimeout != null) "-network-timeout=${cfg.networkTimeout}"
  ++ lib.optional (cfg.hooksHttp != null) "-hooks-http=${cfg.hooksHttp}"
  ++ lib.optional (
    cfg.hooksEnabledEvents != [ ]
  ) "-hooks-enabled-events=${lib.concatStringsSep "," cfg.hooksEnabledEvents}"
  ++ cfg.extraArgs;
in
{
  meta.maintainers = with lib.maintainers; [ m1-s ];

  options.services.tusd = {
    enable = lib.mkEnableOption "tus resumable upload protocol server";

    host = lib.mkOption {
      type = lib.types.str;
      default = "0.0.0.0";
      description = "The host to bind the HTTP server to.";
    };

    port = lib.mkOption {
      type = lib.types.port;
      default = 8080;
      description = "The port to bind the HTTP server to.";
    };

    openFirewall = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Whether to open the firewall port for tusd.";
    };

    basePath = lib.mkOption {
      type = lib.types.str;
      default = "/files/";
      description = "The basepath of the HTTP server.";
    };

    uploadDir = lib.mkOption {
      type = lib.types.path;
      default = "/var/lib/tusd/data";
      description = "The directory to store uploads in.";
    };

    behindProxy = lib.mkEnableOption null // {
      description = "Whether to respect X-Forwarded-* and similar headers which may be set by proxies.";
    };

    maxSize = lib.mkOption {
      type = lib.types.nullOr lib.types.int;
      default = null;
      description = "The maximum size of a single upload in bytes.";
    };

    networkTimeout = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      description = ''
        The timeout for reading the request and writing the response.
        If tusd does not receive data for this duration,
        it will consider the connection dead.
      '';
      example = "30s";
    };

    hooksHttp = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      description = "The HTTP endpoint to which hook events will be sent to.";
      example = "http://localhost:8081/hooks";
    };

    hooksEnabledEvents = lib.mkOption {
      type = lib.types.listOf lib.types.str;
      default = [ ];
      description = "The list of enabled hook events.";
      example = [
        "pre-create"
        "post-finish"
      ];
    };

    extraArgs = lib.mkOption {
      type = lib.types.listOf lib.types.str;
      default = [ ];
      description = "Additional arguments given to tusd.";
      example = [
        "-verbose"
        "-log-format=json"
      ];
    };
  };

  config = lib.mkIf cfg.enable {
    users.users.${username} = {
      isSystemUser = true;
      group = groupname;
    };
    users.groups.${groupname} = { };

    # tusd knows how to create subdirectories in this folder but we have to
    # create the root folder ourselves.
    systemd.tmpfiles.settings."tusd".${cfg.uploadDir}.d = {
      user = username;
      group = groupname;
      # default taken from https://github.com/tus/tusd/blob/55a096a10942b85360664a1e8aea7bd758272053/pkg/filestore/filestore.go#L37
      mode = "0775";
    };

    systemd.services.tusd = {
      description = "tusd - tus resumable upload protocol server";
      documentation = [ "https://github.com/tus/tusd" ];

      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];

      serviceConfig = {
        User = username;
        Group = groupname;

        ExecStart = lib.escapeShellArgs ([ (lib.getExe pkgs.tusd) ] ++ args);
        Restart = "on-failure";

        StateDirectory = "tusd";

        # Hardening
        LockPersonality = true;
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHostUserNamespaces = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
      };
    };

    networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.port;
  };
}
+16 −22
Original line number Diff line number Diff line
@@ -2,37 +2,28 @@

let
  port = 1080;

  client =
    { pkgs, ... }:
    {
      environment.systemPackages = [ pkgs.curl ];
    };

  server =
    { pkgs, ... }:
    {
      # tusd does not have a NixOS service yet.
      systemd.services.tusd = {
        wantedBy = [ "multi-user.target" ];

        serviceConfig = {
          ExecStart = ''${pkgs.tusd}/bin/tusd -port "${toString port}" -upload-dir=/data'';
        };
      };
      networking.firewall.allowedTCPPorts = [ port ];
    };
  uploadDir = "/var/lib/tusd/data";
in
{
  name = "tusd";
  meta.maintainers = with lib.maintainers; [
    m1-s
    nh2
    kalbasit
  ];

  nodes = {
    inherit server;
    inherit client;
    client = {
      environment.systemPackages = [ pkgs.curl ];
    };

    server = {
      services.tusd = {
        enable = true;
        inherit port uploadDir;
        openFirewall = true;
      };
    };
  };

  testScript = ''
@@ -46,6 +37,9 @@ in
    client.wait_for_unit("network.target")
    client.succeed("${./tus-curl-upload.sh} file-100M.bin http://server:${toString port}/files/")

    # Verify file was created in uploadDir
    server.succeed("test -n \"$(ls -A ${uploadDir})\"")

    print("Upload succeeded")
  '';
}
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ buildGoModule (finalAttrs: {
    description = "Reference server implementation in Go of tus: the open protocol for resumable file uploads";
    license = lib.licenses.mit;
    homepage = "https://tus.io/";
    mainProgram = "tusd";
    maintainers = with lib.maintainers; [
      nh2
      kalbasit