RT2600ACでWireGuardを使用する

RT2600acのVPNではOpenVPNは対応していますが、最近流行のwireguardは対応していません。これを動作させるまでの備忘録を載せます。

こちらのサイトでwireguardをRT2600acで使用する方法を載せています。しかし、コンパイル済みのバイナリをroot権限で実行するなどセキュリティ的に怖い部分があります。中身を読んだ感じこのスクリプトの作者というよりこのスクリプトが依存するEntwareにやや疑問が湧いたくらいで実際には問題ないと思いますが、どうせなら自分でバイナリをコンパイルして自分で設定してみます。

https://qiita.com/bellx2/items/68d0b444c2bf372ea33c

Wslでwireguardとwireguard-toolsをコンパイルします。 wsl標準のGOコンパイラでは古いようなのでコンパイラは自分で落とします。以下コマンド

#依存ファイルインストール
sudo apt update
sudo apt install -y \
  git build-essential \
  gcc-arm-linux-gnueabihf
#goは自分でダウンロード
cd /tmp
wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.1.linux-amd64.tar.gz
export PATH=/usr/local/go/bin:$PATH
export GOTOOLCHAIN=local
#wireguardビルド
git clone https://git.zx2c4.com/wireguard-go
cd wireguard-go
export GO111MODULE=on
export GOOS=linux
export GOARCH=arm
export GOARM=7
export CGO_ENABLED=1
export CC=arm-linux-gnueabihf-gcc
make
arm-linux-gnueabihf-strip wireguard-go #サイズ削減
cd ..
#wgビルド
git clone https://git.zx2c4.com/wireguard-tools
cd wireguard-tools/src
make \
  CC=arm-linux-gnueabihf-gcc \
  STRIP=arm-linux-gnueabihf-strip
cd ..
#wireguard-goとwgを一度/tmpフォルダーに移動後、sshでログインし、/volume1/wgに配置します。
#/volume1フォルダは更新時に削除されないため、独自ファイル配置するのによいようです。
cd wireguard-go
scp wireguard-go <acount>@<router ip>:/tmp/
cd ../wireguard-tools/src
scp wg <acount>@<router ip>:/tmp/
ssh <acount>@<router ip> -p <port>
sudo -i
mkdir /volume1/wg
mv /tmp/wireguard-go /volume1/wg
mv /tmp/wg /volume1/wg
#ファイルに実験権限付与
chmod +x /volume1/wg/wg
chmod +x /volume1/wg/wireguard-go

サーバー、クライアントそれぞれに秘密鍵、公開鍵、事前共有鍵を作成します(WireGuardの場合、自分がインターフェース、相手がピアとなり、サーバークライアントの関係は実際にはないようです)。

mkdir -p /volume1/wg/keys
chmod 600 /volume1/wg/keys

鍵ファイルの作成にgenerate_keys.shスクリプトを作成しました。

#!/bin/sh
WG_DIR=/volume1/wg
KEY_DIR=${WG_DIR}/keys

name=$1
$WG_DIR/wg genkey | tee ${name}_private.key | $WG_DIR/wg pubkey > ${name}_public.key | $WG_DIR/wg genpsk > ${name}_psk.key
chmod 600 ${KEY_DIR}/${name}_private.key ${KEY_DIR}/${name}_psk.key
chmod 644 ${KEY_DIR}/${name}_public.key

以下のように鍵を作成できます。

/volume1/wg/generate_keys server

設定ファイル wg0.confも/volume1/wgに保存します。設定と内容は以下です。よくサンプルに表示されているAddressやup, downはwg-quick用の設定なので、wgのみで設定している場合は使えません。

chmod 600 /volume1/wg/wg0.conf
chown root:root /volume1/wg/wg0.conf
[Interface]
PrivateKey = #ルーター側秘密鍵は設定ファイルに直接かかないことが推奨されているよう
#Address = <A.A.A.A/24 #wan側IP>  wg-quickを使用しないのでここの設定は使用しません
ListenPort = <ポート番号>

[Peer]
PublicKey = <クライアント側公開鍵1>
PresharedKey = <クライアント用事前共有鍵1>
AllowedIPs = <A.A.A.B/32 #このピアに割り振るローカルip>

[Peer]
PublicKey = <クライアント側公開鍵2>
PresharedKey = <クライアント用事前共有鍵2>
AllowedIPs = <A.A.A.B/32 #このピアに割り振るローカルip>

TUNデバイスの有無を確認して、なければ作ります(VPN plusをインストールしていればSRM側で作成されているようです)

#tunの有無を確認
ls -l /dev/net/tun
#なければ作成
mkdir -p /dev/net
mknod /dev/net/tun c 10 200
chmod 666 /dev/net/tun

準備ができたらwireguardをテスト起動します。

#以下でwireguardを設定します。
/volume1/wg/wireguard-go wg0
/volume1/wg/wg setconf wg0 /volume1/wg/wg0.conf
$WG_DIR/wg set $WG_IF private_key $PRIVATE_KEY

