scottlamb / moonfire-nvr

Moonfire NVR, a security camera network video recorder
Other
1.3k stars 143 forks source link

Feature request: support Zero Trust networking #308

Open ruzko opened 9 months ago

ruzko commented 9 months ago

Hi!

I'm building a free software project to offer predator alerts and deterrence for farms, using NVR and zero-trust, overlay mesh networking. There aren't any NVR offerings which implement zero-trust principles directly at this time, so in testing I've mostly had to try "bolting on" that capability, using OpenZiti and their tunnelers.

The OpenZiti maintainers suggested on a forum post I made about my project, that embedding OpenZiti using their SDKs would be ideal. OpenZiti has SDKs for embedding zero-trust communication in applications, which is superior to the tunneling approach both in terms of security and usability. They don't have a rust SDK yet, but the C SDK is usable via FFI.

OpenZiti can bootstrap a regular browser session into zero-trust without needing to install anything on the client device, by relying on an OIDC provider. For PWAs like Moonfire, this is nice.

Being written in Rust, Moonfire is a prime candidate for security-minded environments.

I'd like to see integrated support for zero-trust networking in Moonfire, and am prepared to spend ~40 hours on it initially. Is integrated zero-trust via OpenZiti something you'd consider supporting in Moonfire?

*edit: this would touch on https://github.com/scottlamb/moonfire-nvr/issues/27, https://github.com/scottlamb/moonfire-nvr/issues/26, https://github.com/scottlamb/moonfire-nvr/issues/216, https://github.com/scottlamb/moonfire-nvr/issues/154, and https://github.com/scottlamb/moonfire-nvr/issues/133

scottlamb commented 7 months ago

I finally did some reading on OpenZiti.

So the idea is that this would be for the browser->Moonfire leg? And it would be an alternative to binding a TCP port which is reachable by the client (over the LAN, VPN, or public Internet)? And Moonfire could use Ziti APIs to provide authenticated identity and possibly even role-based authorization, as an alternative to the current password->web session or Unix socket permissions-based authentication and/or the per-user permissions in its own database?

I'm looking through a few details of how it might work.

Build/linking: we'd have to link to the OpenZiti APIs. I don't know if it's too compatible with the "build a zero-dependencies, almost-pure-Rust binary" approach I've adopted. How would you feel about this being an optional feature not included in the main release builds?

Threading model: I don't see this documented in their SDK docs. Currently Moonfire uses a multi-threaded tokio reactor. Is Ziti thread-safe? or would we need to handle all Ziti operations in one thread?

General lifecycle: looks like for each [[bind]] ziti = ... item we'd set up a service with ziti_listen. Then in ziti_client_cb, ziti_accept it, then in ziti_conn_cb set up a matching hyper Connection. We receive data via ziti_read_cb; we start writes via ziti_write and get their completion status via ziti_write_cb. Closing happens by ziti_close (locally initiated) / ziti_close_cb (fully closed by either party).

Flow control: I'm a little concerned that I don't see much mention of this.

scottlamb commented 7 months ago

btw, there's something vaguely similar I've been thinking of adding: Cloudflare Tunnel support, perhaps via the libcfd crate if it matures a bit. Comparing the two...

scottlamb commented 7 months ago

A third option in this general space is Tailscale. Again it's a commercial freemium service thing. They have a page here on enabling https that mentions Caddy integration; we could rely on something external (tailscale's software or caddy) or add similar integration to Moonfire.

ruzko commented 7 months ago

Thanks for replying :) I had some trouble getting Moonfire up and running reliably, and to connect to the cameras in my network. I needed to have a working prototype (for my thesis) two weeks after this issue was opened, so I restarted work on a NVR project in Python I'd developed previously. If I manage to get some funding to keep working after my thesis finishes in June; I'm still keen on reimplementing stuff in Rust for Moonfire.

A central theme in the thesis is how transparency and digital self-sovereignity is crucial for building trust and security in a rapidly IoT-connected world. The NVR project I'm working on for the thesis is licensed AGPLv3-or-later, to ensure that users always have access to the source code, even if a service provider decides to sell it as a service; e.g. by linking their cams to a cloud-hosted NVR server.

This might be a little off-topic, but may I ask why you chose GPLv3-or-later over AGPLv3-or-later for Moonfire?

So the idea is that this would be for the browser->Moonfire leg? And it would be an alternative to binding a TCP port which is reachable by the client (over the LAN, VPN, or public Internet)? And Moonfire could use Ziti APIs to provide authenticated identity and possibly even role-based authorization, as an alternative to the current password->web session or Unix socket permissions-based authentication and/or the per-user permissions in its own database?

Yes, to all of that. A future possibility as well is splicing into the radio circuits of an existing camera with a daugtherboard running openziti SDK, so the cam <--> Moonfire connection is zero trust as well; or running openziti SDK on an open-firmware cam.

How would you feel about this being an optional feature not included in the main release builds? Sure, that'd be alright. In order to make use of Openziti the user would have to get access to a ziti network (ziti controller + ziti edge router) either via a network provider like Netfoundry; or by spinning up their own network. Since Moonfire philosophy is minimalistic wrt deps and binary size, having ZT as a build option or separate release for those who need it makes sense.

I've got good experiences with using Cloudflare Tunnels, as well as their Access thing. It works. Buut it's kinda sus that it has a couple neat features, that can easily enable Cloudflare (or attackers in their infra) to extract info; and the backend is proprietary. It works and I use it for some non-critical services, but I don't feel good about it.

Same deal with Tailscale, although they're significantly smaller and don't have as much of a stranglehold on global network infra as CF. It's also way more sysadmin oriented. There's thankfully an open implementation of Tailscale's backend, Headscale, but I couldn't get it to work right.

For the technicalities of using Openziti in Rust, I'm not sure. My experience is via the Python SDK, which uses the C SDK under the hood; but there are probably differences between using it in Python or Rust. Pinging @dovholuknf, who has helped me out a lot regarding Python; could you chime in?