AI・開発、電子工作、クルマ。実際にやってみたことを残すブログ

Discord から Hermes Agent に頼んで VPN サーバーを起動する

スマホの Discord で「VPN サーバーを起動して」と送ると、手元の Linux で動く Hermes Agent がスクリプトで Vultr に WireGuard のサーバーを作り、DDNS を更新し、サーバーの状態と DNS を確かめてから返事をくれます。使わない夜はサーバーを削除しています。仕組みと、AI に任せる範囲の決め方をまとめます。

アイキャッチ。紺色の背景の左に「Discord から Hermes Agent に頼んで VPN サーバーを起動する」という題名と、Discord・Vultr・WireGuard のアイコンを矢印でつないだ列、左下に Seedflower。右に、Hermes Agent のロゴの、カチューシャを付けたボブの髪の女性の白い線画が大きく描かれている
アイキャッチ(Hermes Agent のロゴの画像(MIT ライセンス)を使って作成)
目次
  1. 作ったきっかけ
  2. 全体の流れ
  3. Hermes に任せる範囲
  4. 起動のスクリプトがすること
  5. 「起動した」と返す条件
  6. 使わない夜は削除する
  7. Vultr を選んだ理由

スマホの Discord から「VPN サーバーを起動して」と送ると、VPN のサーバーが立ち上がるようにしています。

受け取るのは、手元の Linux Mint で常駐させている Hermes Agent(以下 Hermes)の Discord の Bot です。Hermes は前から使っている VPN の管理スクリプトを実行して、Vultr に WireGuard のサーバーを作ります。新しいサーバーの IP は DDNS(f5.si)に登録され、スマホの WireGuard のアプリは決まった名前で接続できます。Hermes は最後に、サーバーの状態と DNS を別々に確かめて、両方そろってから「起動しました」と返してきます。

VPN のサーバーは常に動かしてはいません。使わないときは消していて、毎晩 3 時にサーバーを削除する処理が動きます。

作ったきっかけ

出先では、セキュリティのために、なるべく公共の Wi-Fi にはつながないようにしています。ただ、携帯の電波が悪い場所にいるときや、大きなデータをやり取りしなければならないときは、ホテルやフェリーの Wi-Fi に仕方なくつなぐことがあります。そういうときに、この VPN を通すようにしています。

使うのはそういう場面だけなので、VPN のサーバーを常に動かしておく必要はありません。常に動かしておくと、使っていない間も料金がかかりますし、VPN のサーバーを常に動かしておくこと自体のセキュリティのリスクもあります。そこで、使いたいときだけ気軽に起動できる仕組みが欲しくて作りました。

全体の流れ

VPN の起動の流れ。スマホの Discord から手元の Linux Mint の Hermes Agent に「VPN サーバーを起動して」と送る。Hermes は start-vpn.sh nrt を実行し、スクリプトが Vultr の東京にスナップショットからサーバーを作り、f5.si の DDNS に新しい IP を登録する。続いて status-vpn.sh と verify-ddns.sh でサーバーの状態と DNS を確かめ、両方そろったら Discord に完了を返す。毎日 3 時に cron が stop-vpn.sh でサーバーを削除する。スマホの WireGuard は DDNS の名前(ホスト名.f5.si)に接続する
Discord から頼んでから、スマホの WireGuard がつなぐまで
  1. Discord で Hermes に「VPN サーバーを起動して」と送る(DM なら普通のメッセージ、サーバーのチャンネルでは Bot へのメンション)
  2. Hermes が start-vpn.sh nrt を 1 回だけ実行する
  3. スクリプトが Vultr の東京リージョンにサーバーを作り、新しい IP を DDNS に登録する
  4. Hermes がサーバーの状態と DNS を別々に確かめる
  5. 両方そろっていれば、Discord に起動の完了と IP を返す

実際のやり取りはこうなります。Hermes はスキルを読み、スクリプトを実行し、確かめ終わってから返事をくれます。

Discord の画面。自分が「VPNサーバーを起動して」と送ると、Hermes の Bot が「Reading skill vpn-manager」と表示し、terminal で start-vpn と status-vpn のスクリプトを実行し、「Working — 3 min」と経過を出す。最後に「お待たせ。東京のVPNサーバーを起動したよ。サーバーの状態は正常で、接続先のDNSも新しいIPに更新されたことを確認済み。スマホから接続を試してみてね。」と返信している。ユーザー名とパスの一部はモザイクで隠している
Discord で頼んでから、Hermes が返事をするまで(ユーザー名とパスの一部は隠している)

Hermes に任せる範囲

Hermes には、Vultr の API を直接呼ばせていません。サーバーを作る・消す・状態を見るのは前から使っている 3 つのスクリプトで、Hermes はそれを呼ぶだけです。

どの言葉でどのスクリプトを呼ぶかは、Hermes のスキル(作業の手順書)に書いています。

