LoginSignup
13
1

More than 1 year has passed since last update.

サービスメッシュ Kuma の Transparent Proxy を既存 DNS と共存させる

Last updated at Posted at 2021-12-13

日立グループ OSS Advent Calendar 2021 13 日目の記事です。

Kuma 紹介記事の第二弾です。

  1. サービスメッシュ Kuma の サンプルを Docker Compose で動かす
  2. 本記事
  3. サービスメッシュ Kuma で iptables と resolv.conf の設定を取得する方法

(株)日立製作所 研究開発グループ サービスコンピューティング研究部の井出です。
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を変更するのみ)

これら問題は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 が有効化された場合、iptablestcp 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 は裏付けが取れていないため、間違っている可能性があります。ご注意下さい

  1. iptables設定にて DNS トラフィックを TCP/UDP 15053 ポートの CoreDNS に転送
  2. CoreDNS が UDP 15054 ポートの Envoy Proxy に転送
  3. Envoy Proxy にて.meshドメインを240.0.0.0/2の IP アドレスに名前解決
  4. 名前解決できない場合(.meshドメイン以外)、CoreDNS が本来の DNS リゾルバ経由で名前解決(予想)
  5. CoreDNS が DNS リゾルバを送信元にして、元々の問合せ元に名前解決の結果(A or AAAA レコード)を返却

以降、1、2、3、5 の挙動の確認方法を簡単に示します。

1. iptables設定にて DNS トラフィックを TCP/UDP 15053 ポートの CoreDNS に転送

iptablesに関して Redirect DNS オプションを付与すると以下設定が追加されます(差分や関連項目のみ表示)。
差分から、53 番ポートへのアクセスを 15053 に転送しようとしていることが分かります。

iptables設定
-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 ポートに転送していることが分かります。

Corefile
.: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 レコードであることがわかります。

image.png

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 で構築された環境への導入に適しています。皆様も一度試してみてはいかがでしょうか。

13
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
1