理系学生日記

おまえはいつまで学生気分なのか

Consul Connect

Consul Connect の資料を一通り読み終わったので、簡単に Connect についてまとめてみます。 ちなみに、全貌がいちばん分かりやすかったのは、HashiDays 2018 の Keynote でした。

概要

Consul Connect とは何かというと、

  • サービスとサービス間の通信を mTLS で暗号化するとともに「認証」を行い、
  • 論理的なサービス名を用いた通信の「認可」制御を行える機能

なんじゃないかと思います。

つまりはサービス間通信の認証・認可を、(基本的には)アプリの変更なしに Sidecar Pattern で実現しようという機能です。 そして、その大きな目的は、Service Segmentation にあります。

これまでのサブシステム間通信は、VLAN だったり Firewall だったり、SDN だったりでその安全性を担保してきていましたが、 Micro Services になるとその通信制御は複雑になってきてしまいます。50 個の Service があって、5 個の DB があったら、制御ルールは何個必要でしょうか。 そして、そこにまた新しいサービスが加わったら…。

そこで、もう一つレイヤを上げて、「サービス」という概念で制御したら良いんじゃねって機能です。 大きいのは、必要な機能が consul にすべて内包されているということでしょうか。

Consul Connect を構成する 3 つのコンポーネント

Service Access Graph

Service Access Graph は、結局のところ、サービス間の通信を allow/deny するというルールである、Intention を意味しているようです。 Intention では、「どのサービス」から「どのサービス」へ allow/deny する、という形で定義が行われ、これをもとにして通信制御がおこなわれます。

[client1]# consul intention create -deny web socat
Created: web => socat (deny)

ホスト or コンテナが数百、数千あるようなシステムにおいて、IP or TCP/UDP ベースの通信制御を行おうとすると、当然ながらそのルール数は組合せ爆発することになりますし、 動的にホスト/コンテナが追加・削除されるようなケースを考えると、どのホスト/コンテナへの通信を allow し、あるいは deny するか、一々ルールを記述していくのもあまりに煩雑ですし、ミスが起こる下地になります。

Consul Connect では、このあたりを「サービス」という Identity をベースにした通信の認可ルールを記述することにより解決しようとしています。 ホスト or コンテナの数に起因するルール数・管理の問題を、「サービス」という概念に載せかえることでスケールさせようとしています。

で、この「サービス」という Identity を証明するのが、Certificate になりあす。

Certificate Authority

Connect では、"サービス"の識別と暗号化のために TLS を使っています。このため、もちろん証明書が必要になります。

証明書の運用っていうのは基本的に面倒くさいのですが、今回の Consul Connect では、built-in CA という形で CA の機能が 搭載されています。そして、Consul に証明書運用に関する API が加わりました。

この API を利用することで、ルート証明書や中間証明書の作成や、証明書への署名、サービス影響のない証明書のローテーションが可能になっています。 ここで行われた処理は、consul 本来の機能を用いて各 Agent に伝搬していきます。

また、API 経由で呼び出される CA の機能は CA Provider という概念で抽象化され、変更可能になっています。 (built-in CA がデフォルトで提供される CA Provider。CA Provide の 1 つが Vault)

Pluggable Data Plane

ルールの設定を司る Control Plane に対し、実際の通信制御を行うのが Data Plane になります。 Consul Connect における Data Plane は Sidecar Proxy にあたりますが、実はこの Data Plane はプラガブルになっており、 必ずしも Sidecar Proxy に依拠する必要はありません。

といっても、Sidecar Proxy は結構良いかんじにつくられていて、これはサービス間の通信をすべて Proxy 経由にし、その Proxy 上で通信制御や暗号化を 行います。この意味で、アプリケーションは基本的に変更が不要です。

また、アプリケーションへの通信は基本的に Proxy 経由になるので、アプリケーションは localhost のみを bind しておけば良くなりますし、 どこかのサービスと通信したいっていうときも、localhost に bind している Proxy 宛に通信すれば良くなります。 あとは (正しく Intention が設定されていれば) Proxy にお任せ、という感じの世界感が構築できます。

また、この Proxy を手作りし、サービス単位で組込むこともできます。

{
  "service": {
    "name": "web",
    "port": 8080,
    "connect": {
      "proxy": {
        "exec_mode": "daemon",
        "command":   ["/usr/bin/my-proxy", "-flag-example"]
      }
    }
  }
}

Proxy が何をしているかっていうと、 Inbound traffic を処理する Proxy は、以下のような処理をしたあとで、担当する Service に Proxy するようで、

  1. 証明書を作成し提示 (/v1/agent/connect/ca/leaf/ API)
  2. クライアント証明書を確認する (/v1/agent/connect/ca/roots API)
  3. 通信を認可 (/v1/agent/connect/authorize API)

逆に、Outbound traffic を処理する Proxy は、

  1. Proxy 先の Service を Discovery
  2. クライアント証明書を提示 (/v1/agent/connect/ca/leaf API)
  3. サーバ証明書を確認 (/v1/agent/connect/ca/roots API)

をするって感じみたいです。

ほかにも Consul Connect の API をアプリケーションに組み込んでしまう Native Integration と呼ばれる方法もあります。

が、Consul Connect は、各 Agent がローカルに Intention や証明書といったデータのキャッシュを持ち、 Consul Connect の API はマイクロ秒レベルでレスポンスを返すため、よほどのことがない限り、 Sidecar Proxy を使えば良さそうです。

まとめ

Micro Services の文脈に関わらず、サブシステムが分かれてサブシステム間通信が発生することは数多あり、 そしてサブシステム間のネットワークはガバガバになっているってことも多いと思います。

それで問題ない場合もありますが、問題がある場合もあって、そういうときに Consul Connect を挟みこむと、 あんまり難しいことを考えることなく幸せになれるかもなぁと思いました。