Home defense adventures with Hikvision CCTV cameras
We recently got robbed, so we decided to secure the house with some CCTV cameras. Ours is a modest setup with nine 5MP cameras and a DVR recording all the footage from those cameras at 1080p. The DVR can connect to Hikvision's cloud infrastructure so that owners can see live footage using the internet., which I don't like. I mean, in the past few years, I've moved all my online life from Big Brothers to my servers for privacy reasons, so giving free access to live footage of my house to companies is certainly not acceptable.
So I decided to set up the DVR in an isolated network(without access to the internet), expose its web interface(the DVR has a web interface) via a reverse proxy to the internet. This post is an attempt to record my findings.
I'm using Nginx for reverse proxying. Setting up the reverse proxy wasn't straightforward. The web interface has many quirks and no documentation.
First Attempt: simple reverse proxy
server {
server_name example.com;
location / {
proxy_pass http://dvr-ip-address;
}
listen 443 ssl;
listen 80;
--- snip ---
}
This setup exposed the web interface, I was able to log in, but video
playback wasn't working. It would work when I directly access
http://dvr-ip-address
but not through the reverse proxy.
I poked around with browser dev tools for a bit and learned that video was handled by WebSocket connections. And so:
Second Attempt: there's a WebSocket component!
server {
server_name example.com;
# some static files were taking forever to download
# so I hooked it up with a cache
proxy_cache hikvision;
location / {
proxy_pass http://dvr-ip-address;
proxy_http_version 1.1;
# Ensuring it can use websockets
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect http:// $scheme://;
# These sets the timeout so that the websocket can stay alive
proxy_connect_timeout 7m;
proxy_send_timeout 7m;
proxy_read_timeout 7m;
}
listen 443 ssl;
listen 80;
}
Some files were taking forever to download, so I hooked it up with a cache to speed things up. Video playback wouldn't work even with these modifications. So I had to dig deeper with the browser dev tools.
Server ports are hardcoded in the frontend application. The DVR system listens on four ports:
- WebSocket server for video playback (with and without TLS, separate ports)
- controls, authentication, etc. (with and without TLS, dedicated ports)
Instead of having the back-end handle HTTP to HTTPS redirection, the
ports are hardcoded in the frontend. The TLS WebSocket server
listens on 7682
while the plain text WebSocket server listens on
7681
. And the frontend, depending on the URI scheme, toggles between
the ports. The reverse proxy had TLS configured, so it was trying to
contact wss://example.com:7682
.
Third and Final attempt: work already :/
# server handling configuration, authentication, etc.
server {
server_name example.com;
proxy_cache hikvision;
location / {
proxy_pass http://dvr-ip-address;
}
listen 443 ssl; # managed by Certbot
listen 80;
}
# websocket server
server {
server_name example.com;
listen 7682 ssl;
location / {
# nginx talks to backend in plain text
# and hence 7682-7681 plumbing
proxy_pass http://dvr-ip-address:7681;
proxy_http_version 1.1;
# Ensuring it can use websockets
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect http:// $scheme://;
# These sets the timeout so that the websocket can stay alive
proxy_connect_timeout 7m;
proxy_send_timeout 7m;
proxy_read_timeout 7m;
}
}
I opened port 7682
on the reverse proxy to
and forward it to port 7681
on the back-end for video playback and it
works!
Conclusion
There are a lot of weird things about the Hikvision DVR's web interface.
There was an SSH toggle switch that wouldn't work(I dug up its manual on
the internet, apparently my model doesn't have SSH). The root is not at
/
but they have a redirect to /doc/something
, which is weird.
There's a separate WebSocket server that listens on a separate port when
they could have simply scoped it to a certain path on the main server.
The web interface is practically useless on mobile devices: video
decoding is done client-side with WASM. So mobile devices struggle to
process feeds from two cameras simultaneously. I understand that these
systems are designed to Just Work⢠but software design should be
intuitive and it certainly shouldn't take a whole day to set up a simple
reverse proxy!