ip addr add <A.A.A.A/24 #WAN側ip> dev wg0
ip link set wg0 up
ifconfig wg0 mtu 1432 txqueuelen 1000

上記が成功したら、上記を再起動後自動で行うためスクリプトを設定します。スクリプトないで設定するのでwg0.confは必要ありません。RT2600acでは/usr/local/etc/rc.d/に保存したスクリプトは自動実行してくれるようです。ただしここもアップデート時に消える可能性があるので直接実行せず/volume1に保存したスクリプトを実行させます。
/usr/local/etc/rc.d/wireguard.shを作成し、実行権限付与

chmod +x /usr/local/etc/rc.d/wireguard.sh

内容は以下の通りです。rc.dに保存したスクリプトはstart, stopの引数を処理しないと実行されないようです。

#!/bin/sh

case "$1" in
start)
	/volume1/wg/wireguard.sh start
        ;;
stop)
	/volume1/wg/wireguard.sh stop
	;;
esac
                
exit 0

/volume1/wg/wireguard.shを作成し、実行権限付与

chmod +x /volume1/wg/wireguard.sh

内容は以下の通りです。KEY_DIRにある鍵ファイルを自動で読み取って設定していきます。

#!/bin/sh

set -e

WG_IF="wg0"
WG_DIR="/volume1/wg"
KEY_DIR="$WG_DIR/keys"
PRIVATE_KEY="$KEY_DIR/server_private.key"
PORT="46831"
IP_ADDR="172.23.0.1/24"
BASE_IP="172.23.0"
IP_INDEX=2   # .2 から割り当て開始

MTU="1432"

