Compare commits

...

4 Commits

Author SHA1 Message Date
Stefan Benten
92324798d5 server: adding no-store header (#476)
In order to prevent viewing content, which max-download rate has been reached,
we need to ensure the data is not stored locally in a browser cache.
To achieve this, we set the Cache-Control Setting to "no-store" according to:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

fixes #470
2022-04-10 12:13:06 +02:00
Ramon Fischer
b30b296ac8 correct typo (#480) 2022-04-06 01:33:40 +02:00
jeanluc
bb0891cd7d Docker: Allow selection of (unprivileged) UID/GID at build time (#418)
* Docker: use custom non-root UID/GID (build-arg)
2022-04-03 14:55:15 +02:00
Andrea Spacca
9c31ceb2c5 Update README.md 2022-04-03 06:17:51 +02:00
6 changed files with 99 additions and 20 deletions

View File

@@ -34,9 +34,11 @@ jobs:
fi fi
TAGS="--tag ${DOCKER_IMAGE}:${VERSION}" TAGS="--tag ${DOCKER_IMAGE}:${VERSION}"
TAGS_NOROOT="--tag ${DOCKER_IMAGE}:${VERSION}-noroot"
if [ $VERSION = edge -o $VERSION = nightly ]; then if [ $VERSION = edge -o $VERSION = nightly ]; then
TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest" TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest"
TAGS_NOROOT="$TAGS_NOROOT --tag ${DOCKER_IMAGE}:latest-noroot"
fi fi
echo ::set-output name=docker_image::${DOCKER_IMAGE} echo ::set-output name=docker_image::${DOCKER_IMAGE}
@@ -46,6 +48,12 @@ jobs:
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg VCS_REF=${GITHUB_SHA::8} \ --build-arg VCS_REF=${GITHUB_SHA::8} \
${TAGS} . ${TAGS} .
echo ::set-output name=buildx_args_noroot::--platform ${DOCKER_PLATFORMS} \
--build-arg VERSION=${VERSION} \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg VCS_REF=${GITHUB_SHA::8} \
--build-arg RUNAS=noroot \
${TAGS_NOROOT} .
- -
name: Set up QEMU name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1
@@ -64,6 +72,7 @@ jobs:
name: Docker Buildx (build) name: Docker Buildx (build)
run: | run: |
docker buildx build --no-cache --pull --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }} docker buildx build --no-cache --pull --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }}
docker buildx build --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args_noroot }}
- -
name: Docker Login name: Docker Login
if: success() && github.event_name != 'pull_request' if: success() && github.event_name != 'pull_request'
@@ -77,11 +86,13 @@ jobs:
if: success() && github.event_name != 'pull_request' if: success() && github.event_name != 'pull_request'
run: | run: |
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }} docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }}
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args_noroot }}
- -
name: Docker Check Manifest name: Docker Check Manifest
if: always() && github.event_name != 'pull_request' if: always() && github.event_name != 'pull_request'
run: | run: |
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }} docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}-noroot
- -
name: Clear name: Clear
if: always() && github.event_name != 'pull_request' if: always() && github.event_name != 'pull_request'

View File

@@ -21,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. 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

View File

