Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ Almost all of that is mbedTLS built with its default configuration; the agent it
| `cerberus_ping_interval` | `CERBERUS_PING_INTERVAL` | `60` | seconds between probes; `0` disables |
| `cerberus_ping_timeout` | `CERBERUS_PING_TIMEOUT` | `3` | seconds one probe waits |
| `cerberus_ping_fails` | `CERBERUS_PING_FAILS` | `3` | failed probes in a row before the interface is rebuilt |
| `cerberus_events` | `CERBERUS_EVENTS` | `1` | motion events; `0` installs no hook and sends nothing |
| `cerberus_snapshot_url` | `CERBERUS_SNAPSHOT_URL` | `http://127.0.0.1/image.jpg?width=640&height=360` | majestic's frame; `http://user:pass@127.0.0.1/…` when it wants a password |

`cerberus_ca` matters more than it looks: a firmware may carry no CA bundle at all, and then
enrolment fails on the coordinator's certificate with no way to fix it short of a reflash — point it
Expand All @@ -131,7 +133,8 @@ Variables only the agent reads: `CERBERUS_STORE` (`auto` \| `uboot` \| `file:/pa
`CERBERUS_STALE_AFTER` (`180`, seconds without a handshake before the peer is re-poked),
`CERBERUS_RETRY_MIN` / `CERBERUS_RETRY_MAX` (`5` / `300`, enrolment backoff), `CERBERUS_INSECURE=1`
(skip TLS verification, debugging only), `CERBERUS_DRY_RUN=1` (print the `ip`/`wg` commands instead
of running them).
of running them), `CERBERUS_MOTION_HOOK` (`/usr/sbin/motion.sh`), `CERBERUS_MOTION_QUIET` (`10`,
seconds without motion that end an episode), `CERBERUS_PIDFILE` (`/var/run/cerberus.pid`).

```
cerberus run # enroll + up + watch (the default; what S99cerberus starts)
Expand All @@ -140,6 +143,8 @@ cerberus up # bring the interface up from the stored material
cerberus status # what is stored, handshake age, whether the tunnel answers
cerberus keygen # print the keypair (stdout is data, diagnostics go to stderr)
cerberus clean # remove the agent: interface, init script, stored material, the binary
cerberus motion # tell the running agent there is motion, as majestic's hook does
cerberus snapshot # fetch one frame from CERBERUS_SNAPSHOT_URL into /tmp/cerberus-snapshot.jpg
```

### Formerly `streq_*`
Expand Down Expand Up @@ -214,6 +219,27 @@ Video does not pass through the agent: the relay pulls `rtsp://{overlayIp}:554/s
the camera through the tunnel. There is no peer-to-peer on this agent — the camera stays on the hub
path, which is the path that always works.

### Motion events

majestic notices motion itself once its motion detection is switched on (web UI → Majestic →
Motion detection), and runs `/usr/sbin/motion.sh`. cerberus installs that script — keeping one that
was already there as `motion.sh.orig` and still calling it — and the script does nothing but wake
the agent. The agent turns the calls into episodes (motion, then `CERBERUS_MOTION_QUIET` seconds of
quiet) and reports each one twice, `started` and `ended`, to `POST /nodes/{id}/events`.

When the owner has switched object detection on for the camera, the answer to `started` asks for
frames: one a second from majestic's own `/image.jpg`, until the cloud says it has seen enough. The
cloud looks for people, cars and animals in them and tells the owner — the camera only says "look
now", from the moment it happens, without the cloud pulling the stream or knowing the RTSP password.

Events are signed. The heartbeat's answer carries the coordinator's `eventKey`; the key is
`HKDF-SHA256(X25519(device key, eventKey), salt = nodeId, info = "streq-events-v1")` and each request
carries `X-Streq-Ts` and `X-Streq-Sig = base64(HMAC-SHA256(key, ts \n nodeId \n scope \n body))`. No new
secret is stored on the camera: it lives exactly as long as the device key.

To check it on a camera: `cerberus snapshot` (does majestic hand out a frame as configured?) and
`cerberus motion` (an episode, as if somebody walked by).

## Building

```sh
Expand Down
30 changes: 29 additions & 1 deletion README.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ fw_setenv cerberus_enrollment_token tok_...
| `cerberus_ping_interval` | `CERBERUS_PING_INTERVAL` | `60` | секунд между пробами; `0` — выключить |
| `cerberus_ping_timeout` | `CERBERUS_PING_TIMEOUT` | `3` | сколько секунд ждать ответа на пробу |
| `cerberus_ping_fails` | `CERBERUS_PING_FAILS` | `3` | неотвеченных проб подряд до пересборки интерфейса |
| `cerberus_events` | `CERBERUS_EVENTS` | `1` | события движения; `0` — без хука и без событий |
| `cerberus_snapshot_url` | `CERBERUS_SNAPSHOT_URL` | `http://127.0.0.1/image.jpg?width=640&height=360` | кадр majestic; `http://user:pass@127.0.0.1/…`, если он просит пароль |

`cerberus_ca` важнее, чем кажется: в прошивке может не быть бандла CA вовсе, и тогда энролл падает на
сертификате координатора, а поправить это без перепрошивки нечем — укажите путь к корневому
Expand All @@ -127,7 +129,9 @@ fw_setenv cerberus_enrollment_token tok_...
Переменные, которые читает только агент: `CERBERUS_STORE` (`auto` \| `uboot` \| `file:/path`),
`CERBERUS_STALE_AFTER` (`180`, секунд без хендшейка до перетыкания пира), `CERBERUS_RETRY_MIN` /
`CERBERUS_RETRY_MAX` (`5` / `300`, отступ повторов энролла), `CERBERUS_INSECURE=1` (без проверки TLS,
только для отладки), `CERBERUS_DRY_RUN=1` (печатать команды `ip`/`wg`, а не выполнять).
только для отладки), `CERBERUS_DRY_RUN=1` (печатать команды `ip`/`wg`, а не выполнять),
`CERBERUS_MOTION_HOOK` (`/usr/sbin/motion.sh`), `CERBERUS_MOTION_QUIET` (`10`, секунд тишины, после
которых эпизод кончился), `CERBERUS_PIDFILE` (`/var/run/cerberus.pid`).

