標籤:macOS

讓 macOS 根據不同 Domain 選擇 DNS 伺服器

TL;DR

/etc/resolver/ 下建立與目標 Domain 同名的設定檔。

背景

由於在 macOS 設定 DNS resolver 的時候,OS 選擇查詢的 resolver 可能不是按照順序的,所以只要有任何一個 resolver 搶先回 NXDOMAIN 就會讓你的查詢找不到 Domain,這對一些有非公開網域或偷偷自定網域的人們來說十分的困擾。

一般來說,我們可能會想讓 Domain Server 分成內外兩組,把只有自己查得到的 domain 讓內部的 DNS 來解析,剩餘的其他網域則是讓外部的 DNS 來解析就好了,省去讓內部 DNS 做 recursive 的麻煩。

但由於 macOS DNS resolving 的設計(或我們說 BSD 的設計),使得直接將全域 DNS 設定為內部優先,外部次之的這件事情變得無法進行,此時可以利用手動設定的方式來達到將不同 domain 指定到不同 DNS 的目的。

resolver(5)

man 5 resolver[1] 裡面有提到一件事情,/etc/resolv.conf 記錄的是「主要的」DNS resolver 設定[2],但其實所有的設定除了會從 /etc/resolv.conf 讀取以外,還會從 /etc/resolver/ 目錄下面讀取[3]

對於這個目錄裡面的設定檔除了與 resolv.conf 格式一致以外,還有一個限制是檔名必須與要搜尋的 domain 同名,在關於 domain 的這個設定值的說明是這樣說的:

Domain

Domain name associated with this resolver configuration. This option is normally not required by the Mac OS X DNS search system when the resolver configuration is read from a file in the /etc/resolver directory.
In that case the file name is used as the domain name.

However, "domain" must be provided when there are multiple resolver clients for the same domain name, since multiple files may not exist having the same name.

也就是說,我們可以在 /etc/resolver/ 下建立於我們想要額外設定個別網域的 resolver 設定檔。方法也很簡單,我們接下來就來嘗試做點實驗:

/etc/resolver/

假定我們想要個別設定的網域為 davy.home(當然,這個網域實際上並不存在於這個世界上),那麼我們只需要建立 /etc/resolver/davy.home[4] 這個檔案即可,內容如下:

domain davy.home
search davy.home
nameserver 10.10.10.10
nameserver 10.10.10.100

在這個範例中,我設定了兩組 nameserver 分別是 10.10.10.1010.10.10.100,大家可以根據自己的實際情況設定。

雖然文件中寫說 domain 在 macOS 中不是必須的,但我還是按照填寫的規則寫上了 davy.home,各位讀者可以自行嘗試將此欄位移除是否仍然會生效。

接下來我們會清除 macOS 的 DNS cache 並檢查看看設定後的結果是否與我們想像的一樣:

# killall -HUP mDNSResponder
$ scutil --dns
DNS configuration
...
resolver #8
  domain   : davy.home
  search domain[0] : davy.home
  nameserver[0] : 10.10.10.10
  nameserver[1] : 10.10.10.100
  flags    : Request A records
  reach    : 0x00000002 (Reachable)
...
$

透過 scutil[5] 可以確認系統的 resolver 已經特化出給 davy.home 使用的設定了,此時大家就可以在不改變 Global resolver 的情況下來穩定查詢 davy.home 了。

結語

透過獨立 DNS resolver 的設定讓我們可以指定不同網域的查詢方式,也可以讓我們的 Domain Server 減去了不必要的 recursion 的負擔,對於我們這種可能會有一些實驗用的私人網域來說,比起其他系統會循序查詢 resolver 的方式真的是方便了不少。


  1. 此處指的是 macOS 的 resolver(5) 條目,可以直接透過 man 5 resolver 查閱內容,或參考 FreeBSD 收錄的檔案內容 ↩︎

  2. 原文為 "Note that the /etc/resolv.conf file, which contains configuration for the default (or "primary") DNS resolver client, is maintained automatically by Mac OS X and should not be edited manually. Changes to the DNS configuration should be made by using the Network Preferences panel." ↩︎

  3. 原文為 "These are at present located by the system in the /etc/resolv.conf file and in the files found in the /etc/resolver directory." ↩︎

  4. 預設情況下 /etc/resolver/ 這個目錄並不存在,大家自行新增即可 ↩︎

  5. 關於 scutil 的使用說明可以從 man 8 scutilFreeBSD 的收錄的說明查閱 ↩︎

macOS 神奇的 terminal compat mode

TL;DR macOS 內建了一個 compat mode 可以使得部分 CLI 工具以 SUSv3 相容模式執行,試試看 COMMAND_MODE=[legacy|unix2003]

某天強者我朋友在社群軟體上面問了為什麼 macOS 的 less 明明在 manpage 中寫有 s (save log) 可以用,但實際按下去卻是往下捲一行……