@@ -14,12 +14,27 @@ ENV GO111MODULE=on
# build & install server # build & install server
RUN CGO_ENABLED=0 go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=$(git describe --tags) -a -s -w -extldflags '-static'" -o /go/bin/transfersh RUN CGO_ENABLED=0 go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=$(git describe --tags) -a -s -w -extldflags '-static'" -o /go/bin/transfersh
ARG PUID=5000 \
PGID=5000 \
RUNAS
RUN mkdir -p /tmp/useradd && \
if [ ! -z "$RUNAS" ]; then \
echo "${RUNAS}:x:${PUID}:${PGID}::/nonexistent:/sbin/nologin" >> /tmp/useradd/passwd && \
echo "${RUNAS}:!:::::::" >> /tmp/useradd/shadow && \
echo "${RUNAS}:x:${PGID}:" >> /tmp/useradd/group && \
echo "${RUNAS}:!::" >> /tmp/useradd/groupshadow; else touch /tmp/useradd/unused; fi
FROM scratch AS final FROM scratch AS final
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>" LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"
ARG RUNAS
COPY --from=build /go/bin/transfersh /go/bin/transfersh COPY --from=build /tmp/useradd/* /etc/
COPY --from=build --chown=${RUNAS} /go/bin/transfersh /go/bin/transfersh
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
USER ${RUNAS}
ENTRYPOINT ["/go/bin/transfersh", "--listener", ":8080"] ENTRYPOINT ["/go/bin/transfersh", "--listener", ":8080"]
EXPOSE 8080 EXPOSE 8080

View File

@@ -140,12 +140,31 @@ $ go build -o transfersh main.go
## Docker ## Docker
For easy deployment, we've created a Docker container. For easy deployment, we've created an official Docker container. There are two variants, differing only by which user runs the process.
The default one will run as `root`:
```bash ```bash
docker run --publish 8080:8080 dutchcoders/transfer.sh:latest --provider local --basedir /tmp/ docker run --publish 8080:8080 dutchcoders/transfer.sh:latest --provider local --basedir /tmp/
``` ```
The one tagged with the suffix `-noroot` will use `5000` as both UID and GID:
```bash
docker run --publish 8080:8080 dutchcoders/transfer.sh:latest-noroot --provider local --basedir /tmp/
```
### Building the Container
You can also build the container yourself. This allows you to choose which UID/GID will be used, e.g. when using NFS mounts:
```bash
# Build arguments:
# * RUNAS: If empty, the container will run as root.
# Set this to anything to enable UID/GID selection.
# * PUID: UID of the process. Needs RUNAS != "". Defaults to 5000.
# * PGID: GID of the process. Needs RUNAS != "". Defaults to 5000.
docker build -t transfer.sh-noroot --build-arg RUNAS=doesntmatter --build-arg PUID=1337 --build-arg PGID=1338 .
```
## S3 Usage ## S3 Usage
For the usage with a AWS S3 Bucket, you just need to specify the following options: For the usage with a AWS S3 Bucket, you just need to specify the following options:
@@ -204,6 +223,41 @@ You need to create an OAuth Client id from console.cloud.google.com, download th
```go run main.go --provider gdrive --basedir /tmp/ --gdrive-client-json-filepath /[credential_dir] --gdrive-local-config-path [directory_to_save_config] ``` ```go run main.go --provider gdrive --basedir /tmp/ --gdrive-client-json-filepath /[credential_dir] --gdrive-local-config-path [directory_to_save_config] ```
## Shell functions
### Bash and zsh (multiple files uploaded as zip archive)
##### Add this to .bashrc or .zshrc or its equivalent
```bash
transfer(){ if [ $# -eq 0 ];then echo "No arguments specified.\nUsage:\n transfer <file|directory>\n ... | transfer <file_name>">&2;return 1;fi;if tty -s;then file="$1";file_name=$(basename "$file");if [ ! -e "$file" ];then echo "$file: No such file or directory">&2;return 1;fi;if [ -d "$file" ];then file_name="$file_name.zip" ,;(cd "$file"&&zip -r -q - .)|curl --progress-bar --upload-file "-" "https://transfer.sh/$file_name"|tee /dev/null,;else cat "$file"|curl --progress-bar --upload-file "-" "https://transfer.sh/$file_name"|tee /dev/null;fi;else file_name=$1;curl --progress-bar --upload-file "-" "https://transfer.sh/$file_name"|tee /dev/null;fi;}
```
#### Now you can use transfer function
```
$ transfer hello.txt
```
### Zsh (with delete url outpu)
##### Add this to .zshrc or its equivalent
```bash
transfer()
{
local file="${1}"
local filename="${file##*/}"
# show delete link from the response header after upload. the command "sed" is necessary to clean up the output, "gsub()" in "awk" does not work.
curl --request PUT --progress-bar --dump-header - --upload-file "${file}" "https://transfer.sh/${filename}" | sed "s/#//g" | awk '/x-url-delete/ { print "Delete command: curl --request DELETE " $2 } END{ print "Download link: " $1 }'
}
```
#### Sample ouput
```bash
$ transfer image.img
######################################################################################################################################################################################################################################## 100.0%
Delete command: curl --request DELETE https://transfer.sh/Ge9cuW/image.img/<some_delete_token>
Download link: https://transfer.sh/Ge9cuW/image.img
```
## Contributions ## Contributions
Contributions are welcome. Contributions are welcome.

View File

@@ -781,8 +781,7 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
zipfilename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano())) zipfilename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano()))
w.Header().Set("Content-Type", "application/zip") w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", zipfilename)) commonHeader(w, zipfilename)
w.Header().Set("Connection", "close")
zw := zip.NewWriter(w) zw := zip.NewWriter(w)
@@ -848,8 +847,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
tarfilename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano())) tarfilename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano()))
w.Header().Set("Content-Type", "application/x-gzip") w.Header().Set("Content-Type", "application/x-gzip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename)) commonHeader(w, tarfilename)
w.Header().Set("Connection", "close")
gw := gzip.NewWriter(w) gw := gzip.NewWriter(w)
defer CloseCheck(gw.Close) defer CloseCheck(gw.Close)
@@ -910,8 +908,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
tarfilename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano())) tarfilename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano()))
w.Header().Set("Content-Type", "application/x-tar") w.Header().Set("Content-Type", "application/x-tar")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename)) commonHeader(w, tarfilename)
w.Header().Set("Connection", "close")
zw := tar.NewWriter(w) zw := tar.NewWriter(w)
defer CloseCheck(zw.Close) defer CloseCheck(zw.Close)
@@ -1037,6 +1034,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10)) w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10))
w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, filename)) w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, filename))
w.Header().Set("Connection", "keep-alive") w.Header().Set("Connection", "keep-alive")
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("X-Remaining-Downloads", remainingDownloads) w.Header().Set("X-Remaining-Downloads", remainingDownloads)
w.Header().Set("X-Remaining-Days", remainingDays) w.Header().Set("X-Remaining-Days", remainingDays)
@@ -1072,6 +1070,12 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
func commonHeader(w http.ResponseWriter, filename string) {
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
w.Header().Set("Connection", "close")
w.Header().Set("Cache-Control", "no-store")
}
// RedirectHandler handles redirect // RedirectHandler handles redirect
func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {

View File

@@ -25,39 +25,34 @@ THE SOFTWARE.
package server package server
import ( import (
"context"
crypto_rand "crypto/rand" crypto_rand "crypto/rand"
"crypto/tls"
"encoding/binary" "encoding/binary"
"errors" "errors"
gorillaHandlers "github.com/gorilla/handlers"
"log" "log"
"math/rand" "math/rand"
"mime" "mime"
"net/http" "net/http"
_ "net/http/pprof"
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
"time" "time"
context "golang.org/x/net/context"
"github.com/PuerkitoBio/ghost/handlers" "github.com/PuerkitoBio/ghost/handlers"
"github.com/VojtechVitek/ratelimit" "github.com/VojtechVitek/ratelimit"
"github.com/VojtechVitek/ratelimit/memory" "github.com/VojtechVitek/ratelimit/memory"
gorillaHandlers "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"golang.org/x/crypto/acme/autocert"
// import pprof
_ "net/http/pprof"
"crypto/tls"
web "github.com/dutchcoders/transfer.sh-web" web "github.com/dutchcoders/transfer.sh-web"
assetfs "github.com/elazarl/go-bindata-assetfs" assetfs "github.com/elazarl/go-bindata-assetfs"
autocert "golang.org/x/crypto/acme/autocert"
"path/filepath"
) )
// parse request with maximum memory of _24Kilobits // parse request with maximum memory of _24Kilobits