infrastructure/deployments/transfer-sh/default.nix

424 lines
13 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.transfer-sh;
package = (pkgs.callPackage ./package.nix;)
in
{
options = {
services.transfer-sh = {
enable = mkEnableOption "transfer-sh setup";
# package = mkPackageOption pkgs "transfer-sh" { };
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = lib.mdDoc "Environment file as defined in {manpage}`systemd.exec(5)`.";
};
user = mkOption {
description = "user to run as";
default = "transfersh";
type = types.str;
};
group = mkOption {
description = "group to run as";
default = "transfersh";
type = types.str;
};
provider = mkOption {
description = "which storage provider to use (s3, storj, gdrive or local)";
default = "local";
type = types.enum [ "s3" "storj" "gdrive" "local" ];
};
address = mkOption {
description = "address to listen on";
default = "127.0.0.1";
type = types.str;
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = "Open the firewall port";
};
LISTENER = mkOption {
description = "port to use for http";
default = 6080;
type = types.port;
};
PROFILE_LISTENER = mkOption {
description = "port to use for profiler";
default = 6060;
type = types.nullOr types.port;
};
FORCE_HTTPS = mkOption {
description = "redirect to https";
default = false;
type = types.nullOr types.bool;
};
TLS_LISTENER = mkOption {
description = "port to use for https";
default = null;
type = types.nullOr types.port;
};
TLS_LISTENER_ONLY = mkOption {
description = "flag to enable tls listener only";
default = false;
type = types.nullOr types.bool;
};
TLS_CERT_FILE = mkOption {
description = "path to tls certificate";
default = null;
type = types.nullOr types.path;
};
TLS_PRIVATE_KEY = mkOption {
description = "path to tls private key";
default = null;
type = types.nullOr types.path;
};
HTTP_AUTH_USER = mkOption {
description = "user for basic http auth on upload";
default = null;
type = types.nullOr types.str;
};
HTTP_AUTH_PASS = mkOption {
description = "pass for basic http auth on upload";
default = null;
type = types.nullOr types.str;
};
HTTP_AUTH_HTPASSWD = mkOption {
description = "htpasswd file path for basic http auth on upload";
default = null;
type = types.nullOr types.path;
};
HTTP_AUTH_IP_WHITELIST = mkOption {
description = "comma separated list of ips allowed to upload without being challenged an http auth";
default = [ ];
type = with types; listOf str;
};
IP_WHITELIST = mkOption {
description = "comma separated list of ips allowed to connect to the service";
default = [ ];
type = with types; listOf str;
};
IP_BLACKLIST = mkOption {
description = "comma separated list of ips not allowed to connect to the service";
default = [ ];
type = with types; listOf str;
};
TEMP_PATH = mkOption {
description = "path to temp folder";
default = null;
type = types.nullOr types.str;
};
WEB_PATH = mkOption {
description = "path to static web files (for development or custom front end)";
default = null;
type = types.nullOr types.str;
};
PROXY_PATH = mkOption {
description = "path prefix when service is run behind a proxy";
default = null;
type = types.nullOr types.str;
};
PROXY_PORT = mkOption {
description = "port of the proxy when the service is run behind a proxy";
default = null;
type = types.nullOr types.port;
};
EMAIL_CONTACT = mkOption {
description = "email contact for the front end";
default = null;
type = types.nullOr types.str;
};
GA_KEY = mkOption {
description = "google analytics key for the front end";
default = null;
type = types.nullOr types.str;
};
USERVOICE_KEY = mkOption {
description = "user voice key for the front end";
default = null;
type = types.nullOr types.str;
};
AWS_ACCESS_KEY = mkOption {
description = "aws access key";
default = null;
type = types.nullOr types.str;
};
AWS_SECRET_KEY = mkOption {
description = "aws access key";
default = null;
type = types.nullOr types.str;
};
BUCKET = mkOption {
description = "aws bucket";
default = null;
type = types.nullOr types.str;
};
S3_ENDPOINT = mkOption {
description = "Custom S3 endpoint.";
default = null;
type = types.nullOr types.str;
};
S3_REGION = mkOption {
description = "region of the s3 bucket";
default = "eu-west-1";
type = types.nullOr types.str;
};
S3_NO_MULTIPART = mkOption {
description = "disables s3 multipart upload";
default = false;
type = types.nullOr types.bool;
};
S3_PATH_STYLE = mkOption {
description = "Forces path style URLs, required for Minio.";
default = false;
type = types.nullOr types.bool;
};
STORJ_ACCESS = mkOption {
description = "Access for the project";
default = null;
type = types.nullOr types.str;
};
STORJ_BUCKET = mkOption {
description = "Bucket to use within the project";
default = null;
type = types.nullOr types.str;
};
BASEDIR = mkOption {
description = "path storage for local/gdrive provider";
default = "${cfg.stateDir}/store";
type = types.nullOr types.str;
};
GDRIVE_CLIENT_JSON_FILEPATH = mkOption {
description = "path to oauth client json config for gdrive provider";
default = null;
type = types.nullOr types.str;
};
GDRIVE_LOCAL_CONFIG_PATH = mkOption {
description = "path to store local transfer.sh config cache for gdrive provider";
default = null;
type = types.nullOr types.str;
};
GDRIVE_CHUNK_SIZE = mkOption {
description = "chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB)";
default = null;
type = types.nullOr types.str;
};
HOSTS = mkOption {
description = "hosts to use for lets encrypt certificates (comma seperated)";
default = null;
type = types.nullOr types.str;
};
LOG = mkOption {
description = "path to log file";
default = "${cfg.stateDir}/transfer-sh.log";
type = types.nullOr types.str;
};
CORS_DOMAINS = mkOption {
description = "comma separated list of domains for CORS, setting it enable CORS";
default = null;
type = types.nullOr types.str;
};
CLAMAV_HOST = mkOption {
description = "host for clamav feature";
default = null;
type = types.nullOr types.str;
};
PERFORM_CLAMAV_PRESCAN = mkOption {
description = "prescan every upload through clamav feature (clamav-host must be a local clamd unix socket)";
default = false;
type = types.nullOr types.bool;
};
RATE_LIMIT = mkOption {
description = "request per minute";
default = null;
type = types.nullOr types.str;
};
MAX_UPLOAD_SIZE = mkOption {
description = "max upload size in kilobytes";
default = null;
type = types.nullOr types.str;
};
PURGE_DAYS = mkOption {
description = "number of days after the uploads are purged automatically";
default = "7";
type = types.nullOr types.str;
};
PURGE_INTERVAL = mkOption {
description = "interval in hours to run the automatic purge for (not applicable to S3 and Storj)";
default = 1;
type = types.nullOr types.int;
};
RANDOM_TOKEN_LENGTH = mkOption {
description = "length of the random token for the upload path (double the size for delete path)";
default = "6";
type = types.nullOr types.str;
};
stateDir = mkOption {
type = types.path;
description = "Variable state directory";
default = "/var/lib/transfer.sh";
};
};
};
config = mkIf cfg.enable
{
users.users = mkIf (cfg.user == "transfersh") {
transfersh = {
description = "transfer-sh service user";
home = cfg.stateDir;
group = cfg.group;
isSystemUser = true;
};
};
users.groups = mkIf (cfg.group == "transfersh") { transfersh = { }; };
systemd.tmpfiles.rules = [
"d ${cfg.stateDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.BASEDIR} 0750 ${cfg.user} ${cfg.group} - -"
];
systemd.services.transfer-sh = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
User = cfg.user;
Group = cfg.group;
ExecStart = "${lib.getExe package} --provider=${cfg.provider}";
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
};
environment =
{
LISTENER = "${cfg.address}:${toString cfg.LISTENER}";
PROFILE_LISTENER = toString cfg.PROFILE_LISTENER;
HTTP_AUTH_USER = cfg.HTTP_AUTH_USER;
HTTP_AUTH_PASS = cfg.HTTP_AUTH_PASS;
HTTP_AUTH_HTPASSWD = cfg.HTTP_AUTH_HTPASSWD;
HTTP_AUTH_IP_WHITELIST = concatStringsSep "," cfg.HTTP_AUTH_IP_WHITELIST;
IP_WHITELIST = concatStringsSep "," cfg.IP_WHITELIST;
IP_BLACKLIST = concatStringsSep "," cfg.IP_BLACKLIST;
TEMP_PATH = cfg.TEMP_PATH;
WEB_PATH = cfg.WEB_PATH;
PROXY_PATH = cfg.PROXY_PATH;
PROXY_PORT = toString cfg.PROXY_PORT;
EMAIL_CONTACT = cfg.EMAIL_CONTACT;
GA_KEY = cfg.GA_KEY;
USERVOICE_KEY = cfg.USERVOICE_KEY;
HOSTS = cfg.HOSTS;
LOG = cfg.LOG;
CORS_DOMAINS = cfg.CORS_DOMAINS;
CLAMAV_HOST = cfg.CLAMAV_HOST;
PERFORM_CLAMAV_PRESCAN = lib.boolToString cfg.PERFORM_CLAMAV_PRESCAN;
RATE_LIMIT = cfg.RATE_LIMIT;
MAX_UPLOAD_SIZE = cfg.MAX_UPLOAD_SIZE;
PURGE_DAYS = cfg.PURGE_DAYS;
RANDOM_TOKEN_LENGTH = cfg.RANDOM_TOKEN_LENGTH;
BASEDIR = cfg.BASEDIR;
PURGE_INTERVAL = toString cfg.PURGE_INTERVAL;
} // lib.optionalAttrs (cfg.provider == "s3") {
# Options specific to s3 backend
AWS_ACCESS_KEY = cfg.AWS_ACCESS_KEY;
AWS_SECRET_KEY = cfg.AWS_SECRET_KEY;
BUCKET = cfg.BUCKET;
S3_REGION = cfg.S3_REGION;
S3_ENDPOINT = cfg.S3_ENDPOINT;
S3_NO_MULTIPART = lib.boolToString cfg.S3_NO_MULTIPART;
S3_PATH_STYLE = lib.boolToString cfg.S3_PATH_STYLE;
} // lib.optionalAttrs (cfg.provider == "storj") {
# Options specific to storj backend
STORJ_ACCESS = cfg.STORJ_ACCESS;
STORJ_BUCKET = cfg.STORJ_BUCKET;
} // lib.optionalAttrs (cfg.provider == "gdrive") {
# Options specific to google drive backend
GDRIVE_CLIENT_JSON_FILEPATH = cfg.GDRIVE_CLIENT_JSON_FILEPATH;
GDRIVE_LOCAL_CONFIG_PATH = cfg.GDRIVE_LOCAL_CONFIG_PATH;
GDRIVE_CHUNK_SIZE = cfg.GDRIVE_CHUNK_SIZE;
} // lib.optionalAttrs (cfg.TLS_LISTENER != null) {
# TLS specific options
TLS_LISTENER = "${cfg.address}:${toString cfg.TLS_LISTENER}";
TLS_LISTENER_ONLY = lib.boolToString cfg.TLS_LISTENER_ONLY;
TLS_CERT_FILE = cfg.TLS_CERT_FILE;
TLS_PRIVATE_KEY = cfg.TLS_PRIVATE_KEY;
FORCE_HTTPS = lib.boolToString cfg.FORCE_HTTPS;
};
};
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall
([ cfg.LISTENER cfg.PROFILE_LISTENER ] ++ optionals (cfg.TLS_LISTENER != null) [ cfg.TLS_LISTENER ]);
warnings =
let
sensitiveVars = [
"GA_KEY"
"HTTP_AUTH_PASS"
"USERVOICE_KEY"
"AWS_SECRET_KEY"
"STORJ_ACCESS"
];
in
lib.lists.forEach (filter (i: cfg."${i}" != null) sensitiveVars) (x:
''
config.services.transfer-sh.${x} will be stored as plaintext in the Nix store.
Use services.transfer-sh.environmentFile instead to prevent this.
''
);
};
meta.maintainers = with lib.maintainers; [ pinpox ];
}