```
cerberus run # enroll + up + watch (по умолчанию; это запускает S99cerberus)
Expand All @@ -136,6 +140,8 @@ cerberus up # поднять интерфейс из сохранённо
cerberus status # что сохранено, возраст хендшейка, отвечает ли туннель
cerberus keygen # напечатать пару ключей (stdout — данные, диагностика в stderr)
cerberus clean # убрать агента: интерфейс, автозапуск, сохранённое и сам бинарь
cerberus motion # сказать работающему агенту «движение», как это делает хук majestic
cerberus snapshot # взять один кадр с CERBERUS_SNAPSHOT_URL в /tmp/cerberus-snapshot.jpg
```

### Раньше — `streq_*`
Expand Down Expand Up @@ -206,6 +212,28 @@ cerberus clean # убрать агента: интерфейс, автоза
От недоступного координатора `cerberus run` тоже не умирает: init на камере запускает его один раз и
больше не смотрит, поэтому энролл повторяется с удваивающейся паузой сколько понадобится.

### События движения

Движение majestic замечает сам, когда в нём включена детекция движения (веб-интерфейс → Majestic →
Детекция движения), и запускает `/usr/sbin/motion.sh`. Этот скрипт ставит cerberus — если там уже
лежал чужой, он сохраняется как `motion.sh.orig` и по-прежнему вызывается, — и скрипт ничего не
делает, кроме как будит агента. Агент собирает вызовы в эпизоды (движение, затем
`CERBERUS_MOTION_QUIET` секунд тишины) и сообщает о каждом дважды, `started` и `ended`, в
`POST /nodes/{id}/events`.

