OpenWrtでmihomoを動かす方法|バイパスルーターの透過プロキシ設定

OpenWrtのメインルーター/バイパスルーターでmihomoを直接動かす手順を解説。コアの配置、設定ディレクトリ、透過プロキシ、LAN端末のゲートウェイ設定を紹介します。

まずは構成を整理:コア、ゲートウェイ、透過プロキシは別の役割

OpenWrtでmihomoを直接動かしても、GUI付きのClashクライアントをインストールしたことにはなりません。コアは設定の読み込み、プロキシ接続、ルール適用を担い、システムは端末の通信をコアへ転送します。起動ログが正常でも確認できるのはプロセスの稼働までで、LAN端末がプロキシを利用中とは限りません。

本記事では、OpenWrt 23.05/24.10で一般的なfirewall4とnftables環境を前提に、Linux TUNと自動ルーティングを使う方法を説明します。設定項目が利用できるかは、実際に使用するmihomoのバージョンで検証してください。旧版のfirewall3、メーカー独自のファイアウォール、既存のプロキシプラグインには、同じルールをそのまま適用できません。

  • メインルーターとして使う場合:OpenWrtが端末のデフォルトゲートウェイなので、コアによる通信の取り込み、ファイアウォールの許可、DNS転送を設定します。
  • バイパスルーターとして使う場合:OpenWrtをメインルーターと同じLANに接続し、テスト端末のゲートウェイとDNSも変更します。
  • 明示的プロキシでのテスト:アプリから7890の混合ポートへ接続します。ノードとルールの確認用であり、透過プロキシの設定完了を意味するものではありません。

バイパスルーターのIPアドレスと端末のゲートウェイを決める

まずは1台の端末でテストし、LAN全体のDHCP設定をすぐに変更しないでください。以下は一式の設定例です。実際の環境では既存のサブネットに合わせてすべてのアドレスを調整し、一部だけ変更しないでください。

機器・設定項目設定例用途
LANのサブネット192.168.10.0/24メインルーター、バイパスルーター、端末を同じサブネットに配置
メインルーター192.168.10.1インターネットに接続し、DHCPを引き続き提供
OpenWrtバイパスルーター192.168.10.2DHCPのアドレスプールと重複しない固定アドレス
バイパスルーターのデフォルトゲートウェイ192.168.10.1コアがノードや直接接続先へ通信する際の上位ゲートウェイ
テスト端末192.168.10.100既存のサブネットマスクを維持
端末のゲートウェイとDNS192.168.10.2テスト端末の通信とDNSクエリをバイパスルーターへ送る

LuCIの「ネットワーク」→「インターフェース」→「LAN」→「編集」で、固定アドレス、IPv4ゲートウェイ、DNSを確認します。LAN側のポートが1つのバイパスルーターは通常br-lan経由でメインルーターに接続します。2ポートのメインルーターではPPPoEや別のWANデバイスを経由して外部へ接続する場合もあるため、ネットワークデバイス名だけで出口を判断しないでください。

ip -4 addr show
ip -4 route show
ip route get 223.5.5.5
cat /etc/resolv.conf

バイパスルーター自身のデフォルトルートはメインルーターへ向けます。自分自身を指定してはいけません。初回の設定では、バイパスルーターのシステムDNSに直接到達できるDNSサーバーを指定しましょう。「コアの起動にノードのドメイン名解決が必要だが、そのDNS解決には起動前のコアが必要」という循環を防げます。

DHCPサーバーはメインルーターだけで稼働させます。同じブロードキャストドメインで、バイパスルーターから別のアドレスを配布しないでください。IPv6も別途設計が必要です。端末のIPv4ゲートウェイを手動で変更しても、メインルーターのIPv6ルーター広告を使って直接通信する場合があります。

mihomoコアと設定ディレクトリを配置する

アーキテクチャ、カーネルモジュール、ストレージ容量を確認する

まずCPUアーキテクチャとファームウェア情報を確認し、対応するLinuxバイナリを選びます。x86_64、aarch64、各種MIPSは互換性がありません。リリースによっては命令セットの違いもあります。ファイル名が似ていても、同じ機器で実行できるとは限りません。

uname -m
ubus call system board
df -h /overlay /tmp
opkg list-installed | grep -E 'kmod-tun|firewall4|nftables'

opkgを使う公式ファームウェアでは、opkg updateとopkg install kmod-tunでTUNモジュールをインストールできます。モジュールは現在のファームウェアのカーネルに対応している必要があります。依存関係に問題がある場合は、ファームウェアとパッケージソースの対応を確認してください。別バージョンのカーネル向けモジュールを強制インストールしてはいけません。自動通信取り込みに必要なnftables機能は、ファームウェアのビルド設定にも左右されます。

実行ファイルの配置先と作業ディレクトリを固定する

