Skip to content

Commit

Permalink
Merge pull request #234998 from yayayayaka/backport-184586-to-release…
Browse files Browse the repository at this point in the history
…-23.05

[23.05] nixos/sftpgo: init, nixosTests.sftpgo: init
  • Loading branch information
mweinelt authored May 30, 2023
2 parents 0491e5b + 8cc61b1 commit 21c2ec4
Show file tree
Hide file tree
Showing 5 changed files with 769 additions and 1 deletion.
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,7 @@
./services/web-apps/powerdns-admin.nix
./services/web-apps/prosody-filer.nix
./services/web-apps/restya-board.nix
./services/web-apps/sftpgo.nix
./services/web-apps/rss-bridge.nix
./services/web-apps/selfoss.nix
./services/web-apps/shiori.nix
Expand Down
375 changes: 375 additions & 0 deletions nixos/modules/services/web-apps/sftpgo.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
{ options, config, lib, pkgs, utils, ... }:

with lib;

let
cfg = config.services.sftpgo;
defaultUser = "sftpgo";
settingsFormat = pkgs.formats.json {};
configFile = settingsFormat.generate "sftpgo.json" cfg.settings;
hasPrivilegedPorts = any (port: port > 0 && port < 1024) (
catAttrs "port" (cfg.settings.httpd.bindings
++ cfg.settings.ftpd.bindings
++ cfg.settings.sftpd.bindings
++ cfg.settings.webdavd.bindings
)
);
in
{
options.services.sftpgo = {
enable = mkOption {
type = types.bool;
default = false;
description = mdDoc "sftpgo";
};

package = mkOption {
type = types.package;
default = pkgs.sftpgo;
defaultText = literalExpression "pkgs.sftpgo";
description = mdDoc ''
Which SFTPGo package to use.
'';
};

extraArgs = mkOption {
type = with types; listOf str;
default = [];
description = mdDoc ''
Additional command line arguments to pass to the sftpgo daemon.
'';
example = [ "--log-level" "info" ];
};

dataDir = mkOption {
type = types.str;
default = "/var/lib/sftpgo";
description = mdDoc ''
The directory where SFTPGo stores its data files.
'';
};

user = mkOption {
type = types.str;
default = defaultUser;
description = mdDoc ''
User account name under which SFTPGo runs.
'';
};

group = mkOption {
type = types.str;
default = defaultUser;
description = mdDoc ''
Group name under which SFTPGo runs.
'';
};

loadDataFile = mkOption {
default = null;
type = with types; nullOr path;
description = mdDoc ''
Path to a json file containing users and folders to load (or update) on startup.
Check the [documentation](https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md)
for the `--loaddata-from` command line argument for more info.
'';
};

settings = mkOption {
default = {};
description = mdDoc ''
The primary sftpgo configuration. See the
[configuration reference](https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md)
for possible values.
'';
type = with types; submodule {
freeformType = settingsFormat.type;
options = {
httpd.bindings = mkOption {
default = [];
description = mdDoc ''
Configure listen addresses and ports for httpd.
'';
type = types.listOf (types.submodule {
freeformType = settingsFormat.type;
options = {
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = mdDoc ''
Network listen address. Leave blank to listen on all available network interfaces.
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
'';
};

port = mkOption {
type = types.port;
default = 8080;
description = mdDoc ''
The port for serving HTTP(S) requests.
Setting the port to `0` disables listening on this interface binding.
'';
};

enable_web_admin = mkOption {
type = types.bool;
default = true;
description = mdDoc ''
Enable the built-in web admin for this interface binding.
'';
};

enable_web_client = mkOption {
type = types.bool;
default = true;
description = mdDoc ''
Enable the built-in web client for this interface binding.
'';
};
};
});
};

ftpd.bindings = mkOption {
default = [];
description = mdDoc ''
Configure listen addresses and ports for ftpd.
'';
type = types.listOf (types.submodule {
freeformType = settingsFormat.type;
options = {
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = mdDoc ''
Network listen address. Leave blank to listen on all available network interfaces.
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
'';
};

port = mkOption {
type = types.port;
default = 0;
description = mdDoc ''
The port for serving FTP requests.
Setting the port to `0` disables listening on this interface binding.
'';
};
};
});
};

sftpd.bindings = mkOption {
default = [];
description = mdDoc ''
Configure listen addresses and ports for sftpd.
'';
type = types.listOf (types.submodule {
freeformType = settingsFormat.type;
options = {
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = mdDoc ''
Network listen address. Leave blank to listen on all available network interfaces.
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
'';
};

port = mkOption {
type = types.port;
default = 0;
description = mdDoc ''
The port for serving SFTP requests.
Setting the port to `0` disables listening on this interface binding.
'';
};
};
});
};

webdavd.bindings = mkOption {
default = [];
description = mdDoc ''
Configure listen addresses and ports for webdavd.
'';
type = types.listOf (types.submodule {
freeformType = settingsFormat.type;
options = {
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = mdDoc ''
Network listen address. Leave blank to listen on all available network interfaces.
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
'';
};

port = mkOption {
type = types.port;
default = 0;
description = mdDoc ''
The port for serving WebDAV requests.
Setting the port to `0` disables listening on this interface binding.
'';
};
};
});
};

smtp = mkOption {
default = {};
description = mdDoc ''
SMTP configuration section.
'';
type = types.submodule {
freeformType = settingsFormat.type;
options = {
host = mkOption {
type = types.str;
default = "";
description = mdDoc ''
Location of SMTP email server. Leave empty to disable email sending capabilities.
'';
};

port = mkOption {
type = types.port;
default = 465;
description = mdDoc "Port of the SMTP Server.";
};

encryption = mkOption {
type = types.enum [ 0 1 2 ];
default = 1;
description = mdDoc ''
Encryption scheme:
- `0`: No encryption
- `1`: TLS
- `2`: STARTTLS
'';
};

auth_type = mkOption {
type = types.enum [ 0 1 2 ];
default = 0;
description = mdDoc ''
- `0`: Plain
- `1`: Login
- `2`: CRAM-MD5
'';
};

user = mkOption {
type = types.str;
default = "sftpgo";
description = mdDoc "SMTP username.";
};

from = mkOption {
type = types.str;
default = "SFTPGo <sftpgo@example.com>";
description = mdDoc ''
From address.
'';
};
};
};
};
};
};
};
};

