forked from mirrors/nixpkgs
nixos/sftpgo: init
A fully featured and highly configurable SFTP server with optional HTTP/S, FTP/S and WebDAV support. https://github.com/drakkan/sftpgo
This commit is contained in:
parent
643d213ea6
commit
f63f781063
|
@ -1230,6 +1230,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
|
||||
|
|
375
nixos/modules/services/web-apps/sftpgo.nix
Normal file
375
nixos/modules/services/web-apps/sftpgo.nix
Normal file
|
@ -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;
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue