家と AWS の監視をお互いに見せ合う ― SSH トンネル1本で Cacti のグラフを両方に、Uptime Kuma は相手の Kuma も見張る
Web・メール・DNS を AWS のサーバーに移してから、監視は家と AWS の両方に置いています(Web・メール・DNS を AWS に移した話)。止まったことに気づくための Uptime Kuma と、CPU・メモリ・アクセス数などをグラフにする Cacti です(入れている監視のアプリ)。
ただ、Cacti はそれぞれ自分のサーバーしかグラフにしていませんでした。家と AWS のグラフを見比べるには、2つの画面を行き来する必要があります。そこで、どちらの Cacti からも、両方のサーバーのグラフを見られるようにしました。あわせて、Uptime Kuma の見張りの穴も埋めました。作業は、いつもどおり AI(Claude Code)に言葉で頼んで進めました。
1. やったこと
| 対象 | 変えたこと |
|---|---|
| 家の Cacti | AWS のサーバーを登録。グラフ 22 枚(AWS の Cacti と同じ内容) |
| AWS の Cacti | 家のサーバーを登録。グラフ 14 枚(負荷・メモリ・CPU・通信量・ディスクなど) |
| 家の Uptime Kuma | 「AWS の Uptime Kuma」と「SNMP のトンネル」の見張りを追加 |
| AWS の Uptime Kuma | 「家の Uptime Kuma」と「SNMP のトンネル」の見張りを追加 |
どちらのサーバーも、SNMP(機器の数字を読み取るための共通の仕組み)を外に開けていません。家から AWS へ張った SSH のトンネル1本の中を通して、お互いの数字を読んでいます。
2. SSH トンネル1本で、両方向に読む

SSH には、つないだ線の中に別の通信を通す「ポート転送」という機能があります。転送には2つの向きがあり、1本の接続で両方を使えます。
-L(つないだ側 → 相手):家のサーバーの決まった番号に話しかけると、AWS のサーバーの SNMP に届く-R(相手 → つないだ側):AWS のサーバーの決まった番号に話しかけると、家のサーバーの SNMP に届く
# 家のサーバーで常に動かす(systemd のサービス。切れたら30秒後につなぎ直す)
ssh -N -i 専用の鍵 \
-L 127.0.0.1:XXXX:127.0.0.1:XXXX \ # 家 → AWS の SNMP
-R 127.0.0.1:XXXX:127.0.0.1:XXXX \ # AWS → 家の SNMP
-R 127.0.0.1:XXXX:127.0.0.1:XXXX \ # AWS → 家の Uptime Kuma(後で説明)
snmptun@(AWS のサーバー)
つなぐのはいつも家の側からです。こうすると、AWS のサーバーに「家に入る鍵」を置かずに済みます。AWS のサーバーは外から攻撃される側なので、もし入られても家には届かないようにしておきたいからです。
AWS のサーバーには、トンネル専用のユーザーを作りました。ログイン用のシェルは持たせず、鍵には「決まった通り道を開くこと」しか許していません。
# AWS のサーバーの ~snmptun/.ssh/authorized_keys(鍵は1行)
restrict,port-forwarding,permitopen="127.0.0.1:XXXX",permitlisten="127.0.0.1:XXXX",permitlisten="127.0.0.1:XXXX",command="/sbin/nologin" ssh-ed25519 AAAA... home-server
restrict:いったん全部を禁止する(画面・ファイル転送・転送のすべて)port-forwarding:転送だけを許すpermitopen:-Lで行ける先を、AWS のサーバーの SNMP だけにするpermitlisten:-Rで開ける入口を、決まった番号だけにするcommand="/sbin/nologin":コマンドを動かそうとしても、何もせずに終わる
3. つまずいたところ:SNMP は UDP、SSH は TCP
SNMP はふつう UDP(届いたかを確かめない、軽い送り方)で話します。一方、SSH のトンネルが通せるのは TCP(届いたかを確かめながら送る方式)だけです。
まず SNMP に TCP でも受けさせた
Linux の SNMP(Net-SNMP)は、設定1行で TCP でも受けられます。受けるのは、今までどおりサーバー自身(127.0.0.1)からだけです。
# /etc/snmp/snmpd.conf
agentAddress udp:127.0.0.1:XXXX,tcp:127.0.0.1:XXXX
これで、コマンドの snmpget では tcp:127.0.0.1:XXXX のように書けば、トンネルの先の数字が読めました。Cacti にも同じように「tcp:127.0.0.1」と登録すると、5分ごとの数字の取得は動きました。
Cacti の一部が TCP を使えなかった
ところが、ネットワークの口やディスクの一覧を作る処理だけは、0 件になりました。調べると、Cacti はこの処理に PHP の SNMP 機能を使っていて、これが「tcp:」の書き方に対応していませんでした(「tcp」という名前のサーバーを探しに行って失敗する)。5分ごとの取得は Net-SNMP のコマンドを使うので動いていた、という違いです。
UDP と TCP の中継を自作した
そこで、Cacti にはふつうの UDP で話してもらい、間で TCP に変える小さな中継を Python で書きました。50 行ほどです。
- Cacti からの問い合わせを UDP で受ける
- トンネルの入口に TCP でつなぎ、そのまま送る
- 返事を1つ受け取ったら、UDP で Cacti に返す
気をつけたのは「返事を1つ受け取った」の判断です。TCP では、データが途中で分かれて届くことがあります。SNMP の電文は、先頭に「この後ろに何バイト続くか」が書いてある形(BER という書き方)なので、それを読んで、ちょうど1つ分だけ受け取るようにしました。
def recv_ber(conn):
# SNMP の電文は BER の SEQUENCE 1つ。長さの欄を読んで、電文1つ分だけ受け取る
head = recv_exact(conn, 2)
if head[1] < 0x80:
return head + recv_exact(conn, head[1])
n = head[1] & 0x7F
lb = recv_exact(conn, n)
return head + lb + recv_exact(conn, int.from_bytes(lb, "big"))
受ける番号と送る先の番号は起動時に渡す形にしたので、家と AWS で同じファイルを使っています。これで一覧も作れるようになり、家の Cacti では、AWS の Cacti と同じ 22 枚のグラフ(Web のアクセス数・DNS の問い合わせ数・メールの件数などの独自の数字も含む)がそろいました。
4. Cacti に登録するときの小さな注意
- 同じ「127.0.0.1」が2台になる:AWS の Cacti には、自分自身がすでに 127.0.0.1 で登録されています。家のサーバーも 127.0.0.1(番号違い)なので、ふつうは「同じ機器がある」と断られます。コマンドで登録するとき
--proxyを付けると、番号違いの別の機器として登録できました - パスワードをコマンドに書かない:登録はいったん仮のパスワードで行い、本物はデータベースに直接入れました。コマンドの引数に書くと、同じサーバーのほかの利用者から見える一覧(ps)に一瞬出てしまうためです
- パスワードが控えと違っていた:AWS の SNMP のパスワードは、家のものとは別の値で、手元の控えに載っていませんでした。AWS の Cacti の中にしか無い状態だったので、これを機に控えに足しました
- 一覧の読み込み直しは遅い:問い合わせのたびにトンネルの中に通り道を開くので、一覧を作り直す処理には2分以上かかりました。5分ごとの取得は、家の Cacti で全体 2.3 秒ほどのまま変わりませんでした
5. Uptime Kuma の穴を埋める
Uptime Kuma は、もともと家と AWS の両方から相手を見ていました。家の Kuma が AWS の Web・メール・DNS を、AWS の Kuma が家の回線と、家のサーバーから1分ごとに届く「生きています」の連絡を見ています。
見直すと、相手の Kuma そのものが止まったときに気づけないことが分かりました。
- AWS の Kuma が止まると、家が止まったときの知らせが来なくなる。でも、それに気づく仕組みが無い
- 家の Kuma が止まっても、「生きています」の連絡は Kuma ではなく別の定期処理(cron)が送っているので、AWS 側では正常に見える

そこで、両方の Kuma に見張りを2つずつ足しました。
| どこの Kuma | 足した見張り | 見方 |
|---|---|---|
| 家 | AWS の Uptime Kuma | 家の IP からだけ開ける Kuma の画面が開けるか |
| 家 | SNMP のトンネル | 家の側のトンネルの入口につながるか |
| AWS | 家の Uptime Kuma | トンネルに足した通り道(-R)から、家の Kuma の画面が開けるか |
| AWS | SNMP のトンネル | AWS の側のトンネルの入口につながるか |
どれも1分ごとに確かめ、止まったら今までと同じメールで知らせます。家の Kuma は家の外からは開けないので、AWS から見るのにも同じトンネルを使いました。トンネルが切れたときは「家の Uptime Kuma」と「SNMP のトンネル」の両方が止まって見えるので、トンネルの問題だと分かります。
見張りの追加は、Kuma を 30 秒ほど止め、データベースの控えを取ってから、同じ種類の既存の見張りを写して名前と相手だけを変える方法で行いました。起動後、4つとも正常になり、既存の見張りもすべて正常のままでした。
6. 残っていること
- 知らせのメールの試し:新しい見張りで実際にメールが届くかは、わざと止めては試していません(知らせ先は既存の見張りと同じ設定です)
- 家が止まっている間の数字:家のサーバーかトンネルが止まると、AWS の Cacti からも家が見えなくなります。止まったことは Kuma で分かりますが、その間のグラフは途切れます
まとめ
- 家から張った SSH トンネル1本で、家と AWS がお互いの SNMP を読めるようにした。SNMP は外に開けていない
- AWS には家に入る鍵を置かず、トンネル用の鍵は決まった通り道を開くことしかできない
- Cacti の一部が TCP の SNMP を読めなかったので、UDP と TCP の中継を Python で自作した
- Uptime Kuma で、相手の Kuma とトンネルも見張るようにし、「監視が止まったことに気づけない」穴を埋めた
