Sign inSign up

sklieren/yeeti

By sklieren

•Updated 3 days ago

yeeti - Secure, encrypted file sharing with end-to-end encryption directly in your browser.

Image
Security
Web servers
2

228

sklieren/yeeti repository overview

⁠Yeeti

yeetiLogo

Secure, encrypted file sharing with end-to-end encryption directly in your browser.

Yeeti is an open-source solution for anonymous file sharing where no one, not even the server administrator, has access to your content. All encryption and decryption happens client-side in the browser, keeping both metadata and file contents protected at all times.

For encryption, Yeeti uses openpgpjs⁠ in version v6.3.2.

⁠How does it work

tl;dr: User selects file and password, clicks upload. File gets encrypted and uploaded. Once the upload is done they get a download link they can share with whomever they like. Clicking that link allows for downloading and decrypting the file - given you've got the password.

⁠Upload
  1. An anonymous user selects a file and chooses a password (the stronger the better)
  2. As they click on "Upload", the client instructs a Worker thread to establish a WebSocket connection to the Yeeti backend
  3. The Worker thread encrypts the file's metadata (file name, mime type, original file size) using the user provided password and sends it through the WebSocket
  4. Upon receiving the encrypted metadata, the Yeeti backend prepares a file upload by:
    • enerating an upload / file ID (both length and valid characters/digits can be customized via environment variables)
    • Inserting a row in the sqlite db with the file ID, encrypted metadata and a timestamp for deleting the file
    • opening a WriteStream to YEETI_UPLOAD_DIR/<fileID>
      • e.g. /uploads/01a10cf7-1e48-70f7-a4cc-f7bfde5817a4
  5. Yeeti backend sends a message containing the file ID to the client Worker
  6. The Worker thread starts encrypting the file content while streaming the encrypted data into the WebSocket
  7. The backend writes the encrypted content it receives directly into the file
  8. Once the client ist done, it sends an EOF, to which the backend responds with DONE.
  9. The client now displays the file's download link for the user to copy (and share)
⁠Download
  1. User opens a Yeeti download link, which contains a file ID
  2. The UI attempts to fetch the encrypted metadata from the Yeeti backend
  3. User gets prompted for a password
  4. Upon entering the password and hitting "Decrypt metadata", metadata gets decrypted in a Worker thread
  5. The UI displays file name, file type and original file size
  6. User hits "Decrypt and download file"
  7. The file gets downloaded and decryptped on the fly

⁠Work in Progress

First of all: While I claim that Yeeti is open-source, I haven't published it's code, yet. This is going to take place very soon. Until then I must advise you not to use Yeeti, since you cannot verify it's integrity, yet.

There are some key features still missing, but already on the roadmap:

  • Proper error handling
  • Clean up job for automatically deleting files

Once these are done, I plan to publish Yeeti on Codeberg⁠. Stay tuned!

⁠(almost) zero dependencies

openpgp

As mentioned above, Yeeti utilizes openpgpjs and delivers a minified version of it (taken from openpgp on unpkg.com⁠). For privacy reasons, I deliberately avoid using a CDN in Yeeti.

WebSocket server

For being able to stream the file encryption result directly into an upload process, Yeeti relies on a WebSocket connection per upload. Since I haven't found the time nor the motivation to implement the WebSocket protocol on top of node js' built-in httpServer myself, I'm using the ws⁠ npm package, which itself got zero dependencies.

For now, during the Yeeti Docker build npm ci runs only to install ws.

⁠Getting started

To run Yeeti via Docker (recommended), follow the steps below.

⁠Create a directory for Yeeti and navigate into it
mkdir -p yeeti/data
cd yeeti
⁠Data directory permissions

By default Yeeti runs under user yeeti with userid 1001, so you'll need to chown the created data dir for Yeeti to be able to save uploads and store the sqlite database:

chown -R 1001:1001 data/
⁠Run Yeeti
⁠Option A: Docker compose

Example compose file (replace the value of YEETI_BASE_URL):

name: yeeti
services:
    yeeti:
        image: sklieren/yeeti:latest
        container_name: yeeti
        ports:
            - 8080:8080
        volumes:
            - ./data:/data
        environment:
            - YEETI_BASE_URL=https://yeeti.yourdomain.com
⁠Option B: docker run command

Instead of using a compose file, you can start Yeeti with the following docker run command:

docker run --name yeeti -v ./data:/data -p "8080:8080" -e YEETI_BASE_URL=https://yeeti.yourdomain.com sklieren/yeeti:latest
⁠Access Yeeti

Now, Yeeti should be running on your machine, listening on port 8080 unless you've already done some configuration⁠. Yeeti does not handle TLS termination, so you might want to use a reverse proxy such as nginx.

⁠nginx

Example nginx configuration:

server {
    listen 80;
    listen [::]:80;
    server_name yeeti.yourdomain.com;

    server_tokens off;

    # certbot challenge location
    location /.well-known/acme-challenge/ {
        alias /var/www/certbot/.well-known/acme-challenge/;
        default_type "text/plain";
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name yeeti.yourdomain.com;

    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/yeeti.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yeeti.yourdomain.com/privkey.pem;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Allow 250MB uploads
    client_max_body_size 250M;

    location / {
        proxy_pass        http://yeeti:8080;
        proxy_pass_header yeeti-metadata;

        # Required WebSocket headers
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;

        # Increase timeout for long-lived connections
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

⁠Configuration

Yeeti can be configured using the following environment variables.

NameDescriptionDefault
YEETI_BASE_URLThe (public) URL of your yeeti instance. (for example https://yeeti.yourdomain.com)http://127.0.0.1:8080
YEETI_HOSTThe host the http server is going to listen on. 0.0.0.0 for listening on all devices, 127.0.0.1 for listening on localhost only, etc.0.0.0.0
YEETI_PORTThe port the http server should listen on.8080
YEETI_UPLOAD_ID_ALPHABETUpload IDs will consist of random characters out of this alphabet. I deliberately excluded letters which could easily be mistaken by others, such as uppercase i, lowercase L, etc.abcdefghijkmnopqrstuvwxyzABCDEFGHKLMNOPQRSTUVWXYZ
YEETI_UPLOAD_ID_LENGTHLength of the generated files' ID. While the default of 7 may appear a little small, with the default alphabet and a length of 7 characters, there are about 678 billion unique IDs - this should be enough for temporary files.7
YEETI_SQLITE_FILEPath to the sqlite file yeeti should use. Can be ':memory:' if no peristence is needed / wished. Will be located beneath YEETI_DATA_DIR.yeeti.sqlite
YEETI_DUMP_CONFIG_ON_STARTUPSet this to any of [1, true, "1", "true", "yes", "enabled"] in order to instruct the yeeti server to log it's configuration on service startup.undefined

Tag summary

Content type

Image

Digest

sha256:ed16e2ad7…

Size

65.1 MB

Last updated

3 days ago

docker pull sklieren/yeeti