Если владелец включил для камеры детекцию объектов, ответ на `started` просит кадры: по одному в
секунду с собственного `/image.jpg` majestic, пока облако не скажет, что увидело достаточно. Облако
ищет в них людей, машины и животных и сообщает владельцу. Камера говорит только «смотри сейчас», с
самого момента движения: облаку не нужно ни тянуть поток, ни знать пароль RTSP.

События подписаны. В ответе на heartbeat едет `eventKey` координатора; ключ —
`HKDF-SHA256(X25519(ключ устройства, eventKey), salt = nodeId, info = "streq-events-v1")`, у каждого
запроса `X-Streq-Ts` и `X-Streq-Sig = base64(HMAC-SHA256(ключ, ts \n nodeId \n scope \n тело))`. Нового
секрета на камере не появляется: подпись живёт ровно столько, сколько ключ устройства.

Проверить на камере: `cerberus snapshot` (отдаёт ли majestic кадр по настроенному адресу) и
`cerberus motion` (эпизод, как будто кто-то прошёл мимо).

Видео через агента не идёт: релей тянет `rtsp://{overlayIp}:554/stream0` прямо с камеры по туннелю.
P2P этот агент не делает — камера остаётся на пути через хаб, который работает всегда.

Expand Down
4 changes: 4 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ fw_setenv cerberus_enrollment_token tok_...
| `cerberus_ping_interval` | `CERBERUS_PING_INTERVAL` | `60` | 探测间隔(秒),`0` 表示关闭 |
| `cerberus_ping_timeout` | `CERBERUS_PING_TIMEOUT` | `3` | 单次探测等待的秒数 |
| `cerberus_ping_fails` | `CERBERUS_PING_FAILS` | `3` | 连续多少次探测无响应后重建接口 |
| `cerberus_events` | `CERBERUS_EVENTS` | `1` | 移动事件;`0` 表示不安装钩子、不发送事件 |
| `cerberus_snapshot_url` | `CERBERUS_SNAPSHOT_URL` | `http://127.0.0.1/image.jpg?width=640&height=360` | majestic 的截图;需要密码时写成 `http://user:pass@127.0.0.1/…` |

`cerberus_ca` 比看上去更重要:固件可能根本不带 CA 证书包,此时注册会因协调器证书校验失败而中断,
除了重新刷写固件之外无法修复——请将其指向根证书(Let's Encrypt 使用 ISRG Root X1)。
Expand All @@ -130,6 +132,8 @@ cerberus up # 用已保存的材料启动接口
cerberus status # 已保存的内容、握手时间、隧道是否有响应
cerberus keygen # 打印密钥对(stdout 为数据,诊断信息输出到 stderr)
cerberus clean # 移除代理:接口、启动脚本、已保存的材料以及二进制文件本身
cerberus motion # 像 majestic 的钩子一样,通知正在运行的代理“有移动”
cerberus snapshot # 从 CERBERUS_SNAPSHOT_URL 取一帧到 /tmp/cerberus-snapshot.jpg
```

### 以前的 `streq_*`
Expand Down
3 changes: 3 additions & 0 deletions S99cerberus
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export CERBERUS_PING_TARGET="$(uenv ping_target)"
export CERBERUS_PING_INTERVAL="$(uenv ping_interval)"
export CERBERUS_PING_TIMEOUT="$(uenv ping_timeout)"
export CERBERUS_PING_FAILS="$(uenv ping_fails)"
# Motion events: on by default. cerberus_snapshot_url carries majestic's password when it has one.
export CERBERUS_EVENTS="$(uenv events)"
export CERBERUS_SNAPSHOT_URL="$(uenv snapshot_url)"
CERBERUS_IFACE=$(uenv iface)
export CERBERUS_IFACE=${CERBERUS_IFACE:-wg0}

Expand Down
Loading
Loading