Options For Sharing Files Accross Devices

Sharing Files Securely: Combining Python HTTPserver, Tailscale Funnel, and Magic Wormhole

For anyone in need of quickly sharing files across devices, especially within networks of varied operating systems, tools like Python HTTPserver, Tailscale Funnel, and Magic Wormhole can be invaluable. After some testing, I’ve found a combination of these tools to be particularly effective. Here’s a breakdown, including functions which i’ve now added to my dotfiles repo.

How to Use These Tools Together

  1. Python HTTPserver:

    • Use for serving files locally within a network.
    • Combine with Tailscale Funnel for secure, internet-wide access.
  2. Tailscale Funnel:

    • Share services or files securely using a public URL.
    • Use with Python HTTPserver for platform workarounds, especially on MacOS.
  3. Magic Wormhole:

    • Direct file transfers between two devices.
    • Perfect for quick, secure sharing without setting up a server.

Choose the Right Tool for Your Needs

Each of these tools offers unique advantages:

  • Python HTTPserver: Fast and simple for local networks.
  • Tailscale Funnel: Secure and scalable for internet access.
  • Magic Wormhole: Peer-to-peer simplicity for direct transfers.

Python HTTPserver: Local File Sharing Made Simple

The Python HTTPserver has long been a reliable tool for sharing files within a local network. By running a simple command:

python3 -m http.server 8080

You can serve files in the current directory over HTTP. However, there are limitations:

  • Files are not encrypted and can be accessed by anyone on the local network.
  • The service is confined to local traffic, with no straightforward way to share beyond the local network.

To enhance usability, I created a function to streamline the process, allowing you to specify files or directories for sharing. This function simplifies temporary setups and provides flexibility to manage shared content effectively.

pyserver(){
    # usage: pyserver file1 file2 ...
    if [[ "$1" == "-h" || "$1" == "--help" ]]; then
        echo "Usage: pyserver file1 file2 ..."
        return 0
    fi
    # check if python3 is installed
    if ! command -v python3 &> /dev/null; then
        echo "Error: Python 3 is not installed"
        echo "You can install it with:"
        echo 'brew install python'
        return 1
    fi
    # set port for the python server
    local port=8000
    # create a python server for the passed files or dir
    #get local ip
    local_ip=$(hostname -I | awk '{print $1}')
    # path for the server else use current dir
    # if multiple files passed in arg then create tmp dir and add those passed files or dir via ln to the temp server dir
    # if no files passed in arg then use current dir
    if [ -n "$1" ]; then # if there are files passed in arg
        # create temp dir
        mkdir -p /tmp/pyserver
        # add files or dir to the temp dir
        for file in "$@"; do
            ln -s "$file" /tmp/pyserver
        done
        # change dir to the temp dir
        cd /tmp/pyserver
        # start the python server
        python3 -m http.server $port
    else
        # use current dir
        cd .
        # start the python server
        python3 -m http.server $port
    fi
    
}

Tailscale Funnel: Secure and Broader Access

Tailscale Funnel extends the functionality of local servers by creating a public URL for your service. It’s an excellent solution for securely sharing files or directories with anyone on the internet. However, there’s a caveat:

On MacOS, using the tailscale funnel command for direct file sharing is restricted due to sandbox limitations. Instead, you can combine Tailscale Funnel with Python HTTPserver. This approach enables you to serve files locally and securely expose them via Tailscale.

Here’s the combined one-liner:

python3 -m http.server 8080 | tailscale funnel localhost:8080

For better management, I’ve written a function to automate this workflow. This method ensures secure file sharing with encrypted tunnels, leveraging Tailscale’s infrastructure.

funnel() {
    # usage: funnel file1 file2 dir1 dir2 ...
    # On MacOS you must be using the open source tailscaled distribution to use the funnel command for files. 
    if [[ "$1" == "-h" || "$1" == "--help" ]]; then
        echo "Usage: funnel <target>"
        echo "Funnel enables you to share a local server on the internet using Tailscale."
        return 0
    fi
    local port=8080
    use_python=true

    # Check if tailscale is running
    if ! tailscale status &> /dev/null; then
        echo "Error: Tailscale is not running"
        echo "You can start it with: tailscale start"
        return 1
    fi

    # Use current directory if no target is specified
    if [ -z "$1" ]; then
        echo "No target specified. Using current directory as target."
        target="."
    else
        target="$@"
    fi

    # if multiple files passed in arg then create tmp dir and add those passed files or dir via ln to the temp server dir
    # if no files passed in arg then use current dir
    if [ -n "$1" ]; then # if there are files passed in arg
        # create temp dir
        mkdir -p /tmp/funnel
        # add files or dir to the temp dir
        for file in "$@"; do
            ln -s "$file" /tmp/funnel
        done
        # change dir to the temp dir
        cd /tmp/funnel
        if $use_python; then
            # start the python server
            python3 -m http.server $port&
            # start the funnel
            tailscale funnel localhost:$port
        else
            # start the funnel
            tailscale funnel "$target"
        fi
        
    else
        # use current dir
        cd .
        if $use_python; then
            # start the python server
            python3 -m http.server $port&
            # start the funnel
            tailscale funnel localhost:$port
        else
            # start the funnel
            tailscale funnel "$target"
        fi
    fi
}

Magic Wormhole: Direct, Secure Transfers

Magic Wormhole is a command-line tool that enables secure and straightforward file transfers between devices. Unlike HTTP-based solutions, it creates a direct, encrypted connection, making it ideal for transferring sensitive files without relying on an intermediary server.

Wormhole Transfer Function

This function allows for flexible usage:

  • Send multiple files or directories as a zipped archive.
  • Encrypt files before sending, using age, gpg, or openssl for maximum security.
wh-transfer(){
    # Usage and options
    if [[ "$1" == "-h" || "$1" == "--help" ]]; then
        echo "Usage: wh-transfer [-e|--encrypt] path1 path2 ..."
        echo "The default is to send the files as is"
        echo "-e or --encrypt will encrypt the files before sending using age, gpg, or aes"
        return 0
    fi

    # Encryption and sending
    encrypt_and_send() {
        local encrypt_tool="$1"
        shift
        local files=("$@")
        local zip_file="./wormhole_$(date +%Y%m%d%H%M%S).zip"
        zip "$zip_file" "${files[@]}"
        
        local encrypted_file="${zip_file}.age"
        if [[ "$encrypt_tool" == "gpg" ]]; then
            encrypted_file="${zip_file}.gpg"
            gpg --output "$encrypted_file" --symmetric "$zip_file"
        elif [[ "$encrypt_tool" == "aes" ]]; then
            encrypted_file="${zip_file}.aes"
            openssl enc -aes-256-cbc -salt -in "$zip_file" -out "$encrypted_file"
        else
            age -o "$encrypted_file" -p "$zip_file"
        fi

        wormhole send "$encrypted_file"
        rm -rf "$zip_file" "$encrypted_file"
    }

    if [[ "$1" == "-e" || "$1" == "--encrypt" ]]; then
        shift
        if command -v age &> /dev/null; then
            encrypt_and_send "age" "$@"
        elif command -v gpg &> /dev/null; then
            encrypt_and_send "gpg" "$@"
        else
            encrypt_and_send "aes" "$@"
        fi
    else
        if [[ "$#" -gt 1 ]]; then
            local zip_file="./wormhole_$(date +%Y%m%d%H%M%S).zip"
            zip "$zip_file" "$@"
            wormhole send "$zip_file"
            rm -rf "$zip_file"
        else
            wormhole send "$@"
        fi
    fi
}

Feel free to share your experiences and any other tools you’ve found useful. I hope to keep building and refining our workflows together!

Still looking for good magic wormhole options for mobile. Have issue sending and receiving from mobile to desktop or vice versa