展開済みでアーキテクチャが一致するコアを/usr/bin/mihomoに配置し、設定ファイルを/etc/mihomo/config.yamlに保存します。設定にサブスクリプションの認証情報が含まれる場合があるため、ディレクトリとファイルへのアクセスを制限してください。端末の永続ストレージが不足している場合は、再起動で消去される/tmpに設定を置き続けず、外部ストレージを先に用意しましょう。

mkdir -p /etc/mihomo
chmod 700 /etc/mihomo
chmod 755 /usr/bin/mihomo
chmod 600 /etc/mihomo/config.yaml
/usr/bin/mihomo -v
/usr/bin/mihomo -t -d /etc/mihomo -f /etc/mihomo/config.yaml

-dは作業ディレクトリ、-fは設定ファイルを指定し、-tは設定の検証だけを行います。設定からGeoIP、GeoSite、ルールセット、プロキシプロバイダーのファイルを参照する場合は、それぞれのパス、読み取り権限、更新時のネットワーク接続も確認してください。最初のトラブルシューティングでは外部ファイルへの依存を一時的に外すと、ダウンロード失敗をTUNの問題と誤認せずに済みます。

まず明示的プロキシを確認してからTUNで通信を取り込む

2段階で起動する方法がおすすめです。1回目は実際に利用できるノードを含む設定を使い、tun.enableをfalseにして、ルーター本体から混合ポートへの接続を確認します。ノードが利用できることを確かめてから、2回目にTUNと自動ルーティングを有効化します。こうすればノードの問題と通信転送の問題を切り分けられます。

以下は設定と直接接続の基本動作を確認するための最小構成です。プロキシノードはなく、プロキシグループもDIRECTのみのため、プロキシ経由の通信は発生しません。実運用では、サブスクリプション内の有効なノードまたはproxy-providersを取り込み、実際のノードをプロキシグループに追加してください。既存の完全な設定をそのまま上書きしないでください。

mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
ipv6: false

tun:
  enable: false
  device: mihomo
  stack: mixed
  auto-route: true
  auto-redirect: true
  auto-detect-interface: true
  dns-hijack:
    - any:53

dns:
  enable: true
  listen: 127.0.0.1:1053
  ipv6: false
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  default-nameserver:
    - 223.5.5.5
  nameserver:
    - 223.5.5.5
    - 119.29.29.29

proxies: []

proxy-groups:
  - name: 出口選択
    type: select
    proxies:
      - DIRECT

rules:
  - IP-CIDR,192.168.10.0/24,DIRECT,no-resolve
  - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
  - MATCH,出口選択

まずフォアグラウンドで/usr/bin/mihomo -d /etc/mihomo -f /etc/mihomo/config.yamlを実行し、別のSSHセッションからポートの状態を確認します。ファームウェアにcurlがインストールされている場合は、次のリクエストを実行できます。このコマンドで確認できるのはルーター本体から混合ポートまでの接続であり、LANの透過転送ではありません。

curl --proxy http://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com/

実際のノードを追加したら、「出口選択」がDIRECTのままになっていないことを確認します。ログに記録されたマッチルール、プロキシグループ、ノード名からリクエストの経路を判断してください。Webページが開くかどうかだけでは、直接接続とプロキシ接続を区別できません。

TUN、ポリシールーティング、OpenWrtファイアウォールを連携させる

明示的プロキシの動作を確認したら、tun.enableをtrueに変更し、設定を検証してから起動します。auto-routeは通信を取り込むためのルートを作成し、Linuxのauto-redirectはnftablesなどを使って通信のリダイレクトを補助します。自動生成されるルーティングテーブル番号、ルールの優先順位、チェーン名はバージョンによって異なる場合があります。ネット上で見つけた固定番号を汎用の標準値として扱わないでください。

ip link show mihomo
ip rule show
ip route show table all
nft list ruleset
sysctl net.ipv4.ip_forward

次の3つのシステム要件を確認する

  1. TUNデバイスが存在する:設定で指定したmihomoデバイスが確認できる必要があります。見つからない場合は、/dev/net/tun、モジュール、起動権限を確認してください。
  2. ルーティングの取り込みが有効:起動前後でip rule、ルーティングテーブル、nftablesのルールを比較します。インターフェースの作成やルールの追加に失敗するログが繰り返し出ていないか確認してください。
  3. ファイアウォールが転送を遮断していない:IPv4フォワーディングを有効にし、LANからTUNへの転送も許可する必要があります。TUNインターフェースが見えるだけでは、firewall4で転送が許可されているとは限りません。

LuCIの「ネットワーク」→「ファイアウォール」→「一般設定」で、ゾーンと転送設定を確認します。TUN専用ゾーンを使用する場合は、実際の画面に合わせてデバイスmihomoをそのゾーンに割り当て、LANからの転送を許可します。ゾーンへの入力許可と、ゾーン間の転送許可は別の設定です。テスト目的でWAN側の入力を「許可」に変更しないでください。

