mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-25 14:45:54 -05:00
document proxy setup in guide/secure.md (for #26)
The guide is not as quick to follow and amateur-friendly as I'd like. A few things that might improve matters: * complete #27 (built-in https+letsencrypt), so that when not sharing the port, users don't need to use nginx or certbot. * more ubiquitous IPv6 (out of my control but should happen over time) to reduce need to share the port * embed a dynamic DNS client * support UPnP Internet Gateway Device Control Protocol (if common routers have this enabled? probably not for security reasons.) It's progress, though. Enough that I think I'll merge the auth branch into master shortly.
This commit is contained in:
parent
3c1163dfe2
commit
24674f5b50
@ -13,7 +13,8 @@ less than 10% of the machine's total CPU.
|
||||
So far, the web interface is basic: a filterable list of video segments,
|
||||
with support for trimming them to arbitrary time ranges. No scrub bar yet.
|
||||
There's also no support for motion detection, no https/SSL/TLS support (you'll
|
||||
need a proxy server), and no config UI.
|
||||
need a proxy server, as described [here](guide/secure.md)), and only a
|
||||
console-based (rather than web-based) configuration UI.
|
||||
|
||||
![screenshot](screenshot.png)
|
||||
|
||||
|
@ -84,7 +84,8 @@ Moonfire NVR can be run as a systemd service. Create
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/moonfire-nvr run \
|
||||
--db-dir=/var/lib/moonfire-nvr/db \
|
||||
--http-addr=0.0.0.0:8080
|
||||
--http-addr=0.0.0.0:8080 \
|
||||
--require-auth=false
|
||||
Environment=TZ=:/etc/localtime
|
||||
Environment=MOONFIRE_FORMAT=google-systemd
|
||||
Environment=MOONFIRE_LOG=info
|
||||
|
@ -56,7 +56,7 @@ In the fstab you'd add a line similar to this:
|
||||
/dev/disk/by-uuid/23d550bc-0e38-4825-acac-1cac8a7e091f /media/nvr ext4 defaults,noatime,nofail 0 2
|
||||
|
||||
You'll have to lookup the correct uuid for your disk. One way to do that is
|
||||
to issue the following commands:
|
||||
via the following command:
|
||||
|
||||
$ ls -l /dev/disk/by-uuid
|
||||
|
||||
@ -104,6 +104,7 @@ In the user interface,
|
||||
be flushed when the first instant of a completed recording second is a
|
||||
minute old. Lower values cause less video to be lost on power loss;
|
||||
higher values reduce wear on the SSD holding the SQLite database.
|
||||
|
||||
3. Assign disk space to your cameras back in "Directories and retention".
|
||||
Leave a little slack (at least 100 MB per camera) between the total limit
|
||||
and the filesystem capacity, even if you store nothing else on the disk.
|
||||
@ -119,17 +120,30 @@ In the user interface,
|
||||
downloading it), it stays around until the file is closed. Moonfire NVR
|
||||
currently doesn't account for this.
|
||||
|
||||
4. Add a user for yourself (and optionally others) under "Users". You'll need
|
||||
this to access the web UI once you enable authentication.
|
||||
|
||||
## Starting it up
|
||||
|
||||
When finished, start the daemon and enable it for following boots:
|
||||
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.
|
||||
|
||||
The following commands will start Moonfire NVR and enable it for following
|
||||
boots, respectively:
|
||||
|
||||
$ sudo systemctl start moonfire-nvr
|
||||
$ sudo systemctl enable moonfire-nvr
|
||||
|
||||
You can access the HTTP interface on http://localhost:8080/ by default.
|
||||
|
||||
Note that the HTTP port currently has no authentication, encryption, or
|
||||
logging; it should not be directly exposed to the Internet.
|
||||
The HTTP interface is accessible on port 8080; if your web browser is running
|
||||
on the same machine, you can access it at
|
||||
[http://localhost:8080/](http://localhost:8080/).
|
||||
|
||||
If the system isn't working, see the [Troubleshooting
|
||||
guide](troubleshooting.md).
|
||||
|
||||
Once the web interface seems to be working, read through [securing Moonfire
|
||||
NVR](secure.md).
|
||||
|
256
guide/secure.md
Normal file
256
guide/secure.md
Normal file
@ -0,0 +1,256 @@
|
||||
# Securing Moonfire NVR and exposing it to the Internet
|
||||
|
||||
## The problem
|
||||
|
||||
After you've completed the [Downloading, installing, and configuring
|
||||
NVR guide](install.md), you should have a running system you can use from
|
||||
within your home, but one that is insecure in a couple ways:
|
||||
|
||||
1. It doesn't use `https` to encrypt connections & authenticate itself to
|
||||
you.
|
||||
2. It doesn't require you to sign in (with your chosen username and
|
||||
password) to authenticate yourself to it.
|
||||
|
||||
You'll want to change these points if you expose Moonfire NVR's web interface
|
||||
to the Internet. Security-minded folks would say you shouldn't even allow
|
||||
unauthenticated sessions within your local network.
|
||||
|
||||
Besides security, the nature of home Internet setups presents challenges in
|
||||
exposing Moonfire NVR to the Internet:
|
||||
|
||||
1. you likely have a single IPv4 address that all your devices share via NAT.
|
||||
(Your ISP may also provide a set of IPv6 addresses; even if they do, you
|
||||
likely don't have IPv6 available everywhere you want to connect from.)
|
||||
You'll need to set up "port forwarding" on your home router, and there
|
||||
are many routers with different interfaces for doing so.
|
||||
2. that IPv4 address is likely dynamic, so you'll need to configure "dynamic
|
||||
DNS" to get a consistent URL to access Moonfire NVR. Most people do this
|
||||
through their router's interface as well.
|
||||
3. you may want to share your single IP address's `http` and `https` ports
|
||||
with other web interfaces, such as a network-attached storage device.
|
||||
This requires setting up a proxy and configuring it with each
|
||||
destination.
|
||||
4. unlike some commercial providers, Moonfire NVR doesn't have any central
|
||||
organization to provide a central high-bandwidth, Internet-accessible
|
||||
proxying service.
|
||||
|
||||
This guide is therefore more abstract than the previous installation steps,
|
||||
and may even make assumptions that aren't true for your setup. Improvements
|
||||
are welcome, but it's not possible to make a single terse, concrete guide that
|
||||
will work for everyone. If you're not a networking expert, you may need to
|
||||
consult your home router's manual and other external guides or forums.
|
||||
|
||||
## VPN or port forwarding?
|
||||
|
||||
This guide describes how to set up Moonfire NVR with port forwarding.
|
||||
|
||||
Any security camera forums such as [ipcamtalk](https://ipcamtalk.com/) will
|
||||
recommend that you use a VPN to connect to your NVR rather than port
|
||||
forwarding. The backstory is that most NVRs are untrustworthy. They have
|
||||
low-budget, closed-source software written by companies which at best aren't
|
||||
security-conscious and at worst allow the Chinese government to use [deliberate
|
||||
backdoors](https://www.reddit.com/r/bestof/comments/8aqyto/user_explains_how_one_chinese_security_camera/).
|
||||
|
||||
A VPN's advantage is that it doesn't allow any incoming traffic to reach the
|
||||
NVR until after authentication, so it's far more secure when the NVR can't be
|
||||
trusted to perform proper authentication itself.
|
||||
|
||||
Port forwarding's advantage is that, once installed on the server, it's far
|
||||
more convenient to use. There's no VPN client necessary, just a web browser.
|
||||
|
||||
I believe Moonfire NVR authenticates properly. It's also open-source, so it's
|
||||
practical to verify this yourself given sufficient time and expertise.
|
||||
|
||||
If you'd prefer to use a VPN, the [ipcamtalk Cliff
|
||||
Notes](https://ipcamtalk.com/wiki/ip-cam-talk-cliff-notes/) suggest reading
|
||||
[Network Security
|
||||
Primer](https://ipcamtalk.com/threads/network-security-primer.1123/) and/or
|
||||
[VPN Primer for
|
||||
Noobs](https://ipcamtalk.com/threads/vpn-primer-for-noobs.14601/).
|
||||
|
||||
## Overview
|
||||
|
||||
1. Install a webserver
|
||||
2. Configure a static internal IP
|
||||
3. Set up port forwarding
|
||||
4. Configure a public DNS name
|
||||
5. Install a TLS certificate
|
||||
6. Reconfigure Moonfire NVR
|
||||
7. Configure the webserver
|
||||
8. Verify it works
|
||||
|
||||
## 1. Install a webserver.
|
||||
|
||||
Moonfire NVR's builtin webserver doesn't yet support `https` (see [issue
|
||||
\#27](https://github.com/scottlamb/moonfire-nvr/issues/27), so you'll need to
|
||||
proxy through a webserver that does. If Moonfire NVR will be sharing an
|
||||
`https` port with anything else, you'll need to set up the webserver to proxy
|
||||
to all of these interfaces as well.
|
||||
|
||||
I use [nginx](https://https://nginx.com/) as the proxy server. Some folks may
|
||||
prefer [Apache httpd](https://httpd.apache.org/) or some other webserver. Any
|
||||
of these will work. I include snippets of a `nginx` config below, so stick
|
||||
with that if you're not comfortable adapting it to some other server.
|
||||
|
||||
I run the proxying webserver on the same machine as Moonfire NVR itself. You
|
||||
might want to do something else, but this is the simplest setup that means you
|
||||
only need to configure one machine with a static internal IP address.
|
||||
|
||||
digitalocean has a nice [How to install Nginx on Ubuntu 18.04](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04) guide.
|
||||
|
||||
## 2. Configure a static internal IP
|
||||
|
||||
When you configure port forwarding on your router, you'll most likely have to
|
||||
specify the destination as an internal IP address. You could look up the
|
||||
current IP address of the webserver machine, but it might change, and your
|
||||
setup will break if it does.
|
||||
|
||||
The easiest way to ensure your setup keeps working is to use the "static DHCP
|
||||
lease" option on your home router to give your webserver machine the same
|
||||
address every time it asks for a new lease.
|
||||
|
||||
Alternatively, you can configure your webserver to use a static IP address
|
||||
instead of asking for a DHCP lease. Ensure the address you choose is outside
|
||||
the range assigned by the DHCP server, so that there are no conflicts.
|
||||
|
||||
Reboot the webserver machine now and ensure it uses the IP address you choose on
|
||||
startup, so you don't have a confusing experience after your next power
|
||||
failure.
|
||||
|
||||
## 3. Set up port forwarding
|
||||
|
||||
In your router's setup, go to the "Port Forwarding" section and tell it to
|
||||
forward TCP requests on the `http` port (80) and the `https` port (443) to
|
||||
your webserver. The `https` port is necessary for secure access, and the
|
||||
`http` port is necessary for the Let's Encrypt `http` challenge during the
|
||||
setup process.
|
||||
|
||||
Now if you go to your external IP address in a web browser, you should reach
|
||||
your webserver.
|
||||
|
||||
## 4. Configure a public DNS name
|
||||
|
||||
Also in your router's setup, look for "Dynamic DNS" or "DDNS". Configure it to
|
||||
update some DNS name with your home's external IP address. You should then be
|
||||
able to go to this address in a web browser and reach your webserver again.
|
||||
|
||||
It's possible to instead set up a dynamic DNS client on the Moonfire NVR
|
||||
machine instead. See [this Ubuntu
|
||||
guide](https://help.ubuntu.com/community/DynamicDNS). One disadvantage is that
|
||||
it may be slower to recognize IP address changes, so there may be a longer
|
||||
period in which the address is incorrect.
|
||||
|
||||
## 5. Install a TLS certificate
|
||||
|
||||
I recommend using the [Let's Encrypt](https://letsencrypt.org/) Certificate
|
||||
Authority to obtain a TLS certificate that will be automatically trusted by
|
||||
your browser. See [How to secure Nginx with Let's Encrypt on Ubuntu
|
||||
18.04](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04).
|
||||
|
||||
## 6. Reconfigure Moonfire NVR
|
||||
|
||||
In your `/etc/systemd/system/moonfire-nvr.service` file, look for these lines:
|
||||
|
||||
```
|
||||
ExecStart=/usr/local/bin/moonfire-nvr run \
|
||||
...
|
||||
--http-addr=0.0.0.0:8080 \
|
||||
--require-auth=false
|
||||
```
|
||||
|
||||
Change `--require-auth=false` to `--require-auth=true --trust-forward-hdrs`
|
||||
which has two effects:
|
||||
|
||||
* `--require-auth=true` means that web users must authenticate.
|
||||
* `--trust-forward-hdrs` means that Moonfire NVR will look for `X-Real-IP`
|
||||
and `X-Forwarded-Proto` headers as added by the webserver configuration
|
||||
in the next section.
|
||||
|
||||
If the webserver is running on the same machine as Moonfire NVR, you might
|
||||
also change `0.0.0.0:8080` to `127.0.0.1:8080`, which prevents other machines
|
||||
on the network from impersonating the proxy, effectively allowing them to lie
|
||||
about the client's IP and protocol.
|
||||
|
||||
Run these commands to make the configuration take effect:
|
||||
|
||||
```
|
||||
$ sudo systemctl daemon-reload
|
||||
$ sudo systemctl restart moonfire-nvr
|
||||
```
|
||||
|
||||
## 7. Configure the webserver
|
||||
|
||||
Since step 5, you should have a `https`-capable webserver set up on your
|
||||
desired DNS name. Now finalize its configuration:
|
||||
|
||||
* redirect all `http` traffic to `https`
|
||||
* proxy `https` traffic to Moonfire NVR
|
||||
* add a `X-Real-IP` header with the original IP address
|
||||
* add a `X-Forwarded-Proto` header with the original protocol (which should
|
||||
be `https` if you've configured everything correctly).
|
||||
|
||||
The author's system does this via the following
|
||||
`/etc/nginx/sites-available/nvr.home.slamb.org` file:
|
||||
|
||||
```
|
||||
upstream moonfire {
|
||||
server 127.0.0.1:8080;
|
||||
}
|
||||
|
||||
server {
|
||||
root /var/www/html;
|
||||
index index.html index.htm index.nginx-debian.html;
|
||||
|
||||
server_name nvr.home.slamb.org;
|
||||
|
||||
location / {
|
||||
proxy_pass http://moonfire;
|
||||
# try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect http:// $scheme://;
|
||||
|
||||
listen [::]:443 ssl ipv6only=on; # managed by Certbot
|
||||
listen 443 ssl; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/nvr.home.slamb.org/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/nvr.home.slamb.org/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
return 301 https://nvr.home.slamb.org$request_uri;
|
||||
|
||||
server_name nvr.home.slamb.org nvr;
|
||||
}
|
||||
```
|
||||
|
||||
Check your configuration for syntax errors and reload it:
|
||||
|
||||
```
|
||||
$ sudo nginx -t
|
||||
$ sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## Verify it works
|
||||
|
||||
Go to `http://your.domain.here/api/request` and verify the following:
|
||||
|
||||
* the browser redirects from `http` to `https`
|
||||
* the address shown here matches your web browser's public IP address.
|
||||
(Compare to [https://whatsmyip.com/].)
|
||||
* the page says `secure: true` indicating you are using `https`.
|
||||
|
||||
Then go to `https://your.domain.here/` and you should see the web interface,
|
||||
including a login form. If you login, you should see your username and
|
||||
"logout" in the upper-right corner of the web interface.
|
||||
|
||||
If it doesn't work as expected, re-read the guide, or open an issue on github
|
||||
for help.
|
@ -106,7 +106,8 @@ After=network-online.target
|
||||
ExecStart=${SERVICE_BIN} run \\
|
||||
--db-dir=${DB_DIR} \\
|
||||
--ui-dir=${LIB_DIR}/ui \\
|
||||
--http-addr=0.0.0.0:${NVR_PORT}
|
||||
--http-addr=0.0.0.0:${NVR_PORT} \
|
||||
--require=auth=false
|
||||
Environment=TZ=:/etc/localtime
|
||||
Environment=MOONFIRE_FORMAT=google-systemd
|
||||
Environment=MOONFIRE_LOG=info
|
||||
|
@ -194,5 +194,5 @@ pub fn top_dialog(db: &Arc<db::Database>, siv: &mut Cursive) {
|
||||
.map(|(&id, user)| (format!("{}: {}", id, user.username), Some(id))))
|
||||
.full_width())
|
||||
.dismiss_button("Done")
|
||||
.title("Edit cameras"));
|
||||
.title("Edit users"));
|
||||
}
|
||||
|
@ -486,8 +486,11 @@ impl ServiceInner {
|
||||
|
||||
fn request(&self, req: &Request<::hyper::Body>) -> ResponseResult {
|
||||
let authreq = self.authreq(req);
|
||||
let host = req.headers().get(header::HOST).map(|h| String::from_utf8_lossy(h.as_bytes()));
|
||||
let agent = authreq.user_agent.as_ref().map(|u| String::from_utf8_lossy(&u[..]));
|
||||
Ok(plain_response(StatusCode::OK, format!(
|
||||
"when: {}\n\
|
||||
host: {:?}\n\
|
||||
addr: {:?}\n\
|
||||
user_agent: {:?}\n\
|
||||
secure: {:?}",
|
||||
@ -495,8 +498,9 @@ impl ServiceInner {
|
||||
.strftime("%FT%T")
|
||||
.map(|f| f.to_string())
|
||||
.unwrap_or_else(|e| e.to_string()),
|
||||
host.as_ref().map(|h| &*h),
|
||||
&authreq.addr,
|
||||
authreq.user_agent.map(|u| String::from_utf8_lossy(&u[..]).into_owned()),
|
||||
agent.as_ref().map(|a| &*a),
|
||||
self.is_secure(req))))
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user