Add basic transfer.sh deployment
This commit is contained in:
parent
8a0930c356
commit
b76da7822c
|
@ -0,0 +1,23 @@
|
|||
{ lib, fetchFromGitHub, buildGoModule }:
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "transfer.sh";
|
||||
version = "1.6.1";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "dutchcoders";
|
||||
repo = "transfer.sh";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-V8E6RwzxKB6KeGPer5074e7y6XHn3ZD24PQMwTxw5lQ=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-C8ZfUIGT9HiQQiJ2hk18uwGaQzNCIKp/Jiz6ePZkgDQ=";
|
||||
|
||||
meta = with lib; {
|
||||
description = "Easy and fast file sharing and pastebin server with access from the command-line";
|
||||
homepage = "https://github.com/dutchcoders/transfer.sh";
|
||||
changelog = "https://github.com/dutchcoders/transfer.sh/releases";
|
||||
license = licenses.mit;
|
||||
maintainers = with maintainers; [ ecchibitionist ];
|
||||
};
|
||||
}
|
|
@ -0,0 +1,423 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.transfer-sh;
|
||||
package = pkgs.callPackage ../transfer-sh { };
|
||||
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 ];
|
||||
}
|
|
@ -19,6 +19,11 @@
|
|||
owner = config.users.users.nginx.name;
|
||||
group = config.users.users.nginx.group;
|
||||
};
|
||||
sops.secrets."services/nginx/transfersh.htpasswd" = {
|
||||
mode = "0400";
|
||||
owner = config.users.users.transfersh.name;
|
||||
group = config.users.users.transfersh.group;
|
||||
};
|
||||
|
||||
# HedgeDoc
|
||||
sops.secrets."services/hedgedoc/.env" = {
|
||||
|
|
|
@ -3,6 +3,7 @@ services:
|
|||
admin.htpasswd: ENC[AES256_GCM,data:SYy91gzsVPwca7QHsAFnDV7e9hLoqS1+xeFyLNTa7WwFwT6sbvboMEnZUQ==,iv:RX8+6Ivx0ibZvoMlaxIGzJ1/OzMgOHu94J/lsvF5UqY=,tag:LtBBAlmRI0jskINGR7Gw/Q==,type:str]
|
||||
ecchi.htpasswd: ENC[AES256_GCM,data:w6VYz0uQun4QiSmpqjwVLDRseVND0pHNzFxlD9F/0j7YqeHTo8gl1AI2cQ==,iv:7KKyUyoVtvIiZuQTmtKzWjZwr7heVX2K2C/WRSOPh0A=,tag:iOdURKQGTh+wt4PcEXCGUg==,type:str]
|
||||
music.htpasswd: ENC[AES256_GCM,data:kYY/QtHZAjC3d8nn41R5NkVj529oGZdnMcqH0S4GW26HUzQ/yYlKELzCxoHRXq4nqoU+gGdjDsRGnzIiKTn629/MzfwpLwD+objiPFzpnvasD6eEHRKE2w==,iv:TKD8Rbv8XcNJFdrQ9YlruuKGvdXyHOenkAW0B7eytKQ=,tag:CmhQR+u7uvZV1go/YOKR4A==,type:str]
|
||||
transfersh.htpasswd: ENC[AES256_GCM,data:tC4o0/0u2z5vs9FVRBuZrPKujjKXBp/6Ra9g1rnRTvBtM7GUWCUcRItE7Q==,iv:/CLfX+WWahfCCZhHdxIvTUsnTyCymM8pbzkjnVliU/8=,tag:BXHjddJATTeXbnG79du8SA==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
|
@ -27,8 +28,8 @@ sops:
|
|||
a1d3ekVWMDV4dUxrSGNod2JvYmtHMmMKnBaqvtBd53Jz9CtkOeEJ93YBeGA8pmof
|
||||
VlSrnXcJmZ3tG1GwVOu8Q9Xr5gXrvaG4HGvETLsGBafxVtMTU4v8KQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2024-01-05T20:09:42Z"
|
||||
mac: ENC[AES256_GCM,data:lcZQtLr1TtpoETO13T8n62DZlwhjoZteq9BeoF5LjcjS/ol8YdWgJ/a9XxeG8/403wsgpB2P1tjIsNJ14oB7+ehj3G6x51j0saz/qk0Dv0XZVYHsg5GdubBGMd1dgYYHnSQx3Yu5nHXAW+Dx1uN/vLAjZAe1KtYG6MghyiyeOwE=,iv:W2hXqXzbMlZvkPPpMPEscrAlNTwKvilanWEz7EK4df8=,tag:/7HuZHUdM2II/iFBuOr6hQ==,type:str]
|
||||
lastmodified: "2024-01-24T20:03:25Z"
|
||||
mac: ENC[AES256_GCM,data:KoNpLDn791EKRgZ1l6TbBLhHfXTPV0j3Wy+knk/Mc6oW9dTQaN9OsqHCSb4HbXJk4E0Vt2C+Ngwgip5+9xvYuWc1q5z8F91MgY/euhbG1raEAHxLp3c9c+J805dYeim2NqTjWbufLQ12ittn3Rv2lArurFsWoJayfvrTUjXImkU=,iv:RpFUctEZ/yxKLeYMTyPEMShufL1A6BxakBefL4v+3uc=,tag:dZNEvnX4mk8mWYTVyJBPAg==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.8.1
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{ config, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
../../../deployments/transfer-sh/module.nix
|
||||
];
|
||||
|
||||
services.transfer-sh = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
address = "192.168.99.201";
|
||||
HTTP_AUTH_HTPASSWD = "/run/secrets/services/nginx/transfersh.htpasswd";
|
||||
TEMP_PATH = "/mnt/data/transfer-sh/temp";
|
||||
BASEDIR = "/mnt/data/transfer-sh/store";
|
||||
EMAIL_CONTACT = "abuse@lewd.wtf";
|
||||
PURGE_DAYS = "90";
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue