Tailscale in all eReaders

17 Jan 2026|6 minute read

This was an interesting project that all started with me self-hosting rmfakecloud on my server and running the rmfakecloud-proxy to trick my reMarkable 2 into thinking it was connected to the official servers.

A very cool project that allows me to have control over my data and more easily add and edit files without having to either pay for the service (around 30 €/year) or trust their side of securing the data.

For context, all the services that I run on my server are isolated to my LAN and only externally accessible via the Tailscale mesh network (a kind of VPN+).

So my logical next step, after having everything working locally (the connection of my reMarkable to my server), was to have a way to have my reMarkable connect to my server at a distance, Tailscale being the only viable option.

The Challenge: No tun Interface

reMarkable is a device that has an ARMv7 processor, so running Tailscale on it is not very difficult since I can already download the binaries for it.

Fortunately, reMarkable is more open than other eReaders/tablets, such as Amazon’s, since it does not require a jailbreak of the device; it provides a direct way to SSH into it.

So, what is the catch for this not being the easiest task?

Well… it’s the fact that the Linux kernel inside these kinds of devices does not support the creation of the tun0 interface, which is used for the VPN connections, such as those by Tailscale.

The Solution: A Custom TCP Bridge

The solution is to use Tailscale’s already existent functionality, “Tailscale Userspace Networking” that, instead of connecting directly like a normal network, Tailscale runs as a proxy (SOCKS5 or HTTP) on the device.

The presented difficulty was with how the application connected to this proxy; it is essential to have the traffic arrive to my server.

And the possibility that I thought (and, in the end, implemented) was running a middleman who proxies the requests by the applications to the Tailscale proxy.

Writing the Bridge (Go Code)

So for that I’ve developed this simple bridge that is kind of the “glue” that connects local ports (rmfakecloud, or KOReader) to the Tailscale network.

package main

import (
    "flag"
    "io"
    "log"
    "net"
    "golang.org/x/net/proxy"
)

func main() {
    listenAddr := flag.String("l", ":8443", "Local listening address")
    targetAddr := flag.String("t", "", "Target remote address (IP:PORT)")
    socksAddr := flag.String("x", "127.0.0.1:1055", "SOCKS5 proxy address")
    flag.Parse()

    dialer, err := proxy.SOCKS5("tcp", *socksAddr, nil, proxy.Direct)
    if err != nil { log.Fatalf("Err: %v", err) }

    listener, err := net.Listen("tcp", *listenAddr)    if err != nil { log.Fatalf("Err: %v", err) }

    for {
        conn, err := listener.Accept()
        if err == nil { go handle(conn, dialer, *targetAddr) }
    }
}

func handle(src net.Conn, dialer proxy.Dialer, target string) {
    defer src.Close()
    dst, err := dialer.Dial("tcp", target)
    if err != nil { return }
    defer dst.Close()
    go io.Copy(dst, src)
    io.Copy(src, dst)
}

Compilation and Installation

I’ve simply named it bridge.go, and I’ve compiled it on my laptop (running Linux) with the following command:

go mod init bridge && go get golang.org/x/net/proxy
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w" -o bridge bridge.go

And that I finally simply moved to my reMarkable using SSH (more precisely, SCP):

scp bridge root@reMarkable:/home/root/

Then I simply downloaded the ARM binaries from pkgs.tailscale.com and moved them to the folder /home/root/tailscale.

Configuring the Scripts

Later, to simplify the starting of the Tailscale on the device, I’ve created a simple start_tailscale.sh:

#!/bin/sh
killall tailscaled 2>/dev/null

/home/root/tailscale/tailscaled \
  --tun=userspace-networking \
  --socks5-server=localhost:1055 \
  --outbound-http-proxy-listen=localhost:1056 \
  --state=/home/root/tailscale/tailscaled.state \
  > /dev/null 2>&1 &

sleep 2
/home/root/tailscale/tailscale up --hostname=remarkable2

And another bash script for starting the bridge that I’ve binded to my server’s Tailscale IP.

Also, please remember to first connect yourself to your Tailscale mesh (you can just execute the Tailscale binary as you would normally do /home/root/tailscale/tailscale).

#!/bin/sh
killall bridge 2>/dev/null

# rmfakecloud Bridge (Local 8443 -> Remote 443)
/home/root/bridge -l :8443 -t [TAILSCALE IP]:443 -x 127.0.0.1:1055 > /dev/null 2>&1 &

# WebDAV Bridge (Local 8081 -> Remote 443)
/home/root/bridge -l :8081 -t [TAILSCALE IP]:443 -x 127.0.0.1:1055 > /dev/null 2>&1 &

In this script, you can add whatever service pleases you for them to be routed via Tailscale. I’ve added the rmfakecloud, as well as the webdav (that I’ll later connect to KOReader), both self-hosted in my server.

An important thing to not forget is to make this script executable with the command:

chmod +x /home/root/tailscale/start_tailscale.sh /home/root/start_services.sh

The SSL & DNS Workaround

The only missing piece was to add the sub-domains of the services that I self-host (since I use a reverse proxy) to the /etc/hosts list, to something like:

# for the rmfakecloud domain
127.0.0.1 rmfakecloud.selfhosted.lan

# for the webdav server
127.0.0.1 webdav.selfhosted.lan
I have a valid Let’s Encrypt certificate, and I didn’t have to set up a custom CA directly on the device.

There was an error (“HostNameMismatch”) when trying to connect to my server. My resolution was to directly modify the original rmfakecloud-proxy install script.

It simply required me to specify the domain in the certificate generation.

And then, when launching the installation script, we would simply insert the URL: https://rmfakecloud.selfhosted.lan:8443

As well as with other services, in which we could finally connect the applications to the services running remotely in a server.

Just note that when connecting, let’s say, to the WebDAV server, you must connect to the URL in the format: https://webdav.selfhosted.lan:8081.

We connect to the real domain name (so the SSL certificate matches) but on the local bridge port (8081). The bridge then tunnels that traffic silently to the remote server.

8081 is the port used locally by the bridge.

Bonus: Porting to the Kindle

Later I thought, well, I managed to make Tailscale work in my reMarkable; perhaps I’m going to be able to make my Kindle Paperwhite connect to Tailscale as well.

This way I could have KOReader have access to my library (webdav) the same way reMarkable did.

And to my surprise, using the same installation process, I’ve managed to have my Kindle connect as well.

Only requiring to modify some internal paths that I’ll mention below:

Noting that with the Kindle was easier, since there was no need to set up the rmfakecloud. So minus 1 thing to do…

This setup might work with any other eReader or devices that are in the same situation and that run Linux.

So don’t hesitate to expand the capabilities of your devices.

← The one before
Wi-Fi Hotspot in Linux