ONVIF連携をやっていると、必ず同じ壁にぶつかります。パスワードは合っている、ユーザも存在する、発見(Discovery)には応答する——なのに認証が必要な呼び出しは全部 NotAuthorized。ベンダのせい、ファームのせい、ライブラリのせいにされがちですが、犯人はたいてい時計です。
ONVIFの認証は WS-Security UsernameToken で、パスワードダイジェストは Base64(SHA1(nonce + Created + password))。デバイスは Created を自機の時計と照合します。NTPなしで半年動いたカメラは、平気で数分〜数時間ずれています。ダイジェストは数学的に正しくても、時刻的に受け付けられない。だから「認証失敗」になる。
対策は仕様の中にあります。GetSystemDateAndTime は数少ない認証なしで応答しなければならない操作で、これはクライアントが先に時計のずれを測り、デバイス時刻で署名するために用意された仕組みです。
この教訓と、現場でひとつずつ学ぶONVIFの癖をまとめて、小さなツールにしました。
onvif-scan
onvif-scan は、LAN内のONVIFカメラを全部発見し、資産台帳レポート(JSON+単一HTMLファイル)を出すツールです。
onvifscan scan --user onvif_user
台帳には台ごとに:メーカー、型番、ファームウェア、シリアル、チャンネル数、Profileごとのコーデック/解像度/フレームレート、PTZ有無、時計のずれ、そしてProfileごとのRTSPストリームURI(パスワードはマスク済み)が入ります。ストリームURIはそのまま、拙作 RK Edge Doctor の rkdoctor rtsp に渡して「発見 → 検収」のチェーンにできます。
実装はPython標準ライブラリのみ。依存ゼロ、現場に持ち込むマシンに何も入れなくてよく、オフラインで動きます。
フィールドより「所見」が大事
台帳にはフィールドだけでなく、所見(findings)が付きます。
- CLOCK_SKEW:時計のずれ60秒超。他人の認証失敗を説明してくれる所見です。
-
UNAUTH_INFO_DISCLOSURE:認証なしで
GetDeviceInformationが返ってきた。古いファームには普通にあります。現場のどのカメラがそうかは知っておきたい。 - XADDR_MISMATCH:発見応答のアドレスと実際の応答元が違う(二重NIC、NAT、IP変更の未反映)。応答元アドレスへ自動フォールバックします。
- MEDIA2_ONLY / LEGACY_MEDIA_ONLY:どちらのメディア世代か。新しめの機種は ver10 Media を捨てつつあります。
- AUTH_FAILED:渡した資格情報が1回だけ拒否された。ここが一番大事な設計判断につながります。
スキャナが自分のカメラから締め出されてはいけない
onvif-scan が認証に使うのは、利用者が明示的に渡した資格情報だけです(台ごとにマッピングファイル、全台共通のONVIFユーザのどちらか)。そして各資格情報はちょうど1回だけ試します。デフォルトパスワードのリストなし、辞書なし、「念のため admin/admin」なし、リトライなし。ベンダによってはログイン失敗の繰り返しでアカウントがロックされます。台帳ツールが(丁寧にでも)総当たりをするなら、それは道具ではなく負債です。パスワードはどこにも書きません。ストリームURIはマスクして保存し、完成したレポートからは使用したパスワード文字列を消去します。
さらに全体が読み取り専用です。Get* のみ、設定変更なし、PTZ移動なし。
その他の癖、ざっと
- ONVIFは初期状態で無効な現行ファームが多い(Hikvision 5.5.0以降、Axis 5.40以降)。しかもONVIFユーザはWeb UIのユーザとは別体系のことが普通。「何も見つからない」の半分はこれです。0件のときは断定せず、その可能性をヒントとして出します。
- Scopesは当てにならない語り手です。名前/ハードウェア/MAC/場所まで放送する機種もあれば、ProfileのURIひとつだけの機種もある。識別情報は段階的に組み立て、無いものは「無い」と正直に書きます。
- ファームのXMLは雑です。前缀未束縛、名前空間宣言なし。パーサはそれを許容し(所見として記録し)、デバイスを落としません。
詳しい一覧はリポジトリの vendor-notes にあります。
正直な現状
v0.1 です。厳格型/標準型/レガシー型の3種のベンダ挙動を模したシミュレータ(リポジトリ同梱、CIで実行)で、発見から台帳までエンドツーエンド検証済みです。時計ずれケースは「ローカル時刻で署名→拒否」「デバイス時刻で署名→成功」の両方向を再現しています。ただし実機検証はまだこれからで、実カメラのレポートを募集中です。実際のカメラ群で動かしたら、JSONレポート(秘匿化可)を添えた device report issue が一番ありがたい貢献です。
リポジトリ: https://github.com/anguoyang/onvif-scan (Apache-2.0、good first issue あります)。現場をノートPCと表計算で回る半日が浮いたら、スターを付けてもらえると次のインテグレータが見つけやすくなります。