現代の開発者にとって Linux 仮想マシンは必要不可欠なものだが、IP アドレスをどうするかはいつも悩ましい問題だ。例えば下記のような状況は多いはず。
- 社内は ActiveDirectory DNS / DHCP 環境。
- 勝手に固定 IP アドレスを使えない。
- 社内ネットワークから仮想マシンを参照させたい。
このような場合は DHCP を使うことになると思うが、暫く起動しないと IP アドレスが変わって接続する際に一手間かかって面倒くさい。個人的にはこの様な場合、Samba (の nmbd)を使っている。サービスを起動しておくだけで Windows PC からホスト名(NetBIOS 名)でアクセスできるので、とても便利。
しかし最近、ActiveDirectory のドメインに参加せずとも、DHCP で DNS レコードを登録できることを知った。少なくとも ActiveDirectory の DNS / DHCP にはそういう機能があり、そして私の社内ではそれが有効になっている。そうすると後は DHCP クライアント側の話。RHEL では、次の設定でそれができる。(詳細は man dhclient.conf)
RHEL6: /etc/dhcp/dhclient.conf:
send fqdn.fqdn "myhost.example.local.";
send fqdn.server-update off;
RHEL5 ではファイルの場所が /etc/dhclient.conf となる。RHEL4 以前は知らない。
ネット上には、同じく dhclient.conf の host-name や ifcfg-* の DHCP_HOSTNAME でできるよ、みたいな情報も見つかる。しかし BIND ではそれで動くのかも知れない(未検証)が、社内の ActiveDirectory では動かなかった。
なお、dhclient.conf はデバイス(インターフェイス)毎に設定が可能で、その場合はグローバルな dhclient.conf は無視されるので注意。この辺はソースコードを見た方が理解が早い。
RHEL5.11: /etc/sysconfig/network-scripts/ifup-eth:
# allow users to use generic '/etc/dhclient.conf' (as documented in manpage!)
# if per-device file doesn't exist or is empty
if [ -s /etc/dhclient-${DEVICE}.conf ]; then
DHCLIENTCONF="-cf /etc/dhclient-${DEVICE}.conf";
else
DHCLIENTCONF='';
fi;
RHEL6.7: /etc/sysconfig/network-scripts/network-functions:
generate_config_file_name () {
local ver=$1
if [ -s /etc/dhcp/dhclient$ver-${DEVICE}.conf ]; then
DHCLIENTCONF="-cf /etc/dhcp/dhclient$ver-${DEVICE}.conf";
elif [ -s /etc/dhclient$ver-${DEVICE}.conf ]; then
DHCLIENTCONF="-cf /etc/dhclient$ver-${DEVICE}.conf";
else
DHCLIENTCONF='';
fi
}
例えば私の環境では、RHEL5.11 のインストール直後に /etc/dhclient-eth0.conf が存在したので、この場合は /etc/dhclient.conf に設定を書いても無視される。
以上で、私の環境でも DNS にホスト名を登録できた。ただ個人的には、Windows PC からアクセスするだけなら Samba nmbd の方を勧めたい。わざわざ DNS レコードをゴニョゴニョせずに済むなら、それに越したことはない。
と、これで話が終われば幸せだった・・・。というか、こうして本エントリを書く必要すらなかった。残念ながら、以上は RHEL6 までの話。RHEL7 では話が変わる。:-(
周知の通り、RHEL7 では NetworkManager が幅を利かせていて、NetworkManager はシェルスクリプトで書かれた従来処理は使用せずに、自前で処理を行う。(ただし従来処理自体は存在していて、それらは NetworkManager が無効の時に使用される)
まず、dhclient.conf が未設定の状態でどう動くのか確認してみる。
[rhel7]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.2 (Maipo)
[rhel7]# rpm -q NetworkManager
NetworkManager-1.0.6-27.el7.x86_64
[rhel7]# cat /etc/dhcp/dhclient.conf
cat: /etc/dhcp/dhclient.conf: No such file or directory
このときデバイスの dhclient 設定は、デバイス起動時に下記の場所に生成される。
[rhel7]# cat /var/lib/NetworkManager/dhclient-enp0s17.conf
# NetworkManager で作成されています
send host-name "rhel7"; # added by NetworkManager
option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
option ms-classless-static-routes code 249 = array of unsigned integer 8;
option wpad code 252 = string;
also request rfc3442-classless-static-routes;
also request ms-classless-static-routes;
also request static-routes;
also request wpad;
also request ntp-servers;
dhclient.conf を作ってデバイスを再起動すると、上記が再生成されるが・・・、
[rhel7]# cat /etc/dhcp/dhclient.conf
send fqdn.fqdn "rhel7.example.local.";
send fqdn.server-update off;
[rhel7]# systemctl restart network
[rhel7]# cat /var/lib/NetworkManager/dhclient-enp0s17.conf
# NetworkManager で作成されています
# /etc/dhcp/dhclient.conf からマージされています
send fqdn.server-update off;
send host-name "rhel7"; # added by NetworkManager
option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
option ms-classless-static-routes code 249 = array of unsigned integer 8;
option wpad code 252 = string;
also request rfc3442-classless-static-routes;
also request ms-classless-static-routes;
also request static-routes;
also request wpad;
also request ntp-servers;
fqdn.fqdn がマージされていない!
ソースコードを確認してみる。
#define HOSTNAME4_TAG "send host-name"
#define HOSTNAME4_FORMAT HOSTNAME4_TAG " \"%s\"; # added by NetworkManager"
#define FQDN_TAG "send fqdn.fqdn"
#define FQDN_FORMAT FQDN_TAG " \"%s\"; # added by NetworkManager"
/* ...snip... */
char *
nm_dhcp_dhclient_create_config (const char *interface,
gboolean is_ip6,
GBytes *client_id,
const char *anycast_addr,
const char *hostname,
const char *orig_path,
const char *orig_contents,
GBytes **out_new_client_id)
{
/* ...snip... */
/* Override config file hostname and use one from the connection */
if (hostname) {
if (strncmp (p, HOSTNAME4_TAG, strlen (HOSTNAME4_TAG)) == 0)
continue;
if (strncmp (p, FQDN_TAG, strlen (FQDN_TAG)) == 0)
continue;
}
何故か fqdn.fqdn をスキップしている。かといってfqdn.fqdn が全て抹殺される訳でもなくて、
static void
add_hostname4 (GString *str, const char *format, const char *hostname)
{
char *plain_hostname, *dot;
if (hostname) {
plain_hostname = g_strdup (hostname);
dot = strchr (plain_hostname, '.');
/* get rid of the domain */
if (dot)
*dot = '\0';
g_string_append_printf (str, format, plain_hostname);
g_free (plain_hostname);
}
}
/* ...snip... */
static void
add_hostname6 (GString *str, const char *hostname)
{
/* dhclient only supports the fqdn.fqdn for DHCPv6 and requires a fully-
* qualified name for this option, so we must require one here too.
*/
if (hostname && strchr (hostname, '.')) {
g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
g_string_append (str,
"send fqdn.encoded on;\n"
"send fqdn.server-update on;\n");
g_string_append_c (str, '\n');
}
}
何故か IPv6 の方では fqdn.fqdn を設定している。IPv6 の動作は未確認だが、これだと IPv4 で fqdn.fqdn を送る術がないように見える。
もう NetworkManager ってほんと ks だわ、と思っていたら、
- Red Hat Bugzilla – Bug 1255507 - NetworkManager no longer provides complete FQDN (DHCP_HOSTNAME) to dhclient
- cgit.freedesktop.org/NetworkManager - merge: branch 'bg/dhcp-send-fqdn-rh1255507'
どうやら NetworkManager の設定項目に ipv4.dhcp-fqdn が増える模様。果たしてこれは RHEL7.3 に入ってくれるのだろうか。
2018-09-25 追記
あれから 2 年半、現段階での私の結論を記しておく。
まず、前述の修正は RHEL7.3 に入った。しかし私の環境では、全く状況は変わらなかった。これ以上はパケットキャプチャとか更なるソースコード解読とかが必要になるし、面倒いのでずっと放置していた。
その後、今度は dhclient のフックを使う方法を知った。
これは dhclient.conf に FQDN を記述するよりスマートな方法に思える。FQDN は hostname コマンドから引っ張ってこれるので、ハードコードが不要になるからだ。
しかし、これも動かなかった。orz
NetworkManager は dhclient のフックを呼ばないらしい。あれもダメ、これもダメ、一体どうしろというのか。NetworkManager 的には、「dispatcher を使え」とのこと。
/etc/NetworkManager/dispatcher.d/90-nsupdate:
#!/bin/bash
[ -n "$DHCP4_IP_ADDRESS" ] && [ "$HOSTNAME" != "${HOSTNAME/./}" ] \
|| exit 0
nsupdate <<EOF
update delete $HOSTNAME a
update add $HOSTNAME 3600 a $DHCP4_IP_ADDRESS
send
EOF
/etc/NetworkManager/dispatcher.d/pre-down.d/90-nsupdate:
#!/bin/bash
[ "$HOSTNAME" != "${HOSTNAME/./}" ] \
|| exit 0
nsupdate <<EOF
update delete $HOSTNAME a
send
EOF
後者の pre-down 処理は、対称性の観点(up 時に登録した名前を down 時に削除する)から作ったものの、実はあってもなくても同じ。何故なら、pre-down 処理はサーバーシャットダウン時に呼ばれない から。でもネットワーク再起動(systemctl restart network)では呼ばれる。これが仕様なのかバグなのかは分からないが、ほんとこれを作った奴はタヒんでいいよ。
あと、dispatcher 内で利用できる環境変数(例: DHCP4_IP_ADDRESS)は action (例: up, pre-down)毎に異なるのだが、それらは man を見てもいまいち明確でない。なので dispatcher を自作する場合は、まず set コマンドの出力をロギングする dispatcher を作り、利用可能な環境変数を確認してみることをお勧めする。
結論: やっぱり NetworkManager は ks。