diff --git a/guide/build.md b/guide/build.md index af37317..d921232 100644 --- a/guide/build.md +++ b/guide/build.md @@ -309,6 +309,10 @@ You'll also need a `/etc/moonfire-nvr.json`: "allowUnauthenticatedPermissions": { "viewVideo": true } + }, + { + "unix": "/var/lib/moonfire-nvr/sock", + "ownUidIsPrivileged": true } ] } diff --git a/guide/install.md b/guide/install.md index a23633b..f815568 100644 --- a/guide/install.md +++ b/guide/install.md @@ -268,17 +268,20 @@ You'll need to create the runtime configuration file, `/etc/moonfire-nvr.json`: "allowUnauthenticatedPermissions": { "viewVideo": true } + }, + { + "unix": "/var/lib/moonfire-nvr/sock", + "ownUidIsPrivileged": true } ] } ``` -Note that at this stage, Moonfire NVR's web interface is **insecure**: it -doesn't use `https` and doesn't require you to authenticate -to it. You might be comfortable starting it in this configuration to try it -out, particularly if the machine it's running on is behind a home router's -firewall. You might not; in that case read through [secure the -system](secure.md) first. +With this config, Moonfire NVR's web interface is **insecure**: it doesn't use +`https` and doesn't require you to authenticate to it. You might be comfortable +starting it in this configuration to try it out, particularly if the machine +it's running on is behind a home router's firewall. You might not; in that case +read through [secure the system](secure.md) first. This command will start a detached Docker container for the web interface. It will automatically restart when your system does. diff --git a/server/src/cmds/run/mod.rs b/server/src/cmds/run/mod.rs index 733c609..5d23bbd 100644 --- a/server/src/cmds/run/mod.rs +++ b/server/src/cmds/run/mod.rs @@ -184,11 +184,33 @@ async fn async_run(read_only: bool, config: &ConfigFile) -> Result { } } +/// Makes a best-effort attempt to prepare a path for binding as a Unix-domain socket. +/// +/// Binding to a Unix-domain socket fails with `EADDRINUSE` if the dirent already exists, +/// and the dirent isn't automatically deleted when the previous server closes. Clean up a +/// previous socket. As a defense against misconfiguration, make sure it actually is +/// a socket first. +/// +/// This mechanism is inherently racy, but it's expected that the database has already +/// been locked. +fn prepare_unix_socket(p: &Path) { + use nix::sys::stat::{stat, SFlag}; + let stat = match stat(p) { + Err(_) => return, + Ok(s) => s, + }; + if !SFlag::from_bits_truncate(stat.st_mode).intersects(SFlag::S_IFSOCK) { + return; + } + let _ = nix::unistd::unlink(p); +} + fn make_listener(addr: &config::AddressConfig) -> Result { let sa: SocketAddr = match addr { config::AddressConfig::Ipv4(a) => a.clone().into(), config::AddressConfig::Ipv6(a) => a.clone().into(), config::AddressConfig::Unix(p) => { + prepare_unix_socket(p); return Ok(Listener::Unix( tokio::net::UnixListener::bind(p) .with_context(|_| format!("unable bind Unix socket {}", p.display()))?,