start_wg() {

    echo "Starting WireGuard..."

    # 既存IF削除
    ip link del "$WG_IF" 2>/dev/null || true

    # wireguard-go 起動
    "$WG_DIR/wireguard-go" "$WG_IF" &
    sleep 2

    # 基本設定適用
    "$WG_DIR/wg" set "$WG_IF" private-key "$PRIVATE_KEY"
    "$WG_DIR/wg" set "$WG_IF" listen-port  "$PORT"



    for PUB_FILE in "$KEY_DIR"/*_public.key
    do
        [ -e "$PUB_FILE" ] || continue
    
        CLIENT=$(basename "$PUB_FILE" _public.key)

        # --- serverを除外 ---
        case "$CLIENT" in
            server)
                continue
                ;;
        esac


        PSK_FILE="$KEY_DIR/${CLIENT}_psk.key"
    
        if [ ! -f "$PSK_FILE" ]; then
            echo "PSK missing for $CLIENT, skipping"
            continue
        fi
    
        ALLOWED_IP="$BASE_IP.$IP_INDEX/32"
    
        "$WG_DIR/wg" set "$WG_IF" peer "$(cat "$PUB_FILE")" \
            preshared-key "$PSK_FILE" \
            allowed-ips "$ALLOWED_IP"
    
        echo "Added peer: $CLIENT → $ALLOWED_IP"
    
        IP_INDEX=$((IP_INDEX + 1))
    done

    # IP設定
    ip addr add "$IP_ADDR" dev "$WG_IF"
    ip link set "$WG_IF" up
    ip link set "$WG_IF" mtu "$MTU"

    echo "WireGuard started."
}

add_peer() {
    CLIENT="$1"
    ALLOWED_IP="$2"

    PUB_KEY="$KEY_DIR/${CLIENT}_public.key"
    PSK_KEY="$KEY_DIR/${CLIENT}_psk.key"

    if [ ! -f "$PUB_KEY" ] || [ ! -f "$PSK_KEY" ]; then
        echo "Key files missing for $CLIENT"
        return 1
    fi

    "$WG_DIR/wg" set "$WG_IF" peer "$(cat "$PUB_KEY")" \
        preshared-key "$PSK_KEY" \
        allowed-ips "$ALLOWED_IP"

    echo "Added peer: $CLIENT"
}

stop_wg() {
    echo "Stopping WireGuard..."
    ip link del "$WG_IF" 2>/dev/null || true
    echo "Stopped."
}

case "$1" in
    start)
        sleep 20
        start_wg
        ;;
    stop)
        stop_wg
        ;;
    restart)
        stop_wg
        sleep 2
        start_wg
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac

exit 0

これで再起動時にRT2600acでwireguardが待機状態になります。

クライアント向けの設定ファイルは以下のようになります。

[Interface]
PrivateKey = <クライアント秘密鍵1>
Address = <B.B.B.B/32 #上記で設定したクライアント側ローカルip>
DNS = 1.1.1.1 #VPN越しでは標準のNTTのDNSが動作しない模様
MTU = 1432

[Peer]
PublicKey = <ルーターの公開鍵>

[Peer]
PublicKey = <クライアント側公開鍵>
PresharedKey = <クライアント用事前共有鍵>
AllowedIPs = 0.0.0.0/0 #すべての通信をVPNに通す場合
Endpoint = <ルーターグローバルip>:<ポート>

こちらも自動生成するスクリプトを準備しました。./generate_conf.sh <端末名> <割り振るip末尾>として使用します。

#!/bin/sh
WG_DIR="/volume1/wg"
KEY_DIR="$WG_DIR/keys"

PRIVATE=$(cat $KEY_DIR/${1}_private.key)
SERVER_PUB=$(cat $KEY_DIR/server_public.key)
PSK=$(cat $KEY_DIR/${1}_psk.key)
SERVER_IP="<サーバーIPまたはドメイン>"
PORT="XXXX"
PEER_IP="172.23.0.${2}"

cat > ${1}.conf <<EOF
[Interface]
PrivateKey = $PRIVATE
Address = ${PEER_IP}/24
DNS = 1.1.1.1

[Peer]
PublicKey = $SERVER_PUB
PresharedKey = $PSK
Endpoint = ${SERVER_IP}:${PORT}
AllowedIPs = 0.0.0.0/0
EOF

qrencode -t ansiutf8 < ${1}.conf
chmod 600 /volume1/wg/${1}.conf

後はrt2600acのファイアーフォールを開けば、動作確認できました。接続が確立するまでの速さはOpenVPNよりずっと早かったですが、回線速度はスマホでは差が出なかったです。

ここで問題点としてWireguardで接続した場合SRMのログには表示されず、接続されても分かりません。色々怖いので、接続時にメールを送るようにします。先ほどと同様に/usr/local/etc/rc.d/wg-watch.shを作成し、実行権限付与を付与します。内容は以下です。

#!/bin/sh

case "$1" in
	start)
		/bin/sh /volume1/wg/watch.sh &
	        ;;
        stop)
		pkill -f /volume1/wg/watch.sh
                ;;
esac
            
exit 

/volume1/wg/watch.shを作成し、実行権限付与を付与します。内容は以下です。

#!/bin/sh

IF=wg0
STATE_FILE=/tmp/wg_state
THRESHOLD=300   # 300秒以内を接続中とみなす
WG_DIR=/volume1/wg
CONNECT_SH=$WG_DIR/on_connect.sh #新規接続時に実行するスクリプト
CONNECT_SH=$WG_DIR/on_disconnect.sh

touch $STATE_FILE

while true
do
    NOW=$(date +%s)

    $WG_DIR/wg show $IF latest-handshakes | while read PUBKEY TS
    do
        if [ "$TS" = "0" ]; then
            CURRENT=0
        else
            AGE=$((NOW - TS))
            [ $AGE -lt $THRESHOLD ] && CURRENT=1 || CURRENT=0
        fi

        PREV=$(grep "^$PUBKEY " $STATE_FILE 2>/dev/null | awk '{print $2}')

        [ -z "$PREV" ] && PREV=0

        # 未接続→接続 に変わった瞬間のみ実行
        if [ "$PREV" = "0" ] && [ "$CURRENT" = "1" ]; then
            $CONNECT_SH "$PUBKEY"
        fi
        # 未接続→接続 に変わった瞬間のみ実行
        if [ "$PREV" = "1" ] && [ "$CURRENT" = "0" ]; then
            $DISCONNECT_SH "$PUBKEY"
        fi

        # 状態更新
	grep -v "^$PUBKEY " $STATE_FILE > ${STATE_FILE}.tmp
	echo "$PUBKEY $CURRENT" >> ${STATE_FILE}.tmp
	mv ${STATE_FILE}.tmp $STATE_FILE
    done

    sleep 5
done

これで接続時に/volume1/wg/on_connect.sh切断時に/volume1/wg/on_disconnect.shが実行されます。同様に作成して実行権限を付与します内容は以下です。捨てGoogleアカウントでアプリパスワードを設定してcurlで普段使いのメールに送信します。

#on_connect.sh
#!/bin/sh
curl    --url 'smtps://smtp.gmail.com:465' --mail-rcpt  '<To@gmail.com>' --mail-from  '<from@gmail.com>' --user '<from@gmail.com>:<password>' --ssl-reqd -T - <<EOS
To: <To@gmail.com>
From: <from@gmail.com>
Subject: VPN connected

VPN is used.
EOS
#on_disconnect.sh
#!/bin/sh
curl    --url 'smtps://smtp.gmail.com:465' --mail-rcpt  '<To@gmail.com>' --mail-from  '<from@gmail.com>' --user '<from@gmail.com>:<password>' --ssl-reqd -T - <<EOS
To: <To@gmail.com>
From: <from@gmail.com>
Subject: VPN disconnected

VPN is disconnected
EOS

これでRT2600acでもWireGuardが動作するようになりました。WireGuardは鍵に依存しすぎているので二段階認証とか設定した方がいいのですが、RT2600acではこれ以上は無理そうです。多分単独で動作させることは想定していないと思うので、素直にOpenVPN使うかOpenWrtに移行した方が楽だと思います。しばらく動作テストしたら、廃止してOpenVPNに戻ります。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です