config = mkIf cfg.enable {
services.sftpgo.settings = (mapAttrs (name: mkDefault) {
ftpd.bindings = [{ port = 0; }];
httpd.bindings = [{ port = 0; }];
sftpd.bindings = [{ port = 0; }];
webdavd.bindings = [{ port = 0; }];
httpd.openapi_path = "${cfg.package}/share/sftpgo/openapi";
httpd.templates_path = "${cfg.package}/share/sftpgo/templates";
httpd.static_files_path = "${cfg.package}/share/sftpgo/static";
smtp.templates_path = "${cfg.package}/share/sftpgo/templates";
});

users = optionalAttrs (cfg.user == defaultUser) {
users = {
${defaultUser} = {
description = "SFTPGo system user";
isSystemUser = true;
group = defaultUser;
home = cfg.dataDir;
};
};

groups = {
${defaultUser} = {
members = [ defaultUser ];
};
};
};

systemd.services.sftpgo = {
description = "SFTPGo daemon";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];

environment = {
SFTPGO_CONFIG_FILE = mkDefault configFile;
SFTPGO_LOG_FILE_PATH = mkDefault ""; # log to journal
SFTPGO_LOADDATA_FROM = mkIf (cfg.loadDataFile != null) cfg.loadDataFile;
};

serviceConfig = mkMerge [
({
Type = "simple";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = cfg.dataDir;
ReadWritePaths = [ cfg.dataDir ];
LimitNOFILE = 8192; # taken from upstream
KillMode = "mixed";
ExecStart = "${cfg.package}/bin/sftpgo serve ${utils.escapeSystemdExecArgs cfg.extraArgs}";
ExecReload = "${pkgs.util-linux}/bin/kill -s HUP $MAINPID";

# Service hardening
CapabilityBoundingSet = [ (optionalString hasPrivilegedPorts "CAP_NET_BIND_SERVICE") ];
DevicePolicy = "closed";
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = [ "@system-service" "~@privileged" ];
UMask = "0077";
})
(mkIf hasPrivilegedPorts {
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
})
(mkIf (cfg.dataDir == options.services.sftpgo.dataDir.default) {
StateDirectory = baseNameOf cfg.dataDir;
})
];
};
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ in {
seafile = handleTest ./seafile.nix {};
searx = handleTest ./searx.nix {};
service-runner = handleTest ./service-runner.nix {};
sftpgo = runTest ./sftpgo.nix;
sfxr-qt = handleTest ./sfxr-qt.nix {};
sgtpuzzles = handleTest ./sgtpuzzles.nix {};
shadow = handleTest ./shadow.nix {};
Expand Down
Loading

0 comments on commit 21c2ec4

Please sign in to comment.