📻

WSLのresolv.confのリセットを容易にしたい

2021/01/18に公開

※ 大前提として WSL2 の話です。

WSL2 で最近たまに /etc/resolv.conf が壊れることがあり、 IP 解決に失敗することがあります。

一過性のものかわかりませんが、コマンドだけでなんとか解決できないか考え、 shell スクリプトにし .zshrc に alias として登録しました。

環境

  • Ubuntu 20.04 on WSL2
  • Windows build version は 2004、ビルド番号が 21292.1010 (Windows insider program の Dev チャンネルに合わせている)

前提

WSL2 はデフォルトだと resolv.conf を自動的に生成するのですが、独自に DNS 設定を入れている場合、毎回書き換えられて壊れる問題があります。個人の利用ニーズによりますが、これは結構困ります。

そこで毎回 resolv.conf が書き換えられないよう、下記の設定を /etc/wsl.conf に追記しています。

[network]
generateResolvConf = false

しかし今はこの設定がされているのにもかかわらず、たまにファイルごと消えてしまうようです(謎)🥲

WSL 上の shell ファイルから Powershell のコマンドを呼ぶ

WSL で設定する nameserver IP は powershell の ipconfig コマンドにて確認可能です。わざわざ powershell を起動しなくても WSL 上で確認できます。

❯ powershell.exe ipconfig

Windows IP 構成


イーサネット アダプター イーサネット:

   接続固有の DNS サフィックス . . . . .: flets-east.jp
(中略)

イーサネット アダプター vEthernet (WSL):

(中略)
   IPv4 アドレス . . . . . . . . . . . .: 172.19.64.1
(中略)

※ 多分 ipconfig.exe でもいけます。

今欲しいのは イーサネット アダプター vEthernet (WSL)IPv4 アドレス で、これが 172.19.64.1 になっています。これを resolv.conf に埋めたいです。

下記の様に shell ファイルから取得を試みたのですが、 powershell.exe が見つからない、とエラーが出ました。
コマンドライン上で実行した際は動いたので、PATH の解釈の仕方が違うのかもしれません。

NAMESERVER=$(powershell.exe ipconfig | grep "IPv4" | tail -1 | awk '{print $NF}' | awk 'sub(/\r$/,"")')

元となるスクリプトは下記の記事から拝借しました。

https://qiita.com/souyakuchan/items/a484e1dd23639eb63bdb#結論-ホスト側-ip-を指定する

この問題は絶対パス指定により解決しました。 tail -1 にしているのは WSL 側のネットワークアダプターが自分の環境では最後に来ているためです。

# sh ファイル実行時、 powershell が見えなかったので、絶対パス指定
POWERSHELL_COMMAND="/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe"
NAMESERVER=$($POWERSHELL_COMMAND ipconfig | grep "IPv4" | tail -1 | awk '{print $NF}' | awk 'sub(/\r$/,"")')

DNS検索設定を埋め込む

引数として受け取るようにし、下記の 2 ファイルに分けました。下記のように呼び出す想定です。
パスなどが雑ですが、まあ自分で動かす分には問題も起きなかったので、目をつむってください(すみません)。

~/scripts/write_resolv_conf_with_dns.sh

追記

下で説明してますが tmux を使った場合にうまくいかないケースがあるので、下記で実行したほうが無難かもしれないです。

wsl.exe sh -c "~/scripts/write_resolv_conf_with_dns.sh"

write_resolv_conf_with_dns.sh

~/scripts/write_resolv_conf_with_dns.sh に配置してます。

#!/bin/sh

# DNS SERVER をスペース区切りで入れとく。適宜置き換え。
DNS_SERVER="でぃーえぬえすさーばー1 でぃーえぬえすさーばー2 でぃーえぬえすさーばー3"
sudo ~/scripts/write_resolv_conf.sh $DNS_SERVER

write_resolv_conf.sh

~/scripts/write_resolv_conf.sh に配置してます。

#!/bin/sh
TMP_FILE_NAME="tmp_resolve.conf"

if [ -f $TMP_FILE_NAME ]; then
  rm $TMP_FILE_NAME
fi
touch $TMP_FILE_NAME

# sh のバッチファイル実行上から powershell が見えなかったので、絶対パス指定
POWERSHELL_COMMAND="/mnt/c/Windows/System32/WindowsPowerShell/v1.0//powershell.exe"
NAMESERVER=$($POWERSHELL_COMMAND ipconfig | grep "IPv4" | tail -1 | awk '{print $NF}' | awk 'sub(/\r$/,"")')

echo "# created by script, $(date "+%Y/%m/%d %H:%M:%S")" >> $TMP_FILE_NAME
echo "nameserver $NAMESERVER" >> $TMP_FILE_NAME
if [ $# != 0 ]; then
  echo "search $@" >> $TMP_FILE_NAME
fi

cat $TMP_FILE_NAME
mv $TMP_FILE_NAME /etc/resolv.conf

実行結果

埋め込んだものが正しく動きました。DNS サーバーはサンプルなので、実際に入れたものとは異なります。

❯ ~/scripts/write_resolv_conf_with_dns.sh
# created by script, 2021/01/18 07:33:32
nameserver 172.19.64.1
search でぃーえぬえすさーばー1 でぃーえぬえすさーばー2 でぃーえぬえすさーばー3

❯ cat /etc/resolv.conf
# created by script, 2021/01/18 07:33:32
nameserver 172.19.64.1
search でぃーえぬえすさーばー1 でぃーえぬえすさーばー2 でぃーえぬえすさーばー3

.zshrc に登録してコマンドとして呼び出す

これも雑ですが、下記で書きました。 alias 増えてくると別ファイルに分けたほうがいいかもしれないですね。

また、試しているうちに実行時の問題点として tmux を起動しているとスクリプトがうまく動かないことがわかりました。
一旦 wsl.exe を挟んで実行することで問題を回避しています。

もちろん tmux を起動していない状態でも動きます。

# resolv conf
if [ -f ~/scripts/write_resolv_conf_with_dns.sh ]; then
     alias reset_resolv='wsl.exe sh -c "~/scripts/write_resolv_conf_with_dns.sh"'
fi

(ただたまに tmux 上から操作すると失敗している時がある気がするので、よくわからんですね…。)

展望など

これ以上設定が複雑になる場合は、他のプログラミング言語でやろうかなと思います…。

今は nameserver 設定と、DNS サーバーだけなのであまり引数やパラメーター処理を考える必要がないですが、これ以上増えるとつらいなという気持ち。

必要になったら考えよう…。

まとめ

雑ですが、日ごろから悩まされていたものを自動的に解決できるようになったので、思考をそこに向ける時間が減りそうです。いいこと。

自分で自分の使いやすい環境にカスタマイズするのはこれからも続けていきたいですね。

今回記載した方法はあまりイケているとは思えない方法ですが、何かの参考になればと思います。

Discussion