如何将 dnsmasq 与 DHCP 分配的 DNS 服务器一起使用?

如何将 dnsmasq 与 DHCP 分配的 DNS 服务器一起使用?

TL;DR 版本:如何配置 dnsmasq 以回退到 LAN 上的 DHCP 服务器指向的 DNS 服务器,以启用切换无线网络?

在我的开发人员笔记本电脑上,我最近开始使用 dnsmasq,以便我可以捕获所有到 *.dev 的流量并将其重定向到虚拟机(使用 mod_vhost_alias)。

为了实现此功能,我需要配置我的网络设置,以便 dnsmasq(运行在 127.0.0.1)用作主 DNS 服务器,常规 DNS 服务器作为辅助 DNS 服务器 - 当 dnsmasq 无法处理域查找时,会回退到这些 DNS 服务器。此功能运行良好,但回退 DNS 服务器现在不再通过 DHCP 配置。每当我切换无线网络时,我的连接就会中断 - 尤其是在需要通过网页进行身份验证的网络上(否则使用 8.8.8.8 等公共 DNS 服务器也是一个选择)。

我尝试阅读 dnsmasq 文档,但无数选项似乎都不能满足我的需要,或者也许我误解了某些选项的作用。

注意:考虑到 dnsmasq 的服务器特性,这个问题最初发布到 ServerFault。由于 Mac OS X 不是服务器操作系统,它很快就被关闭了。我在那里没有足够的声誉来发起移动,所以违背我的判断,我交叉发布到 SU。

答案1

听起来您正在尝试实现与我刚刚在新 MacBook 上设置的以及之前在我的 Linux 开发机器上操作过的功能完全相同的功能。

如您所知,在网络设置中手动将 127.0.0.1 添加到 DNS 条目非常麻烦,因为在更改网络接口/连接到备用 wifi 接入点时必须重新应用它,并且还会阻止您的机器自动获取通过 DHCP 分配的 DNS 服务器。幸运的是,以下解决方案完全避免了必须弄乱您的网络设置,因此您可以正常使用 DHCP。

首先,如果您之前已手动将 127.0.0.1 和外部 DNS 服务器添加到网络接口,现在是时候删除它们并将其重置为 DHCP 默认值了。

完成后,您现在需要创建文件夹 /etc/resolver。

sudo mkdir /etc/resolver

在此文件夹中,您现在可以添加按域命名的文本文件以进行匹配,并包含用于匹配请求的名称服务器条目。OS X 将自动在此文件夹中查找规则,因此它真的就是这么简单。

因此,对于您的设置(与我的相同),我们要创建一个名为 /etc/resolver/dev 的文本文件(以捕获对 *.dev 的所有请求),其中包含 127.0.0.1(dnsmasq 使用的本地 IP)的标准名称服务器条目。

sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/dev'

现在,所有对 *.dev 域名的 DNS 请求都将传递到 127.0.0.1 的 dnsmasq,并且任何与 *.dev 不匹配的内容都将由您的 DHCP 选择的任何 DNS 服务器正常处理。

答案2

以下是我想到的解决方案:

我在用着咆哮硬件咆哮者触发一个脚本,写出 dnsmasq 配置使用的解析器文件。

也许我理解错了(Growl 的文档情况相当缺乏),但我想法Growl 2.1 能够直接在正确的目录中触发 shell 脚本。但是,我无法让它工作。因此,我改用 Applescript 规则脚本来运行 shell 脚本,如下所示...

在 ~/Library/Application Scripts/com.Growl.GrowlHelperApp/Rules.scpt 中:

using terms from application "Growl"
on evaluate notification with notification
    --Rules go in here
    if notification's note title contains "IP Address" then
        set shell_result to do shell script "/usr/local/sbin/set_nameservers.sh"
    end if
    --Ultimately return what you want Growl to do with the notification
end evaluate notification
end using terms from

然后,我的脚本 /usr/local/sbin/set_nameservers.sh:

#!/bin/bash

# locking variables
LOCKDIR="/tmp/nameservers.lock"
PIDFILE="${LOCKDIR}/nameservers.pid"

