Skip to content

Commit

Permalink
nixos/send: init
Browse files Browse the repository at this point in the history
  • Loading branch information
Moraxyc committed Oct 27, 2024
1 parent 8756853 commit c9086d8
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 0 deletions.
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,7 @@
./services/web-servers/phpfpm/default.nix
./services/web-servers/pomerium.nix
./services/web-servers/rustus.nix
./services/web-servers/send.nix
./services/web-servers/stargazer.nix
./services/web-servers/static-web-server.nix
./services/web-servers/tomcat.nix
Expand Down
228 changes: 228 additions & 0 deletions nixos/modules/services/web-servers/send.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption types;
cfg = config.services.send;
in
{
options = {
services.send = {
enable = lib.mkEnableOption "Send, a file sharing web sevice for ffsend.";

package = lib.mkPackageOption pkgs "send" { };

environment = mkOption {
type =
with types;
attrsOf (
nullOr (oneOf [
bool
int
str
(listOf int)
])
);
description = ''
All the available config options and their defaults can be found here: https://github.com/timvisee/send/blob/master/server/config.js,
some descriptions can found here: https://github.com/timvisee/send/blob/master/docs/docker.md#environment-variables
Values under {option}`services.send.environment` will override the predefined values in the Send service.
- Time/duration should be in seconds
- Filesize values should be in bytes
'';
example = {
DEFAULT_DOWNLOADS = 1;
DETECT_BASE_URL = true;
EXPIRE_TIMES_SECONDS = [
300
3600
86400
604800
];
};
};

dataDir = lib.mkOption {
type = types.path;
readOnly = true;
default = "/var/lib/send";
description = ''
Directory for uploaded files.
Due to limitations in {option}`systemd.services.send.serviceConfig.DynamicUser`, this item is read only.
'';
};

baseUrl = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Base URL for the Send service.
Leave it blank to automatically detect the base url.
'';
};

host = lib.mkOption {
type = types.str;
default = "127.0.0.1";
description = "The hostname or IP address for Send to bind to.";
};

port = lib.mkOption {
type = types.port;
default = 1443;
description = "Port the Send service listens on.";
};

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

redis = {
createLocally = lib.mkOption {
type = types.bool;
default = true;
description = "Whether to create a local redis automatically.";
};

name = lib.mkOption {
type = types.str;
default = "send";
description = ''
Name of the redis server.
Only used if {option}`services.send.redis.createLocally` is set to true.
'';
};

host = lib.mkOption {
type = types.str;
default = "localhost";
description = "Redis server address.";
};

port = lib.mkOption {
type = types.port;
default = 6379;
description = "Port of the redis server.";
};

passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/agenix/send-redis-password";
description = ''
The path to the file containing the Redis password.
If {option}`services.send.redis.createLocally` is set to true,
the content of this file will be used as the password for the locally created Redis instance.
Leave it blank if no password is required.
'';
};
};
};
};

config = lib.mkIf cfg.enable {

services.send.environment.DETECT_BASE_URL = cfg.baseUrl == null;

assertions = [
{
assertion = cfg.redis.createLocally -> cfg.redis.host == "localhost";
message = "the redis host must be localhost if services.send.redis.createLocally is set to true";
}
];

networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.port;

services.redis = lib.optionalAttrs cfg.redis.createLocally {
servers."${cfg.redis.name}" = {
enable = true;
bind = "localhost";
port = cfg.redis.port;
};
};

systemd.services.send = {
serviceConfig = {
Type = "simple";
Restart = "always";
StateDirectory = "send";
WorkingDirectory = cfg.dataDir;
ReadWritePaths = cfg.dataDir;
LoadCredential = lib.optionalString (
cfg.redis.passwordFile != null
) "redis-password:${cfg.redis.passwordFile}";

# Hardening
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
AmbientCapabilities = lib.optionalString (cfg.port < 1024) "cap_net_bind_service";
DynamicUser = true;
CapabilityBoundingSet = "";
NoNewPrivileges = true;
RemoveIPC = true;
PrivateTmp = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "full";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = "0077";
};
environment =
{
IP_ADDRESS = cfg.host;
PORT = toString cfg.port;
BASE_URL = if (cfg.baseUrl == null) then "http://${cfg.host}:${toString cfg.port}" else cfg.baseUrl;
FILE_DIR = cfg.dataDir + "/uploads";
REDIS_HOST = cfg.redis.host;
REDIS_PORT = toString cfg.redis.port;
}
// (lib.mapAttrs (
name: value:
if lib.isList value then
"[" + lib.concatStringsSep ", " (map (x: toString x) value) + "]"
else if lib.isBool value then
lib.boolToString value
else
toString value
) cfg.environment);
after =
[
"network.target"
]
++ lib.optionals cfg.redis.createLocally [
"redis-${cfg.redis.name}.service"
];
description = "Send web service";
wantedBy = [ "multi-user.target" ];
script = ''
${lib.optionalString (cfg.redis.passwordFile != null) ''
export REDIS_PASSWORD="$(cat $CREDENTIALS_DIRECTORY/redis-password)"
''}
${lib.getExe cfg.package}
'';
};
};

meta.maintainers = with lib.maintainers; [ moraxyc ];
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,7 @@ in {
seafile = handleTest ./seafile.nix {};
searx = runTest ./searx.nix;
seatd = handleTest ./seatd.nix {};
send = runTest ./send.nix;
service-runner = handleTest ./service-runner.nix {};
sftpgo = runTest ./sftpgo.nix;
sfxr-qt = handleTest ./sfxr-qt.nix {};
Expand Down
34 changes: 34 additions & 0 deletions nixos/tests/send.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{ lib, pkgs, ... }:
{
name = "send";

meta = {
maintainers = with lib.maintainers; [ moraxyc ];
};

nodes.machine =
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
curl
ffsend
];

services.send = {
enable = true;
};
};

testScript = ''
machine.wait_for_unit("send.service")
machine.wait_for_open_port(1443)
machine.succeed("curl --fail --max-time 10 http://127.0.0.1:1443")
machine.succeed("echo HelloWorld > /tmp/test")
url = machine.succeed("ffsend upload -q -h http://127.0.0.1:1443/ /tmp/test")
machine.succeed(f'ffsend download --output /tmp/download {url}')
machine.succeed("cat /tmp/download | grep HelloWorld")
'';
}

0 comments on commit c9086d8

Please sign in to comment.