ファームウェアによって、未管理デバイスをゾーンに割り当てる方法や、自動リダイレクトとfirewall4の連携方法は異なります。現在のルールセットを確認して設定してください。固定のiptablesスクリプトをそのままコピーするのは避けましょう。特にREDIRECT、TPROXY、自動TUNのルールを同時に追加しないでください。本記事ではTUN方式を使用します。

LAN側インターフェースが1つのバイパスルーターでは、同一インターフェース内の転送、戻りの経路、コネクショントラッキングも確認します。直接接続の通信がbr-lanから入り、同じインターフェース経由でメインルーターへ送られる場合があります。片方向だけ通信できる、接続が断続的に切れるといった症状があれば、パケットをキャプチャして送信元アドレスと応答経路を確認してから、必要に応じて送信元アドレス変換を検討してください。LAN全体のマスカレードを先に有効化するのは避けましょう。

最初のトラブルシューティングでは、「ネットワーク」→「ファイアウォール」でソフトウェアフローオフロードとハードウェアフローオフロードを一時的に無効にすることをおすすめします。通信が安定したら、1つずつ元に戻して再確認してください。バイパスルーターからはインターネットに接続できるのに端末から接続できない場合は、まず転送経路を調べます。先にノードを変更しないでください。

DNS転送を設定し、名前解決のループとIPv6経由の迂回を防ぐ

設定例では、端末からバイパスルーターの53番ポートへ送られたDNSクエリを、dnsmasqがローカルのmihomoの1053番ポートへ転送し、さらに上位DNSサーバーへ送ります。ポートを分けることで、dnsmasqとmihomoが同じアドレスとポートを奪い合うのを防げます。

LuCIの「ネットワーク」→「DHCP/DNS」→「一般設定」で、DNS転送先を127.0.0.1#1053に設定します。対応する詳細設定では「上流から取得したDNSサーバーを使用しない」オプションも確認してください。画面上の項目名はLuCIのバージョンによって異なります。重要なのは、dnsmasqが自動取得した他の上位DNSサーバーにも同時にクエリを送らないようにすることです。

# まずバックアップし、以下は1つ目のdnsmasqインスタンスを対象に操作
cp /etc/config/dhcp /etc/config/dhcp.before-mihomo
uci set dhcp.@dnsmasq[0].noresolv='1'
uci -q delete dhcp.@dnsmasq[0].server
uci add_list dhcp.@dnsmasq[0].server='127.0.0.1#1053'
uci commit dhcp
/etc/init.d/dnsmasq restart

nslookup example.com 127.0.0.1

このコマンドを実行すると、対象インスタンスに既存のDNS転送先リストが置き換わります。社内ドメイン向けの条件付き転送を設定している場合は、先に記録して該当する設定を残してください。dnsmasqを複数のインスタンスで動かしている環境では、1つ目がLAN用だと決めつけないでください。端末のDNSアドレスが実際に192.168.10.2になっていることも確認します。

通常のDNS、fake-ip、暗号化DNSの違いを理解する

  • fake-ip:このモードでは、クエリに対して198.18.0.0/16内のアドレスが返される場合があります。これは仮想的なマッピングであり、接続先Webサイトの実際のグローバルIPアドレスではありません。該当する通信は引き続きコアで処理する必要があります。
  • 通常のDNSのリダイレクト:any:53は対象となる通常のDNSリクエストを取り込む設定です。あらゆる名前解決プロトコルを識別してリダイレクトできるわけではありません。
  • DoHとDoT:ブラウザーのセキュアDNS、アプリ内のDoH、TCP 853を使うDoTは、通常の53番ポートをリダイレクトする設定だけではdnsmasq経由になりません。テストでは、まずアプリの名前解決経路を確認してください。
  • 上位DNSサーバーの選択:設定例では起動時の依存関係を減らすために通常のIPアドレスを指定しており、暗号化DNSは有効にしていません。ドメイン名を使うDoHアドレスに変更する場合は、初回接続時の名前解決とノードのドメイン名解決を別途計画してください。

ipv6: falseはコアの該当機能を制限する設定であり、端末が受け取るIPv6のデフォルトルートを無効にはしません。最初の動作確認ではテスト端末のIPv6を一時的に無効にし、その後、IPv6 TUN、ルーター広告、ファイアウォールを計画してください。「IPv4の出口が正しい」だけで、デュアルスタック通信もすべて取り込めていると判断しないでください。

procdで起動と再起動を管理する

フォアグラウンドでの実行、DNS、端末からのアクセスがすべて正常になったら、起動時サービスとして登録します。以下のprocdスクリプトはプロセスの監視のみを行い、サブスクリプションの更新や独自のファイアウォール設定は含みません。/etc/init.d/mihomoとして保存し、別のプラグインに同名のサービスが残っていないことを確認してください。

