bitbound / ControlR

Open-source, self-hostable remote control and remote access.
https://controlr.app
GNU General Public License v3.0
40 stars 2 forks source link
remote-control webrtc

ControlR

A zero-trust remote control solution built with .NET 8, MAUI, and SignalR.

Build Status Discord

Website: https://controlr.app
Docker: https://hub.docker.com/r/translucency/controlr
Discussions: https://github.com/bitbound/ControlR/discussions

Upcoming Changes:

ControlR is being rewritten with a web front-end, which will replace the native Windows and Android apps.

The below readme will change once the transition is complete.

Quick Start:

Public Server:

Self-Hosted:

wget https://raw.githubusercontent.com/bitbound/ControlR/main/docker-compose/docker-compose.yml

# If needed, make changes to docker-compose.yml.  For example, you may want
# to use a bind mount for '/app/AppData' instead of a  Docker volume.
# See the comments in the compose file for additional configuration info.

sudo docker compose up -d

ControlR should now be available on port 5120 (by default). Running curl http://127.0.0.1:5120/api/health should return "Healthy".

Navigating to the root URI in the browser will redirect to https://controlr.app. This is because ControlR doesn't have a web UI and is only meant to be used through the native app.

Side-loaded vs Store Installations

The viewer app can be installed from one of the stores (Microsoft/Google Play), or it can be side-loaded by downloading the installer file. The app will change auto-update behavior based on whether it was side-loaded or acquired from the store.

It's recommended that you only use one or the other to avoid conflicts. If you're self-hosting, you should only be using the side-loaded method so the version will stay in sync with your specific server instance.

If you're using the public server at app.controlr.app, you can use either installation method.

You can see which method the app is currently using on the About page, next to Install Source.

Relay Servers

On the public server (https://app.controlr.app), when you start a remote control session, the main server will attempt to transfer the session to the relay server that's closest to you. These relay servers use another of my projects (WebSocketBridge) to stream the remote control data.

Currently, there are servers deployed in the following locations:

I may add or remove servers in the future, depending on sponsorships/donations.

Screenshots

Windows Sessions on Desktop

Windows Sessions on Android

How the Zero-Trust Works

Zero-trust is implemented via RSA public/private keypairs. When the agent is installed, you supply the public keys that are allowed to access the device.

This public key comes from the keypair that you create when you first open the viewer app. When the viewer connects to the server, it authenticates by including a signed message in the initial request, allowing the server to verify your public key. This is implemented through a custom AuthenticationHandler.

Your private key is never shared with the server; only your public key is.

This process allows the viewer to establish a connection with the server. However, it doesn't authenticate the viewer with any connected agents.

When the agent comes online, it broadcasts its presence via SignalR to public keys in its config file.

When viewers try to connect or issue commands, every message is signed with the viewer's private key. The agent verifies every signature and checks that the signer's public key exists in their AuthorizedKeys config section. If the key doesn't exist, a critical-level log entry is made, and the command is ignored. The messages' timestamps are also signed, so they can't be captured and reissued in the future.

This means that the agent doesn't implicitly trust anything coming from the server. It's able to independently verify all commands issued to it.

No user or device data is persisted on the server. There is no database. All state and identity information is maintained on local devices. When you uninstall the viewer and agent, it's like they were never connected.

Agent Auto-Updates

Installed agents will automatically update themselves when new versions are released. To increase security, the agent will verify that the SHA256 hash of the new version's binary/archive exists in a separate data store (currently hosted by Cloudflare R2). If not, the update is aborted, and a critical-level log entry is written.

The hashes are uploaded to Cloudflare with each build, from a different location and server than where ControlR is hosted. Write access to R2 is limited to a single IP address.

This separation ensures that, even if the ControlR server is compromised, they wouldn't be able to get agents to auto-update with malicious binaries.