我第一個想法當然是先看程式碼再說,看看是不是有什麼隱藏設定,但我一開始看了原版的 less 發現沒什麼可疑的地方,於是我轉念一想,那這說不定是 Apple 做的什麼神奇功能? 好在 less 是 GPL 授權,Apple 按照規定應該要放出原始碼來給大家。

於是我就找到了一個版本比較舊的(less-34)原始碼(我不知道為什麼現在 macOS 內建的已經到 100 多版了,但放出來的只有 v34……),而且還真的被我找到了一個可疑的地方[1]

/* 
 * Command table for UNIX 2003 compatibility: added first before builtin
 * so that these commands override the normal LESS commands
 */

static unsigned char UNIX03cmdtable[] =
{
	's',0,				A_F_LINE
};

Apple 在這個地方新增了一個按鍵映射,並且寫了一段註解告訴我們,這個按鍵覆蓋預設的 less 指令。 為何 Apple 在這裡多了一個奇怪的行為呢? 原來是 Apple 從 Mac OS X 10.3 就開始在內建的 CLI tools 裡面新增了這個 Single UNIX Specification v3 (SUSv3)[2] 相容模式,而某些終端機軟體預設就會把這個模式開啓(例如 iTerm2)而導致 less 等工具的行為與我們想像中的不太一樣。

在翻閱了文件[3]以及網路資料[4]之後發現,只要透過環境變數就可以控制這個相容模式的開啓與否,於是我們可以透過設定 COMMAND_MODE 這個環境變數來開關相容模式:

$ # 使用 SUSv3 相容模式
$ COMMAND_MODE=unix2003 less file.txt
$ # 不使用相容模式
$ COMMAND_MODE=legacy less file.txt

於是關掉相容模式之後就可以愉快的在 less 中使用 s 來 save log file 了!


  1. decode.c ↩︎

  2. 詳見維基百科〈Single UNIX Specification〉條目 ↩︎

  3. man 5 compat ↩︎

  4. https://www.oradba.ch/2011/04/mac-os-x-terminal-compatibility-settings/ ↩︎

使用 M1 Mac 的一些小技巧

前幾天拿到了新的 M1 MacBook Air,想說來試試看一些平常使用的工具以及 iOS App,結果發現很多常用的 iOS App 都還沒有開放 macOS 下載使用…… 但很多常用的工具都已經有原生支援的版本了,在使用上也沒有什麼太多的問題,但有些小技巧想分享給大家,希望大家在這個轉換之路(?)上可以再更順暢一些~

Rosetta 2 相關

這裡會列出一些與 Intel 相容模式(Rosetta 2)有關的一些小技巧:

原生支援?

在活動監視器(Activity Monitor)可以看到一個叫做「建築」[1]的欄位,在這裡我們可以看到有分成 Apple 以及 Intel,分別對應到原生 Apple silicon 以及 Rosetta 2[2] 相容的 Intel 模式。

如果你發現正在執行的應用程式是 Intel 表示他正在 Rosetta 2 底下運作。

手動安裝 Rosetta 2

個人覺得這個技巧至關重要,因為我曾經在 11.0.1 升級到 11.1 之後,Rosetta 就消失了,導致我還在用 Intel 架構的輸入法(對!連輸入法都可以靠 Rosetta 相容)死亡,也不知道為什麼 macOS 沒有主動提醒我要再安裝一次 Rosetta,直到我要安裝別的 Intel 應用程式時才又提醒,當時我才恍然大悟,原來我的 Rosetta 在升級的時候消失了……

方法也很簡單,只要打開終端機然後用軟體升級的方式安裝就可以了:

$ /usr/sbin/softwareupdate --install-rosetta --agree-to-license

雖然加了 --agree-to-license 之後就不會提醒你再看一次 SLA,但第一次使用前還是看一下吧XD

強制使用 Rosetta 來執行 Universal 應用程式

蘋果在轉換到 Apple silicon 的時候提出了一個以前[3]也用過的解決方案,對於這樣的通用架構應用程式,我們可以透過「取得資訊」來將通用架構應用程式設定為使用 Rosetta 開啓。

透過 CLI 強制使用 Rosetta

前面講的是可以將 .app 應用程式以 Rosetta 方式開啓的技巧,但如果在 CLI 下的話該如何操作呢?

方法也很簡單,我們可以透過 arch 這個指令來幫我們跳入 x86_64 的世界:

$ which uname
/usr/bin/uname
$ file /usr/bin/uname
/usr/bin/uname: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/usr/bin/uname (for architecture x86_64):	Mach-O 64-bit executable x86_64
/usr/bin/uname (for architecture arm64e):	Mach-O 64-bit executable arm64e
$ uname -u
arm64
$ arch -arch x86_64 uname -u
x86_64
$

同理,我們也可以靠 arch 指令來讓我們打開一個 x86_64 的 Shell,在裡面運作的程式就都會以 x86_64 優先了:

$ arch -arch x86_64 bash
[x86_64] $ uname -m
x86_64
$ arch -arch arm64e bash
[arm64e] $ uname -m
arm64
[arm64e] $

雖然這個範例中表示可以在 x86_64 模式下再跳回 arm64e 模式[4],但其實這件事情在 11.0.1 的時候還是不行的,會噴出不認識的 architecture 錯誤,但在 11.1 之後就可以了!

透過 sysctl 取得目前是不是在 Rosetta 中運作

這個也是一個實用的小技巧,我們可以透過 sysctl 瞭解目前的環境是不是在 Rosetta 中:

$ sysctl -n sysctl.proc_translated
0
$ arch -arch x86_64 sysctl -n sysctl.proc_translated
1
$

如果 sysctl.proc_translated 的值是 1 表示目前正在 Rosetta 中執行。

iOS App 相關

這裡會列出一些與 iOS App 相關的小技巧:

安裝 ipa 後無法執行

有時候會遇到安裝 .ipa 之後,macOS 卻阻止你執行的情況,這時候有可能是因為這個 App 被上了 com.apple.quarantine 這個屬性,我們只要用下面的方式就可以解開束縛:

$ sudo xattr -dr com.apple.quarantine <Installed.app>

通常這個屬性代表的是不安全的應用程式,例如從瀏覽器下載回來的應用程式就會自動打上這個屬性,只是不知道為何從 ipa 安裝的 App 並不會提醒你是否允許執行,而是直接拒絕你執行。

CLI 相關

這裡列出一些跟 CLI 有關的小技巧,如果是有關如何在 CLI 執行 Rosetta 的話可以往上面的節次看:

安裝 Homebrew

這好像不管是不是 M1 Mac 都會用到,但由於 Homebrew 散佈的執行檔都是單一架構的,所以這邊會另外展示如何安裝 Intel 架構的 Homebrew(以取得 Intel 架構的執行檔)。

Apple Silicon 版本的 Homebrew,按照原先的安裝方式就可以了:

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

再來是 Intel 架構的 Homebrew,雖然 Homebrew 是用 Ruby 撰寫而成,其實可以直接用前面提到的 arch 來切換至 Intel 架構執行,但我是建議把 Intel 架構用的 Homebrew 跟 Apple 架構用的 Homebrew 分開,避免 Homebrew 搞不清楚跨架構 library 之間的相依性。

所以我們需要用替代安裝[5]的方式把 Homebrew 安裝到指定的位置(這裡舉例是 /opt/homebrew.x86_64):

$ mkdir -p /opt/homebrew.x86_64
$ curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C /opt/homebrew.x86_64

接下來我會在我的 Terminal 中設定兩個 Profile,一個是 Apple 架構的 Shell、另一個是 Intel 架構的(一樣用 arch 關進 Intel 模式)Shell,並且在 shellrc 中加入環境偵測來判斷使用哪個 Homebrew:

# ~/.zprofile

if [ "$(sysctl -n sysctl.proc_translated)" = "1" ]; then
	eval $(/opt/homebrew.x86_64/bin/brew shellenv)
else
	eval $(/opt/homebrew/bin/brew shellenv)
fi
# ~/.config/fish/config.fish

set is_rosetta (sysctl -n sysctl.proc_translated)
if [ $is_rosetta = '1' ]
    set homebrew /opt/homebrew.x86_64/bin
else
    set homebrew /opt/homebrew/bin
end

set default_path /usr/local/bin /usr/bin /usr/sbin /bin /sbin
set -gx PATH $homebrew $default_path

雜記

最後小小抱怨一下 macOS 11.0.1 根本就是還沒有完成的版本……
Bug 有夠多[6],而且還沒辦法做雙向轉換(人家 Windows <> WSL 就可以互相呼叫),好險 11.1 之後這些缺點都有補回來。
再用一陣子也許還會再增加一些技巧,不過基於我還在複習 macOS(畢竟有好多年沒用 Mac 了……),所以可能需要再一下下才會更新XD


  1. 個人猜測是 Architecture 的翻譯啦…… 近年來蘋果很多名詞都會刻意找一個中文翻譯…… 以前不會的啊 QQ ↩︎

  2. Rosetta 2 可讓配備 Apple 晶片的 Mac 使用專為配備 Intel 處理器的 Mac 所開發的 App。 詳見 Apple 支援頁面 ↩︎

  3. 以前從 PowerPC 架構轉換至 Intel 架構時蘋果就有提出相同的解決方案,使得不同架構的應用程式可以同時存在於一個執行檔中。詳見維基百科的〈Universal Binary〉條目↩︎

  4. man arch 中有列出幾個可以使用的模式,包含:i386, x86_64, x86_64h(Haswell), arm64, arm64e(Apple Silicon)。 ↩︎

  5. 參考 Homebrew 安裝說明 ↩︎

  6. 有興趣看我踩到什麼雷的可以看看我發的廢推(ry ↩︎