頼み方 実行するもの
「VPN を起動して」(リージョンの指定なし) start-vpn.sh nrt
「東京で VPN を起動」 start-vpn.sh nrt
「ソウルで VPN を起動」 start-vpn.sh icn
「VPN を停止して」 stop-vpn.sh
「VPN の状態」 status-vpn.sh

スキルには、ほかにも決まりを書いています。

  • サーバーを作る・消すのは、はっきり頼まれたときだけ。 お金のかかるクラウドの資源を変えるので、会話の流れから勝手に起動したり作り直したりしない
  • 状態を変えるコマンドは 1 回だけ実行する
  • リージョンを指定されなければ東京にして、nrt を明示して渡す。 スクリプトは引数が無いと設定ファイルの既定のリージョンを使うので、Hermes の既定と食い違わないようにしている
  • Vultr の API を直接呼ばない。 スナップショットからの復元を待つ確認を飛ばして、使えないサーバーが残るおそれがあるため

起動のスクリプトがすること

start-vpn.sh は次の順に進みます。

  1. リージョンに合わせたファイアウォールのグループを選ぶ
  2. ラベルが wireguard-vpn のサーバーがすでにあれば、何も作らずに終わる
  3. 無ければ、WireGuard を設定済みのスナップショットからサーバーを作る
  4. Vultr の status が active になり、続いて server_status が ok になるまで待つ
  5. 割り当てられた IP を取り、f5.si の DDNS の更新 API に送る

サーバーは作るたびに違う IP になります。スマホの WireGuard の接続先は IP ではなく <ホスト名>.f5.si という名前にしてあるので、DDNS が新しい IP を返すようになれば、アプリの設定は変えずにつながります。

「起動した」と返す条件

スクリプトの最後に VPN started と出ても、Hermes はそれだけで完了とは返しません。スキルで、次の 2 つを別々に確かめるようにしています。

  • サーバーの状態: status-vpn.sh で、東京のサーバーがあり、server_status が ok であること
  • DNS: verify-ddns.sh --wait 300 で、DDNS の名前が今のサーバーの IP を返すこと

DNS を別に確かめるのは、DDNS の更新 API が成功を返すことと、DNS が実際に新しい IP を返すことが別の話だからです。verify-ddns.sh は、ふつうのキャッシュ DNS ではなく、f5.si の権威 DNS 4 台に直接問い合わせます。キャッシュ DNS は TTL の間は古い IP を返すことがあり、「まだ行き渡っていない」のか「更新が反映されていない」のかを見分けられないからです。4 台すべてが今の IP を返したときだけ OK にして、合わなければ 15 秒おきに最大 300 秒まで待ちます。

DNS が合わないまま時間切れになったときは、「サーバーは動いているが、名前はまだ確かめられていない」と返す決まりです。スクリプトの終了コードだけで成功と報告することもしません。

ただし、ここで確かめているのは、クラウドのサーバーと接続先の名前の準備ができたところまでです。WireGuard がサーバーで待ち受けているか、スマホとのハンドシェイクができるかまでは見ていません。スマホからつながらないときは、別に調べることになります。

使わない夜は削除する

stop-vpn.sh は、ラベルが wireguard-vpn のサーバーを削除します。電源を切って残すのではなく、消します。次に使うときは、またスナップショットから作ります。

この stop-vpn.sh を、cron で毎日 3 時に動かしています。8 か月ほどの記録で 247 回動いていて、そのうちサーバーを削除したのは 128 回、何も無かったのは 119 回でした。おおよそ 2 日に 1 回くらい、VPN を立ち上げていたことになります。

今の停止のスクリプトは、Vultr からのエラーの中身を確かめていません。削除を断られても Deleted と表示しうるので、止めたあとは status-vpn.sh で、サーバーが本当に無くなったかを確かめます。状態の確認も、Vultr の API が一時的にエラーを返すと「停止しています」と表示してしまうことがあるので、疑わしいときは問い合わせ直します。

Vultr を選んだ理由

VPN 経由で動画などを見ると、VPN のサーバーからスマホへ送る通信(サーバーから見たアウトバウンド)が増えます。

  • AWS EC2 は、インターネット向けのデータ転送が、全サービス合計で月 100GB の無料枠を超えると、量に応じて課金されます(EC2 の料金)
  • Vultr にも転送量の上限と超過料金はあります。ただ、アカウントごとに月 2TB の無料枠があり、動いているサーバーのプランの付属枠が稼働時間に応じて足されます。超えた分は 1GB あたり $0.01 です(転送量の上限の計算、超過料金)

無料枠と超過の単価が事前に分かっていて、通信量と料金を見ながら使いやすいと考えて、Vultr にしました。料金は 2026 年 10 月に公式の資料で確かめたもので、変わることがあります。