Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
acd6fb084f | ||
|
6f49951bc0 | ||
|
d2a0e77814 | ||
|
014b95ff07 | ||
|
0eec27586d | ||
|
c7164856d2 | ||
|
96723b2685 | ||
|
4a56bad05f | ||
|
fa74be02d2 | ||
|
2959fc2992 | ||
|
694b3ce246 | ||
|
92948fab23 | ||
|
e5455d9713 | ||
|
6ab75b30e5 | ||
|
6b8eff8322 | ||
|
2da62eb235 | ||
|
156daa5a24 |
4
.github/workflows/build-docker-images.yml
vendored
4
.github/workflows/build-docker-images.yml
vendored
@@ -4,9 +4,9 @@ on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # everyday at midnight UTC
|
||||
pull_request:
|
||||
branches: master
|
||||
branches: main
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
tags:
|
||||
- v*
|
||||
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -107,7 +107,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.16
|
||||
go-version: ^1.17
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
|
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
@@ -17,6 +17,7 @@ jobs:
|
||||
- 1.14.x
|
||||
- 1.15.x
|
||||
- 1.16.x
|
||||
- 1.17.x
|
||||
name: Test with ${{ matrix.go_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
@@ -13,6 +13,7 @@ Examples of unacceptable behavior by participants include:
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses, without explicit permission
|
||||
* Other unethical or unprofessional conduct
|
||||
* Use of harsh language
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
|
||||
|
||||
@@ -20,5 +21,5 @@ This code of conduct applies both within project spaces and in public spaces whe
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.2.0, available at https://www.contributor-covenant.org/version/1/2/0/code-of-conduct.html
|
||||
This Code of Conduct is adapted from the [Contributor Covenant] (https://www.contributor-covenant.org), version 1.2.0, available at https://www.contributor-covenant.org/version/1/2/0/code-of-conduct.html
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# Default to Go 1.16
|
||||
ARG GO_VERSION=1.16
|
||||
# Default to Go 1.17
|
||||
ARG GO_VERSION=1.17
|
||||
FROM golang:${GO_VERSION}-alpine as build
|
||||
|
||||
# Necessary to run 'go get' and to compile the linked binary
|
||||
|
30
README.md
30
README.md
@@ -1,4 +1,4 @@
|
||||
# transfer.sh [](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [](https://hub.docker.com/r/dutchcoders/transfer.sh/) [](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml?query=branch%3Amaster)
|
||||
# transfer.sh [](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [](https://hub.docker.com/r/dutchcoders/transfer.sh/) [](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml?query=branch%3Amain)
|
||||
|
||||
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance.
|
||||
|
||||
@@ -15,17 +15,17 @@ The service at transfersh.com is of unknown origin and reported as cloud malware
|
||||
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt
|
||||
```
|
||||
|
||||
### Encrypt & upload:
|
||||
### Encrypt & Upload:
|
||||
```bash
|
||||
$ cat /tmp/hello.txt|gpg -ac -o-|curl -X PUT --upload-file "-" https://transfer.sh/test.txt
|
||||
````
|
||||
|
||||
### Download & decrypt:
|
||||
### Download & Decrypt:
|
||||
```bash
|
||||
$ curl https://transfer.sh/1lDau/test.txt|gpg -o- > /tmp/hello.txt
|
||||
```
|
||||
|
||||
### Upload to virustotal:
|
||||
### Upload to Virustotal:
|
||||
```bash
|
||||
$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal
|
||||
```
|
||||
@@ -51,7 +51,7 @@ $ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Days: 1"
|
||||
|
||||
### X-Url-Delete
|
||||
|
||||
The URL used to request the deletion of a file. Returned as a response header.
|
||||
The URL used to request the deletion of a file and returned as a response header.
|
||||
```bash
|
||||
curl -sD - --upload-file ./hello https://transfer.sh/hello.txt | grep 'X-Url-Delete'
|
||||
X-Url-Delete: https://transfer.sh/hello.txt/BAYh0/hello.txt/PDw0NHPcqU
|
||||
@@ -90,6 +90,7 @@ temp-path | path to temp folder | system temp | TEMP_PATH |
|
||||
web-path | path to static web files (for development or custom front end) | | WEB_PATH |
|
||||
proxy-path | path prefix when service is run behind a proxy | | PROXY_PATH |
|
||||
proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT |
|
||||
email-contact | email contact for the front end | | EMAIL_CONTACT |
|
||||
ga-key | google analytics key for the front end | | GA_KEY |
|
||||
provider | which storage provider to use | (s3, storj, gdrive or local) |
|
||||
uservoice-key | user voice key for the front end | | USERVOICE_KEY |
|
||||
@@ -161,23 +162,23 @@ To use a custom non-AWS S3 provider, you need to specify the endpoint as defined
|
||||
|
||||
## Storj Network Provider
|
||||
|
||||
To use the Storj Network as storage provider you need to specify the following flags:
|
||||
To use the Storj Network as a storage provider you need to specify the following flags:
|
||||
- provider `--provider storj`
|
||||
- storj-access _(either via flag or environment variable STORJ_ACCESS)_
|
||||
- storj-bucket _(either via flag or environment variable STORJ_BUCKET)_
|
||||
|
||||
### Creating Bucket and Scope
|
||||
|
||||
In preparation you need to create an access grant (or copy it from the uplink configuration) and a bucket.
|
||||
You need to create an access grant (or copy it from the uplink configuration) and a bucket in preparation.
|
||||
|
||||
To get started, login to your account and go to the Access Grant Menu and start the Wizard on the upper right.
|
||||
To get started, log in to your account and go to the Access Grant Menu and start the Wizard on the upper right.
|
||||
|
||||
Enter your access grant name of choice, hit *Next* and restrict it as necessary/preferred.
|
||||
Aftwards continue either in CLI or within the Browser. You'll be asked for a Passphrase used as Encryption Key.
|
||||
**Make sure to save it in a safe place, without it you will lose the ability to decrypt your files!**
|
||||
Afterwards continue either in CLI or within the Browser. Next, you'll be asked for a Passphrase used as Encryption Key.
|
||||
**Make sure to save it in a safe place. Without it, you will lose the ability to decrypt your files!**
|
||||
|
||||
Afterwards you can copy the access grant and then start the startup of the transfer.sh endpoint.
|
||||
For enhanced security its recommended to provide both the access grant and the bucket name as ENV Variables.
|
||||
Afterwards, you can copy the access grant and then start the startup of the transfer.sh endpoint.
|
||||
It is recommended to provide both the access grant and the bucket name as ENV Variables for enhanced security.
|
||||
|
||||
Example:
|
||||
```
|
||||
@@ -196,8 +197,7 @@ For the usage with Google drive, you need to specify the following options:
|
||||
|
||||
### Creating Gdrive Client Json
|
||||
|
||||
You need to create a Oauth Client id from console.cloud.google.com
|
||||
download the file and place into a safe directory
|
||||
You need to create an OAuth Client id from console.cloud.google.com, download the file, and place it into a safe directory.
|
||||
|
||||
### Usage example
|
||||
|
||||
@@ -221,7 +221,7 @@ Contributions are welcome.
|
||||
|
||||
**Stefan Benten**
|
||||
|
||||
## Copyright and license
|
||||
## Copyright and License
|
||||
|
||||
Code and documentation copyright 2011-2018 Remco Verhoef.
|
||||
Code and documentation copyright 2018-2020 Andrea Spacca.
|
||||
|
10
cmd/cmd.go
10
cmd/cmd.go
@@ -98,6 +98,12 @@ var globalFlags = []cli.Flag{
|
||||
Value: "",
|
||||
EnvVar: "PROXY_PORT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "email-contact",
|
||||
Usage: "email address to link in Contact Us (front end)",
|
||||
Value: "",
|
||||
EnvVar: "EMAIL_CONTACT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "ga-key",
|
||||
Usage: "key for google analytics (front end)",
|
||||
@@ -348,6 +354,10 @@ func New() *Cmd {
|
||||
options = append(options, server.ProxyPort(v))
|
||||
}
|
||||
|
||||
if v := c.String("email-contact"); v != "" {
|
||||
options = append(options, server.EmailContact(v))
|
||||
}
|
||||
|
||||
if v := c.String("ga-key"); v != "" {
|
||||
options = append(options, server.GoogleAnalytics(v))
|
||||
}
|
||||
|
89
examples.md
89
examples.md
@@ -5,6 +5,7 @@
|
||||
* [Archiving and backups](#archiving-and-backups)
|
||||
* [Encrypting and decrypting](#encrypting-and-decrypting)
|
||||
* [Scanning for viruses](#scanning-for-viruses)
|
||||
* [Uploading and copy download command](#uploading-and-copy-download-command)
|
||||
|
||||
## Aliases
|
||||
<a name="aliases"/>
|
||||
@@ -173,4 +174,90 @@ $ curl -X PUT --upload-file ./eicar.com https://transfer.sh/eicar.com/scan
|
||||
### Upload malware to VirusTotal, get a permalink in return
|
||||
```bash
|
||||
$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal
|
||||
```
|
||||
```
|
||||
## Uploading and copy download command
|
||||
|
||||
Download commands can be automatically copied to the clipboard after files are uploaded using transfer.sh.
|
||||
|
||||
It was designed for Linux or macOS.
|
||||
|
||||
### 1. Install xclip or xsel for Linux, macOS skips this step
|
||||
|
||||
- install xclip see https://command-not-found.com/xclip
|
||||
|
||||
- install xsel see https://command-not-found.com/xsel
|
||||
|
||||
Install later, add pbcopy and pbpaste to .bashrc or .zshrc or its equivalent.
|
||||
|
||||
- If use xclip, paste the following lines:
|
||||
|
||||
```sh
|
||||
alias pbcopy='xclip -selection clipboard'
|
||||
alias pbpaste='xclip -selection clipboard -o'
|
||||
```
|
||||
|
||||
- If use xsel, paste the following lines:
|
||||
|
||||
```sh
|
||||
alias pbcopy='xsel --clipboard --input'
|
||||
alias pbpaste='xsel --clipboard --output'
|
||||
```
|
||||
|
||||
### 2. Add Uploading and copy download command shell function
|
||||
|
||||
1. Open .bashrc or .zshrc or its equivalent.
|
||||
|
||||
2. Add the following shell script:
|
||||
|
||||
```sh
|
||||
transfer() {
|
||||
curl --progress-bar --upload-file "$1" https://transfer.sh/$(basename "$1") | pbcopy;
|
||||
echo "1) Download link:"
|
||||
echo "$(pbpaste)"
|
||||
|
||||
echo "\n2) Linux or macOS download command:"
|
||||
linux_macos_download_command="wget $(pbpaste)"
|
||||
echo $linux_macos_download_command
|
||||
|
||||
echo "\n3) Windows download command:"
|
||||
windows_download_command="Invoke-WebRequest -Uri "$(pbpaste)" -OutFile $(basename $1)"
|
||||
echo $windows_download_command
|
||||
|
||||
case $2 in
|
||||
l|m) echo $linux_macos_download_command | pbcopy
|
||||
;;
|
||||
w) echo $windows_download_command | pbcopy
|
||||
;;
|
||||
esac
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Test
|
||||
|
||||
The transfer command has two parameters:
|
||||
|
||||
1. The first parameter is the path to upload the file.
|
||||
|
||||
2. The second parameter indicates which system's download command is copied. optional:
|
||||
|
||||
- This parameter is empty to copy the download link.
|
||||
|
||||
- `l` or `m` copy the Linux or macOS command that downloaded the file.
|
||||
|
||||
- `w` copy the Windows command that downloaded the file.
|
||||
|
||||
For example, The command to download the file on Windows will be copied:
|
||||
|
||||
```sh
|
||||
$ transfer ~/temp/a.log w
|
||||
######################################################################## 100.0%
|
||||
1) Download link:
|
||||
https://transfer.sh/y0qr2c/a.log
|
||||
|
||||
2) Linux or macOS download command:
|
||||
wget https://transfer.sh/y0qr2c/a.log
|
||||
|
||||
3) Windows download command:
|
||||
Invoke-WebRequest -Uri https://transfer.sh/y0qr2c/a.log -OutFile a.log
|
||||
```
|
||||
|
41
flake.lock
generated
Normal file
41
flake.lock
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1631561581,
|
||||
"narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1632470817,
|
||||
"narHash": "sha256-tGyOesdpqQEVqlmVeElsC98OJ2GDy+LNaCThSby/GQM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "39e8ec2db68b863543bd377e44fbe02f8d05864e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
212
flake.nix
Normal file
212
flake.nix
Normal file
@@ -0,0 +1,212 @@
|
||||
{
|
||||
description = "Transfer.sh";
|
||||
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
let
|
||||
transfer-sh = pkgs: pkgs.buildGoModule {
|
||||
src = self;
|
||||
name = "transfer.sh";
|
||||
vendorSha256 = "sha256-bgQUMiC33yVorcKOWhegT1/YU+fvxsz2pkeRvjf3R7g=";
|
||||
};
|
||||
in
|
||||
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
rec {
|
||||
packages = flake-utils.lib.flattenTree {
|
||||
transfer-sh = transfer-sh pkgs;
|
||||
};
|
||||
defaultPackage = packages.transfer-sh;
|
||||
apps.transfer-sh = flake-utils.lib.mkApp { drv = packages.transfer-sh; };
|
||||
defaultApp = apps.transfer-sh;
|
||||
}
|
||||
) // rec {
|
||||
|
||||
nixosModules = {
|
||||
transfer-sh = { config, lib, pkgs, ... }: with lib; let
|
||||
RUNTIME_DIR = "/var/lib/transfer.sh";
|
||||
cfg = config.services.transfer-sh;
|
||||
|
||||
general_options = {
|
||||
|
||||
enable = mkEnableOption "Transfer.sh service";
|
||||
listener = mkOption { default = 80; type = types.int; description = "port to use for http (:80)"; };
|
||||
profile-listener = mkOption { default = 6060; type = types.int; description = "port to use for profiler (:6060)"; };
|
||||
force-https = mkOption { type = types.nullOr types.bool; description = "redirect to https"; };
|
||||
tls-listener = mkOption { default = 443; type = types.int; description = "port to use for https (:443)"; };
|
||||
tls-listener-only = mkOption { type = types.nullOr types.bool; description = "flag to enable tls listener only"; };
|
||||
tls-cert-file = mkOption { type = types.nullOr types.str; description = "path to tls certificate"; };
|
||||
tls-private-key = mkOption { type = types.nullOr types.str; description = "path to tls private key "; };
|
||||
http-auth-user = mkOption { type = types.nullOr types.str; description = "user for basic http auth on upload"; };
|
||||
http-auth-pass = mkOption { type = types.nullOr types.str; description = "pass for basic http auth on upload"; };
|
||||
ip-whitelist = mkOption { type = types.nullOr types.str; description = "comma separated list of ips allowed to connect to the service"; };
|
||||
ip-blacklist = mkOption { type = types.nullOr types.str; description = "comma separated list of ips not allowed to connect to the service"; };
|
||||
temp-path = mkOption { type = types.nullOr types.str; description = "path to temp folder"; };
|
||||
web-path = mkOption { type = types.nullOr types.str; description = "path to static web files (for development or custom front end)"; };
|
||||
proxy-path = mkOption { type = types.nullOr types.str; description = "path prefix when service is run behind a proxy"; };
|
||||
proxy-port = mkOption { type = types.nullOr types.str; description = "port of the proxy when the service is run behind a proxy"; };
|
||||
ga-key = mkOption { type = types.nullOr types.str; description = "google analytics key for the front end"; };
|
||||
email-contact = mkOption { type = types.nullOr types.str; description = "email contact for the front end"; };
|
||||
uservoice-key = mkOption { type = types.nullOr types.str; description = "user voice key for the front end"; };
|
||||
lets-encrypt-hosts = mkOption { type = types.nullOr (types.listOf types.str); description = "hosts to use for lets encrypt certificates"; };
|
||||
log = mkOption { type = types.nullOr types.str; description = "path to log file"; };
|
||||
cors-domains = mkOption { type = types.nullOr (types.listOf types.str); description = "comma separated list of domains for CORS, setting it enable CORS "; };
|
||||
clamav-host = mkOption { type = types.nullOr types.str; description = "host for clamav feature"; };
|
||||
rate-limit = mkOption { type = types.nullOr types.int; description = "request per minute"; };
|
||||
max-upload-size = mkOption { type = types.nullOr types.int; description = "max upload size in kilobytes "; };
|
||||
purge-days = mkOption { type = types.nullOr types.int; description = "number of days after the uploads are purged automatically "; };
|
||||
random-token-length = mkOption { type = types.nullOr types.int; description = "length of the random token for the upload path (double the size for delete path)"; };
|
||||
|
||||
};
|
||||
|
||||
provider_options = {
|
||||
|
||||
aws = {
|
||||
enable = mkEnableOption "Enable AWS backend";
|
||||
aws-access-key = mkOption { type = types.str; description = "aws access key"; };
|
||||
aws-secret-key = mkOption { type = types.str; description = "aws secret key"; };
|
||||
bucket = mkOption { type = types.str; description = "aws bucket "; };
|
||||
s3-endpoint = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
Custom S3 endpoint.
|
||||
If you specify the s3-region, you don't need to set the endpoint URL since the correct endpoint will used automatically.
|
||||
'';
|
||||
};
|
||||
s3-region = mkOption { type = types.str; description = "region of the s3 bucket eu-west-"; };
|
||||
s3-no-multipart = mkOption { type = types.nullOr types.bool; description = "disables s3 multipart upload "; };
|
||||
s3-path-style = mkOption { type = types.nullOr types.str; description = "Forces path style URLs, required for Minio. "; };
|
||||
};
|
||||
|
||||
storj = {
|
||||
enable = mkEnableOption "Enable storj backend";
|
||||
storj-access = mkOption { type = types.str; description = "Access for the project"; };
|
||||
storj-bucket = mkOption { type = types.str; description = "Bucket to use within the project"; };
|
||||
};
|
||||
|
||||
gdrive = {
|
||||
enable = mkEnableOption "Enable gdrive backend";
|
||||
gdrive-client-json = mkOption { type = types.str; description = "oauth client json config for gdrive provider"; };
|
||||
gdrive-chunk-size = mkOption { default = 8; type = types.nullOr types.int; description = "chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB)"; };
|
||||
basedir = mkOption { type = types.str; description = "path storage for gdrive provider"; default = "${cfg.stateDir}/store"; };
|
||||
purge-interval = mkOption { type = types.nullOr types.int; description = "interval in hours to run the automatic purge for (not applicable to S3 and Storj)"; };
|
||||
|
||||
};
|
||||
|
||||
local = {
|
||||
enable = mkEnableOption "Enable local backend";
|
||||
basedir = mkOption { type = types.str; description = "path storage for local provider"; default = "${cfg.stateDir}/store"; };
|
||||
purge-interval = mkOption { type = types.nullOr types.int; description = "interval in hours to run the automatic purge for (not applicable to S3 and Storj)"; };
|
||||
};
|
||||
|
||||
};
|
||||
in
|
||||
{
|
||||
options.services.transfer-sh = fold recursiveUpdate {} [
|
||||
general_options
|
||||
{
|
||||
provider = provider_options;
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = "User to run the service under";
|
||||
default = "transfer.sh";
|
||||
};
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
description = "Group to run the service under";
|
||||
default = "transfer.sh";
|
||||
};
|
||||
stateDir = mkOption {
|
||||
type = types.path;
|
||||
description = "Variable state directory";
|
||||
default = RUNTIME_DIR;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
config = let
|
||||
|
||||
mkFlags = cfg: options:
|
||||
let
|
||||
mkBoolFlag = option: if cfg.${option} then [ "--${option}" ] else [];
|
||||
mkFlag = option:
|
||||
if isBool cfg.${option}
|
||||
then mkBoolFlag option
|
||||
else [ "--${option}" "${cfg.${option}}" ];
|
||||
|
||||
in
|
||||
lists.flatten (map (mkFlag) (filter (option: cfg.${option} != null && option != "enable") options));
|
||||
|
||||
aws-config = (mkFlags cfg.provider.aws (attrNames provider_options)) ++ [ "--provider" "aws" ];
|
||||
gdrive-config = mkFlags cfg.provider.gdrive (attrNames provider_options.gdrive) ++ [ "--provider" "gdrive" ];
|
||||
storj-config = mkFlags cfg.provider.storj (attrNames provider_options.storj) ++ [ "--provider" "storj" ];
|
||||
local-config = mkFlags cfg.provider.local (attrNames provider_options.local) ++ [ "--provider" "local" ];
|
||||
|
||||
general-config = concatStringsSep " " (mkFlags cfg (attrNames general_options));
|
||||
provider-config = concatStringsSep " " (
|
||||
if cfg.provider.aws.enable && !cfg.provider.storj.enable && !cfg.provider.gdrive.enable && !cfg.provider.local.enable then aws-config
|
||||
else if !cfg.provider.aws.enable && cfg.provider.storj.enable && !cfg.provider.gdrive.enable && !cfg.provider.local.enable then storj-config
|
||||
else if !cfg.provider.aws.enable && !cfg.provider.storj.enable && cfg.provider.gdrive.enable && !cfg.provider.local.enable then gdrive-config
|
||||
else if !cfg.provider.aws.enable && !cfg.provider.storj.enable && !cfg.provider.gdrive.enable && cfg.provider.local.enable then local-config
|
||||
else throw "transfer.sh requires exactly one provider (aws, storj, gdrive, local)"
|
||||
);
|
||||
|
||||
in
|
||||
lib.mkIf cfg.enable
|
||||
{
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.stateDir} 0750 ${cfg.user} ${cfg.group} - -"
|
||||
] ++ optional cfg.provider.gdrive.enable cfg.provider.gdrive.basedir
|
||||
++ optional cfg.provider.local.enable cfg.provider.local.basedir;
|
||||
|
||||
systemd.services.transfer-sh = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = "${transfer-sh pkgs}/bin/transfer.sh ${general-config} ${provider-config} ";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ cfg.listener cfg.profile-listener cfg.tls-listener ];
|
||||
};
|
||||
};
|
||||
|
||||
default = { self, pkgs, ... }: {
|
||||
imports = [ nixosModules.transfer-sh ];
|
||||
# Network configuration.
|
||||
|
||||
# useDHCP is generally considered to better be turned off in favor
|
||||
# of <adapter>.useDHCP
|
||||
networking.useDHCP = false;
|
||||
networking.firewall.allowedTCPPorts = [];
|
||||
|
||||
# Enable the inventaire server.
|
||||
services.transfer-sh = {
|
||||
enable = true;
|
||||
provider.local = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
nixosConfigurations."container" = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
nixosModules.default
|
||||
({ ... }: { boot.isContainer = true; })
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
}
|
7
go.mod
7
go.mod
@@ -8,10 +8,11 @@ require (
|
||||
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2
|
||||
github.com/aws/aws-sdk-go v1.37.14
|
||||
github.com/calebcase/tmpfile v1.0.2 // indirect
|
||||
github.com/chris-ramon/douceur v0.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
||||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
|
||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311
|
||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20211215083008-31e11925a9d3
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/garyburd/redigo v1.6.2 // indirect
|
||||
@@ -19,7 +20,7 @@ require (
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.5
|
||||
github.com/microcosm-cc/bluemonday v1.0.16
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
@@ -27,7 +28,7 @@ require (
|
||||
github.com/urfave/cli v1.22.5
|
||||
go.opencensus.io v0.22.6 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99
|
||||
google.golang.org/api v0.40.0
|
||||
google.golang.org/genproto v0.0.0-20210218151259-fe80b386bf06 // indirect
|
||||
|
10
go.sum
10
go.sum
@@ -88,6 +88,8 @@ github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a h1:+N7
|
||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
|
||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311 h1:/Rwuhcp8ZLUauWajAgMyy6AiVbobvD52I+/OnzThK0A=
|
||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
|
||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20211215083008-31e11925a9d3 h1:HyfU90/8y9S5IkHTQgIfzs4dT3iagbJUJLncCsSwZCI=
|
||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20211215083008-31e11925a9d3/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -219,6 +221,8 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/microcosm-cc/bluemonday v1.0.5 h1:cF59UCKMmmUgqN1baLvqU/B1ZsMori+duLVTLpgiG3w=
|
||||
github.com/microcosm-cc/bluemonday v1.0.5/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
|
||||
github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc=
|
||||
github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||
@@ -356,6 +360,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -419,6 +425,8 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe h1:WdX7u8s3yOigWAhHEaDl8r9G+4XwFQEQFtBMYyN+kXQ=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -428,6 +436,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@@ -57,6 +57,7 @@ func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
|
||||
c := clamd.NewClamd(s.ClamAVDaemonHost)
|
||||
|
||||
abort := make(chan bool)
|
||||
defer close(abort)
|
||||
response, err := c.ScanStream(reader, abort)
|
||||
if err != nil {
|
||||
s.logger.Printf("%s", err.Error())
|
||||
@@ -70,6 +71,4 @@ func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
|
||||
case <-time.After(time.Second * 60):
|
||||
abort <- true
|
||||
}
|
||||
|
||||
close(abort)
|
||||
}
|
||||
|
@@ -37,7 +37,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
blackfriday "github.com/russross/blackfriday/v2"
|
||||
"html"
|
||||
html_template "html/template"
|
||||
"io"
|
||||
@@ -54,9 +53,12 @@ import (
|
||||
text_template "text/template"
|
||||
"time"
|
||||
|
||||
blackfriday "github.com/russross/blackfriday/v2"
|
||||
|
||||
"net"
|
||||
|
||||
"encoding/base64"
|
||||
|
||||
web "github.com/dutchcoders/transfer.sh-web"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
@@ -251,6 +253,7 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := struct {
|
||||
Hostname string
|
||||
WebAddress string
|
||||
EmailContact string
|
||||
GAKey string
|
||||
UserVoiceKey string
|
||||
PurgeTime string
|
||||
@@ -260,6 +263,7 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}{
|
||||
hostname,
|
||||
webAddress,
|
||||
s.emailContact,
|
||||
s.gaKey,
|
||||
s.userVoiceKey,
|
||||
purgeTime,
|
||||
@@ -286,7 +290,7 @@ func (s *Server) notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func sanitize(fileName string) string {
|
||||
return path.Clean(path.Base(fileName))
|
||||
return path.Base(fileName)
|
||||
}
|
||||
|
||||
func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -342,6 +346,11 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
reader, err = os.Open(file.Name())
|
||||
if err != nil {
|
||||
s.logger.Printf("%s", err.Error())
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
reader = bytes.NewReader(b.Bytes())
|
||||
}
|
||||
@@ -491,6 +500,11 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
reader, err = os.Open(file.Name())
|
||||
if err != nil {
|
||||
s.logger.Printf("%s", err.Error())
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
reader = bytes.NewReader(b.Bytes())
|
||||
}
|
||||
@@ -521,6 +535,10 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
||||
s.logger.Printf("%s", err.Error())
|
||||
http.Error(w, errors.New("Could not encode metadata").Error(), 500)
|
||||
return
|
||||
} else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) {
|
||||
s.logger.Print("Invalid MaxDate")
|
||||
http.Error(w, errors.New("Invalid MaxDate, make sure Max-Days is smaller than 290 years").Error(), 400)
|
||||
return
|
||||
} else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
|
||||
s.logger.Printf("%s", err.Error())
|
||||
http.Error(w, errors.New("Could not save metadata").Error(), 500)
|
||||
@@ -614,25 +632,24 @@ func getURL(r *http.Request, proxyPort string) *url.URL {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
|
||||
if u.Host == "" {
|
||||
host, port, err := net.SplitHostPort(r.Host)
|
||||
if err != nil {
|
||||
host = r.Host
|
||||
port = ""
|
||||
}
|
||||
if len(proxyPort) != 0 {
|
||||
port = proxyPort
|
||||
}
|
||||
if len(port) == 0 {
|
||||
host, port, err := net.SplitHostPort(r.Host)
|
||||
if err != nil {
|
||||
host = r.Host
|
||||
port = ""
|
||||
}
|
||||
if len(proxyPort) != 0 {
|
||||
port = proxyPort
|
||||
}
|
||||
|
||||
if len(port) == 0 {
|
||||
u.Host = host
|
||||
} else {
|
||||
if port == "80" && u.Scheme == "http" {
|
||||
u.Host = host
|
||||
} else if port == "443" && u.Scheme == "https" {
|
||||
u.Host = host
|
||||
} else {
|
||||
if port == "80" && u.Scheme == "http" {
|
||||
u.Host = host
|
||||
} else if port == "443" && u.Scheme == "https" {
|
||||
u.Host = host
|
||||
} else {
|
||||
u.Host = net.JoinHostPort(host, port)
|
||||
}
|
||||
u.Host = net.JoinHostPort(host, port)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,10 +1100,22 @@ func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {
|
||||
} else if strings.HasSuffix(ipAddrFromRemoteAddr(r.Host), ".onion") {
|
||||
// .onion addresses cannot get a valid certificate, so don't redirect
|
||||
} else if r.Header.Get("X-Forwarded-Proto") == "https" {
|
||||
} else if r.URL.Scheme == "https" {
|
||||
} else if r.TLS != nil {
|
||||
} else {
|
||||
u := getURL(r, s.proxyPort)
|
||||
u.Scheme = "https"
|
||||
if len(s.proxyPort) == 0 && len(s.TLSListenerString) > 0 {
|
||||
_, port, err := net.SplitHostPort(s.TLSListenerString)
|
||||
if err != nil || port == "443" {
|
||||
port = ""
|
||||
}
|
||||
|
||||
if len(port) > 0 {
|
||||
u.Host = net.JoinHostPort(u.Hostname(), port)
|
||||
} else {
|
||||
u.Host = u.Hostname()
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
|
||||
return
|
||||
|
@@ -99,6 +99,13 @@ func CorsDomains(s string) OptionFn {
|
||||
|
||||
}
|
||||
|
||||
// EmailContact sets email contact
|
||||
func EmailContact(emailContact string) OptionFn {
|
||||
return func(srvr *Server) {
|
||||
srvr.emailContact = emailContact
|
||||
}
|
||||
}
|
||||
|
||||
// GoogleAnalytics sets GA key
|
||||
func GoogleAnalytics(gaKey string) OptionFn {
|
||||
return func(srvr *Server) {
|
||||
@@ -339,6 +346,7 @@ type Server struct {
|
||||
webPath string
|
||||
proxyPath string
|
||||
proxyPort string
|
||||
emailContact string
|
||||
gaKey string
|
||||
userVoiceKey string
|
||||
|
||||
|
Reference in New Issue
Block a user