# Temporary file for discovered nameservers
TMP_NAMESERVERS_FILE="${LOCKDIR}/nameservers.conf"
# List of public nameservers to add to the list
PUB_NAMESERVERS_FILE="/etc/nameservers_public.conf"
# The nameserver list read in by dnsmasq
SYS_NAMESERVERS_FILE="/etc/nameservers.conf"

# Commands
GROWL="/usr/local/bin/growlnotify"

# Setup a lock in case Growl triggers this script multiple times due to multiple network changes
if mkdir "${LOCKDIR}"; then
    echo "$$" >"${PIDFILE}";
    echo "# DHCP supplied nameserves:" > "${TMP_NAMESERVERS_FILE}";

    # 1) For each interface listed by the networksetup command...
    while IFS=" " read -r -a interfaces;
    do
        ((int_count = ${#interfaces[@]} - 1));
        for ((i = 0; i <= int_count; i++));
        do
        # 2) Try to get a nameserver from DHCP. (Only the first of multiple is returned)
        nameserver=`/usr/sbin/ipconfig getoption ${interfaces[i]} domain_name_server`
        # 3) If a nameserver was returned, add it to our configuration.
        if [ ${nameserver} ]; then
            echo "nameserver" ${nameserver} >> ${TMP_NAMESERVERS_FILE};
        fi
        done
    done <<< `networksetup -listallhardwareports | grep Device | sed 's/Device: //g'`

# 4) If a file of public nameservers exists, add these to our configuration.
if [ -e ${PUB_NAMESERVERS_FILE} ]; then
    cat ${PUB_NAMESERVERS_FILE} >> ${TMP_NAMESERVERS_FILE};
fi
cp ${TMP_NAMESERVERS_FILE} ${SYS_NAMESERVERS_FILE};

# Display a Growl notification showing what our new nameserver config looks like.
${GROWL} -d "us.loranz.steve.set_nameservers" \
         -N "Nameserver Configuration" \
         -m "`cat ${SYS_NAMESERVERS_FILE}`" \
         "System nameservers set to:";

rm -rf "${LOCKDIR}";
else
# Display an informational message if we failed to establish a lock.
${GROWL} -d "us.loranz.steve.set_nameservers" \
         -N "Nameserver Configuration Failed" \
         -m "$0 ($$) failed to run, lock already established by process: " `cat ${PIDFILE}` \
         "Failed to set nameservers:";
exit 1;
fi

exit 0

然后我配置了 HardwareGrowler 来显示网络事件,并配置了 Growl 来为 IP 地址更改、网络链接断开和网络链接接通触发 ScriptAction。

最后,我在网络偏好设置中将我的名称服务器设置为 127.0.0.1,这样我就可以访问 dnsmasq。

然后使用以下选项设置 dnsmasq:

resolv-file=/etc/nameservers.conf
all-servers

第一行将 dnsmasq 指向上述脚本正在使用通过 DHCP 发现的名称服务器和您想要的任何公共服务器填充的文件。

第二行应该使 dnsmasq 能够向它知道的所有名称服务器发送查询并接受第一个响应。Mac OS X 解析器应该消除了对给定解析器文件和 DHCP 域搜索的需求……并且依靠它会让你成为更好的网民,而不是每次都查询列表中的每个服务器。经过更多测试后,我可能会删除该选项,并感谢其他人对此的任何见解。

答案3

如果我理解的场景正确的话——我可能错了,我不完全明白你在做什么,或者你的问题发生在哪里——我认为解决你的问题有两个部分/要素——

  1. 对于 DNSMasq,请使用与您的盒子上的网卡关联的 IP 地址,而不是 127.0.0.1。

  2. DNSMasq 读取 /etc/resolv.conf 以读取它应该在没有权威的地方使用的名称服务器。(如果您愿意,可以使用 --resolv-file (-r) 选项将其更改为读取另一个文件。实际上,您需要指定可以从任何地方查询的递归 DNS 服务器 - 例如您提到的 8.8.8.8 上的 Google。如果有一种“集成”方式让 DNSMasq 自动从 DHCP 服务器获取名称服务器,我会感到非常惊讶 - 虽然如果您的 DHCP 客户端正在重写您的 resolv.conf 文件,这可能会间接发生 - 但对您来说似乎并非如此。

相关内容