#!/bin/sh /etc/rc.common

START=99
STOP=10
USE_PROCD=1

start_service() {
    /usr/bin/mihomo -t -d /etc/mihomo \
        -f /etc/mihomo/config.yaml || return 1

    procd_open_instance
    procd_set_param command /usr/bin/mihomo \
        -d /etc/mihomo -f /etc/mihomo/config.yaml
    procd_set_param respawn 3600 5 5
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_set_param file /etc/mihomo/config.yaml
    procd_close_instance
}
chmod 755 /etc/init.d/mihomo
/etc/init.d/mihomo enable
/etc/init.d/mihomo start
logread -e mihomo

サービスを起動する前に、フォアグラウンドで動かしていたコアを停止し、2つのインスタンスがポートやTUNデバイスを奪い合わないようにします。このスクリプトはデフォルトでroot権限で実行します。TUNの作成、ルート設定、ファイアウォール設定に必要な権限があるためです。その分、設定ファイル、サブスクリプションの取得元、コアの実行ファイルを厳重に管理してください。

設定を変更したら、まずmihomo -tを実行してから/etc/init.d/mihomo restartを実行します。スクリプトにファイルを記述しても、自動サブスクリプション更新機能が追加されるわけではありません。更新には、ダウンロード、設定の検証、アトミックな置き換えを別途用意する必要があります。設定に認証情報が含まれる場合、起動ログ全体を公開の場に投稿しないでください。

起動順を遅くしても、ネットワークが利用可能になったとは限りません。機器を再起動し、インターフェースの起動後にノードのドメイン名を解決できるか、接続を確立できるか、端末から再びアクセスできるか確認してください。リモートのルールセットに依存する構成では、コールドブート直後に外部ファイルへアクセスできない場合も考慮しましょう。

通信経路ごとに動作を確認し、復旧手段を確保する

確認はルーター本体から1台の端末へと進め、最後にDHCP配布へ範囲を広げます。変更は一度に1つだけにし、関連するログとゲートウェイ設定を記録してください。速度測定で分かるのは測定時の通信状況だけです。ルーティング経路やDNS経路の確認に代わるものではありません。

症状まず確認する項目判断の手がかり
コアが起動しないアーキテクチャ、設定、ポート、TUNモジュール設定検証と起動時のエラーを確認し、端末のゲートウェイはまだ変更しない
明示的プロキシは正常だが、端末から接続できない端末のゲートウェイ、TUNによる通信の取り込み、LAN転送端末のリクエストがコアのログに記録されるか比較する
IPアドレスには接続できるが、ドメイン名ではアクセスできないdnsmasq、1053番ポートの待ち受け、上位DNSの名前解決バイパスルーターと端末からのDNSリクエストを個別にテストする
一部のアプリが直接接続するルール、IPv6、アプリ内DNS実際に適用されたルールとデュアルスタックの通信経路を確認する
ファイアウォールの再読み込み後に通信が切れる自動生成されたルールが残っているか再読み込み前後のnftablesとポリシールーティングを比較する
再起動後に通信が復旧しないサービスの有効化状態、インターフェースの起動順、コールドブート時の依存関係システムログで最初に発生したエラーを特定する

1台の端末で確認してから対象を広げる

  1. 端末からメインルーターとバイパスルーターの管理画面にアクセスし、LAN内の直接通信が正常か確認します。
  2. 端末のIPv4デフォルトゲートウェイとDNSが両方ともバイパスルーターを指していることを確認します。古いDNSキャッシュを消去してから、新しいブラウザー接続を試してください。
  3. 直接接続するリクエストとプロキシ経由にするリクエストをそれぞれ確認し、適用されたルールとノードを調べます。ページが読み込まれるかどうかだけで判断しないでください。
  4. UDPを使うアプリ、IPv6の方針、ファイアウォール再読み込み後の動作を確認してから、メインDHCPサーバーで配布するゲートウェイとDNSを変更します。

正常に停止したら、TUNインターフェース、ポリシールーティング、自動生成されたファイアウォールルールが削除されたことを確認します。残っている場合は、保存した設定を基準に復元してください。nftablesのルールセット全体を消去するコマンドをリモートから実行しないでください。動作中の状態を整理する必要がある場合は、永続設定を復元してから再起動を検討します。

OpenWrtをバイパスルーターとして使う際の要点は、ルーターにバイナリを置くだけではありません。端末のゲートウェイ、DNS、通信の取り込み、ルールの出口、サービスの起動・停止を、検証可能な1つの経路としてつなぐことが重要です。この順番で確認すれば、サブスクリプションの変更、ルールの調整、コアのアップデート時に、問題がどの層で起きているかを特定しやすくなります。

Clash をダウンロード インストーラーとシステム要件