日立グループ OSS Advent Calendar 2021 13 日目の記事です。
Kuma 紹介記事の第二弾です。
(株)日立製作所 研究開発グループ サービスコンピューティング研究部の井出です。
Kuma の Transparent Proxy (透過プロキシ) 構成にて Kuma の DNS と既存の DNS 設定を共存させる方法を紹介します。なお、本記事は Kuma v1.3.1 が前提です。
要約
Kuma の Transparent Proxy 設定時、--skip-resolv-conf
と--redirect-dns
オプションを追加すると、.mesh
のドメインのみ Kuma で名前解決し、それ以外は既存 DNS に問い合わせてくれます。
既存 DNS 設定に手を入れなくて良くなるので便利です。
sudo kumactl install transparent-proxy \
--kuma-dp-user kuma-dp \
--kuma-cp-ip $CONTROL_PLANE_IP \
--skip-resolv-conf \
--redirect-dns
Kuma とは
Kuma は OSS のサービスメッシュツールです。
2021 年現在、CNCF の Sandbox Project に所属しています。
Kuma ではトラフィック制御や、セキュリティ管理、監視をアプリから透過的に実施可能です。L7 レベルのアクセス制御など、細かい部分で未対応な機能もありますが、サービスメッシュの主要機能は大体実装されている印象です。Kuma と他ツールとの機能比較は Service Mesh Comparison - INNOQ が詳しいです。
私的に、Kuma の一番の特徴は Kubernetes 以外でも Control Plane を稼働できる Universal Mode だと思います。Kubernetes がなくとも動作するため、VM ベースのシステムにサービスメッシュを導入する場合に便利です。
本記事も VM 上に透過プロキシ(Transparent Proxy)として Kuma を構築する際の Tips になります。
Transparent Proxy 構成とは
Transparent Proxy 構成とは、Data Plane を透過プロキシとして用いる構成です。アプリケーションは Kuma を意識せずに通信できるため、ポートマッピングに比べて Kuma を管理しやすくなります。
Kuma の Transparent Proxy は、iptables
を用いて inbound / outbound のトラフィックを Kuma の Data Plane である Envoy Proxy に転送することで実現しています。Kuma がiptables
をどのように用いているかはこちらの記事を参照下さい。
Transparent Proxy は確かに便利なのですが、公式ドキュメントの手順は、サービスディスカバリのために Kuma 独自の DNS を使う必要があるため、VM 環境で利用すると以下のような問題が発生します。
- Control Plane への UDP 53 ポート割り当て
- 要 sudo 権限
-
systemd-resolved
との競合回避要
- Data Plane の DNS サーバを Kuma 専用に変更
- Kuma 用のドメイン(
.mesh
)以外は名前解決できない - DHCP による DNS サーバのアドレスを配布と相性が悪い
-
systemd-resolved
を用いている場合、自動インストール不可
(kumactl install transparent proxy
は/etc/hosts
を変更するのみ)
- Kuma 用のドメイン(
これら問題はkumactl install transparent proxy
の--redirect-dns
オプションを使うといい感じに回避できます。
Redirect DNS 設定
Redirect DNS は Transparent Proxy 利用時に DNS トラフィック(TCP/UCP 53)を Envoy Proxy に飛ばし、Kuma 用ドメイン(.mesh
)の場合は Envoy Proxy で名前解決し、それ以外のドメインは元々の DNS リゾルバに転送してくれる設定です。公式ドキュメントの DNS に記載されています。
Redirect DNS の挙動を以下に示します(CoreDNS は Kuma が用いる DNS サーバ)。
リダイレクトにより 53 ポートの通信を 15053 ポートに飛ばし、CoreDNS にて名前解決します。
また、Transparent Proxy 構成であるため、inbond / outbound 通信は Envoy Proxy 経由でやり取りされます。
Redirect DNS を使うには、以下のように Transparent Proxy の設定時に--skip-resolv-conf
と--redirect-dns
を付与します。kumactl install transparent-proxy
コマンドはiptables
と/etc/resolv.conf
を書き換えるコマンドです。他オプションの内容は公式ページやkumactl
のヘルプを参照下さい。
sudo kumactl install transparent-proxy \
--kuma-dp-user kuma-dp \
--kuma-cp-ip $CONTROL_PLANE_IP \
--skip-resolv-conf \
--redirect-dns
Redirect DNS が有効化された場合、iptables
にtcp dpt:53 redir ports 15053
が付与されます。
sudo iptables -t nat -n -L | grep 53
# REDIRECT tcp -- 0.0.0.0/0 127.0.0.53 tcp dpt:53 redir ports 15053
Redirect DNS により、外部のドメインと同じように Kuma のドメインを名前解決できるようになります。
nslookup www.hitachi.com
# Server: 127.0.0.53
# Address: 127.0.0.53#53
# Non-authoritative answer:
# www.hitachi.com canonical name = www.hitachi.com.edgekey.net.
# www.hitachi.com.edgekey.net canonical name = www.hitachi.com.edgekey.net.globalredir.akadns.net.
# www.hitachi.com.edgekey.net.globalredir.akadns.net canonical name = e7880.a.akamaiedge.net.
# Name: e7880.a.akamaiedge.net
# Address: 104.71.147.221
nslookup server1.mesh
# Server: 127.0.0.53
# Address: 127.0.0.53#53
# Non-authoritative answer:
# Name: server1.mesh
# Address: 240.0.0.1
# ** server can't find server1.mesh: NXDOMAIN
(参考) Redirect DNS では何がおきているか
参考のため、Redirect DNS の挙動を調査した結果をまとめます。
Redirect DNS はおおよそ以下のような挙動をとります。
ただし、4 は裏付けが取れていないため、間違っている可能性があります。ご注意下さい
-
iptables
設定にて DNS トラフィックを TCP/UDP 15053 ポートの CoreDNS に転送 - CoreDNS が UDP 15054 ポートの Envoy Proxy に転送
- Envoy Proxy にて
.mesh
ドメインを240.0.0.0/2
の IP アドレスに名前解決 - 名前解決できない場合(
.mesh
ドメイン以外)、CoreDNS が本来の DNS リゾルバ経由で名前解決(予想) - CoreDNS が DNS リゾルバを送信元にして、元々の問合せ元に名前解決の結果(A or AAAA レコード)を返却
以降、1、2、3、5 の挙動の確認方法を簡単に示します。
1. iptables
設定にて DNS トラフィックを TCP/UDP 15053 ポートの CoreDNS に転送
iptables
に関して Redirect DNS オプションを付与すると以下設定が追加されます(差分や関連項目のみ表示)。
差分から、53 番ポートへのアクセスを 15053 に転送しようとしていることが分かります。
-DNS_CAPTURE=false
+DNS_CAPTURE=true
REDIRECT_ALL_DNS_TRAFFIC=false
-DNS_SERVERS=[],[]
+DNS_SERVERS=[127.0.0.53],[]
AGENT_DNS_LISTENER_PORT=15053
DNS_UPSTREAM_TARGET_CHAIN=RETURN
# IPv4設定
* nat
--A MESH_OUTPUT -o lo ! -d 127.0.0.1/32 -m owner --uid-owner 1001 -j MESH_IN_REDIRECT
--A MESH_OUTPUT -o lo -m owner ! --uid-owner 1001 -j RETURN
+-A MESH_OUTPUT -o lo ! -d 127.0.0.1/32 -p tcp ! --dport 53 -m owner --uid-owner 1001 -j MESH_IN_REDIRECT
+-A MESH_OUTPUT -o lo -p tcp ! --dport 53 -m owner ! --uid-owner 1001 -j RETURN
--A MESH_OUTPUT -o lo -m owner ! --gid-owner 1001 -j RETURN
+-A MESH_OUTPUT -o lo -p tcp ! --dport 53 -m owner ! --gid-owner 1001 -j RETURN
+-A MESH_OUTPUT -p tcp --dport 53 -d 127.0.0.53/32 -j REDIRECT --to-ports 15053
+-I OUTPUT 1 -p udp --dport 53 -m owner --uid-owner 1001 -j RETURN
+-I OUTPUT 2 -p udp --dport 53 -m owner --gid-owner 1001 -j RETURN
+-I OUTPUT 3 -p udp --dport 53 -d 127.0.0.53/32 -j REDIRECT --to-port 15053
# IPv6設定
* nat
+-I OUTPUT 1 -p udp --dport 53 -m owner --uid-owner 1001 -j RETURN
+-I OUTPUT 2 -p udp --dport 53 -m owner --gid-owner 1001 -j RETURN
そこで、lsof
などで 15053 ポートを調べると、CoreDNS に使用されていることが分かります。
sudo lsof -i:15053
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# coredns 1174 kuma-dp 7u IPv6 27112 0t0 TCP *:15053 (LISTEN)
# coredns 1174 kuma-dp 8u IPv6 27113 0t0 UDP *:15053
2. CoreDNS が UDP 15054 ポートの Envoy Proxy に転送
/tmp/kuma-dp-<ID>/Corefile
の CoreDNS の設定を見ると、15053 ポート宛のトラフィックを 15054 ポートに転送していることが分かります。
.:15053 {
forward . 127.0.0.1:15054
# We want all requests to be sent to the Envoy DNS Filter, unsuccessful responses should be forwarded to the original DNS server.
# For example: requests other than A, AAAA and SRV will return NOTIMP when hitting the envoy filter and should be sent to the original DNS server.
# Codes from: https://github.com/miekg/dns/blob/master/msg.go#L138
alternate NOTIMP,FORMERR,NXDOMAIN,SERVFAIL,REFUSED . /etc/resolv.conf
prometheus localhost:19153
errors
}
.:15055 {
template ANY ANY . {
rcode NXDOMAIN
}
}
Data Plane の起動コマンドであるkuma-dp run
のヘルプを見ると 15054 ポートは Envoy Proxy にてバーチャル IP を名前解決する用途と分かります。また、説明文から CoreDNS は最初に 15054 ポートへアクセスし、その後、実際の DNS リゾルバを試す仕様のようです。
kuma-dp run -h
# Launch Dataplane (Envoy).
# ~ 略 ~
# Flags:
# ~ 略 ~
# --dns-envoy-port uint32 A port that handles Virtual IP resolving by Envoy.
# CoreDNS should be configured that it first tries to
# use this DNS resolver and then the real one (default 15054)
3. Envoy Proxy にて.mesh
ドメインを240.0.0.0/2
の IP アドレスに名前解決
.mesh
ドメインであるserver1.mesh
にアクセスしたときの tcpdump および Envoy Proxy の strace 結果を以下に示します。15054 ポートでアクセスされた Envoy Proxy が CoreDNS に応答を返していることが分かります。
ubuntu@client:~$ sudo tcpdump -i lo not port ssh and not port 5678 and not port 30001 and not arp
# ~ 略 ~
04:21:55.814336 IP localhost.55218 > localhost.15053: UDP, length 30 # Application から CoreDNS へのリクエスト
04:21:55.814525 IP localhost.53117 > localhost.15054: UDP, length 30 # CoreDNS から Envoy Proxy へのリクエスト
04:21:55.814643 IP localhost.15054 > localhost.53117: UDP, length 58 # Envoy Proxy から CoreDNS への応答
04:21:55.814709 IP localhost.domain > localhost.55218: 7928 1/0/0 A 240.0.0.1 (58) # Application への応答
この応答を WireShark などで DNS として読み取るとserver1.mesh
を名前解決した A レコードであることがわかります。
4. 名前解決できない場合(.mesh
ドメイン以外)、CoreDNS が本来の DNS リゾルバ経由で名前解決(予想)
前述のようにkuma-dp
のヘルプメッセージを読む限り、CoreDNS は Envoy Proxy での名前解決に失敗した後は実際の DNS リゾルバでの名前解決を試す仕様と考えられます。しかし、tcpdump や strace で CoreDNS のリクエストの流れを追っても既存の DNS リゾルバへのアクセスは確認できませんでした。
5. CoreDNS が DNS リゾルバを送信元にして、元々の問合せ元に名前解決の結果(A or AAAA レコード)を返却
以下 tcpdump 結果を見ると、Application には 46291 ポート宛で名前解決結果が返されていることが分かります。
04:08:43.784631 IP localhost.46291 > localhost.15053: UDP, length 30 # Application から CoreDNS へのリクエスト
04:08:43.786176 IP localhost.43931 > localhost.15054: UDP, length 30 # CoreDNS から Envoy Proxy へのリクエスト
04:08:43.786837 IP localhost.15054 > localhost.43931: UDP, length 58 # Envoy Proxy から CoreDNS への応答
04:08:43.787368 IP localhost.domain > localhost.46291: 12290 1/0/0 A 240.0.0.1 (58) # Application への応答
strace を用いて CoreDNS のプロセスのシステムコールを見ていると、上と同タイミングで CoreDNS から 46291 ポートへのリクエストが送付されており、localhost.domain
が実は CoreDNS であると分かります。
sudo strace -tt -ff -e trace=network -p 1174 # CoreDNS の PID
# ~ 略 ~
04:08:43.784798 recvmsg(8, {msg_name={sa_family=AF_INET6, sin6_port=htons(46291), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_scope_id=0}, msg_namelen=112->28, msg_iov=[{iov_base="0\2\1\0\0\1\0\0\0\0\0\0\7server1\4mesh\0\0\1\0\1", iov_len=512}], msg_iovlen=1, msg_control=[{cmsg_len=36, cmsg_level=SOL_IPV6, cmsg_type=0x32}], msg_controllen=40, msg_flags=MSG_CTRUNC}, 0) = 30
04:08:43.787281 sendmsg(8, {msg_name={sa_family=AF_INET6, sin6_port=htons(46291), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_scope_id=0}, msg_namelen=28, msg_iov=[{iov_base="0\2\201\200\0\1\0\1\0\0\0\0\7server1\4mesh\0\0\1\0\1\7s"..., iov_len=58}], msg_iovlen=1, msg_control=[{cmsg_len=28, cmsg_level=SOL_IP, cmsg_type=IP_PKTINFO, cmsg_data={ipi_ifindex=0, ipi_spec_dst=inet_addr("127.0.0.1"), ipi_addr=inet_addr("0.0.0.0")}}], msg_controllen=32, msg_flags=0}, 0) = 58 #これ
以上が、Redirect DNS のおおよその挙動となります。
まとめ
本記事では、Kuma の Transparent Proxy にて既存 DNS をそのまま使用できる--redirect-dns
オプションを解説しました。これにより、OS レベルでの設定変更がほぼ不要になるため、既存環境への Kuma 組み込みの敷居を低減可能です。
Kuma はサービスメッシュツールの中でも珍しく Kubernetes に依存しない構成を取れるため、VM で構築された環境への導入に適しています。皆様も一度試してみてはいかがでしょうか。