Carpe Diem

備忘録

ConsulのACLでアクセス制御

概要

ConsulにはACL(Access Control List)といって、AWSのIAMに似たアクセスコントロールの仕組みがあります。
今回はそれの設定方法を説明します。

環境

  • Consul v1.4.0

Consul ACL

ACL System

大きく分けて

というコンポーネントがあります。

ACL Policies

各リソースの対する権限のポリシーです。以下の権限設定ができます。

設定 説明
read read権限
write read/write権限
deny readもwriteも不可

各リソースはこちらに載っています。

ACL Rules and Scope

これを次のACL Tokensと紐づけて利用します。

またGlobal Managementという管理者権限のPolicyが最初は用意されています。
IDは00000000-0000-0000-0000-000000000001です。
この管理者権限から細かいリソース毎のPolicyを作成していきます。

1.4.0より前はToken毎にPolicyを設定していく形でPolicyとTokenの分離ができておらず、Policyの再利用ができませんでした。
今は再利用ができるため、あらかじめPolicyを定義しておいてTokenにアタッチしていくことが可能です。

またTokenにアタッチしたPolicyを更新した場合、Tokenを再作成したりしなくてもPolicyの更新は反映されます

ACL Tokens

ACL Tokenには大きく4種類あって

種類 役割
Master Token Global Managementに紐づく管理者権限のToken。
configで明示的にacl.tokens.masterを設定しなかった場合、Bootstrap時に自動生成される
Anonymous Token CLIなどでリクエストする際、Tokenを明示的に指定しなかった場合に使われるToken。
00000000-0000-0000-0000-000000000002というアクセスID。
後からPolicyを追加可能
Agent token Consul agentの内部オペレーション(ノード情報の更新など)で利用されるToken。
configではacl.tokens.agentにセットする
通常のToken Anonymous Tokenでは権限が足りない時に自前で生成し、適切なPolicyをアタッチする。
configではacl.tokens.defaultにセットする

このうちMater TokenAnonymous TokenACLを有効にしたタイミングで自動で用意されます。
Agent Token通常のTokenは自前で生成します。

ハンズオン

こちらのリポジトリを利用してください。

github.com

中身の説明

サーバ

{
  "primary_datacenter": "dc1",
  "acl": {
    "enabled": true,
    "default_policy": "deny",
    "down_policy": "extend-cache",
    "tokens": {
      "master": "b1gs33cr3t"
    }
  }
}

acl.enabled: trueにすることでACLが有効化されます。

"default_policy": "deny"のようにデフォルトで全リソースのアクセス禁止にしてwhitelist形式で扱っていきます。
tokens.masterは書かずにあとでbootstrapingで生成しても大丈夫です。

注意としてMaster tokenが漏れるとどんな操作も可能なので、必ず漏洩しないようにしてください

今回は明記した方が説明しやすいのでこうしています。

クライアント

{
  "primary_datacenter": "dc1",
  "acl": {
    "enabled": true,
    "down_policy": "extend-cache"
  }
}

サーバからdefault_policytokens.masterを抜いただけです。

起動

起動してみます。

$ docker-compose up

デフォルトPolicyをdenyにしたので、agent間の通信もできず以下のエラーがでます。

consul-agent-1_1           |     2018/12/06 18:54:35 [ERR] consul: "Coordinate.Update" RPC failed to server 172.21.0.3:8300: rpc error making call: rpc error making call: Permission denied
consul-agent-1_1           |     2018/12/06 18:54:35 [WARN] agent: Coordinate update blocked by ACLs
consul-agent-2_1           |     2018/12/06 18:54:38 [ERR] consul: "Coordinate.Update" RPC failed to server 172.21.0.6:8300: rpc error making call: rpc error making call: Permission denied
consul-agent-2_1           |     2018/12/06 18:54:38 [WARN] agent: Coordinate update blocked by ACLs

下の「Agent間通信の許可」にてこのエラーを解消します。

Bootstraping

※この操作はMaster Tokenをconfigで設定していない場合で必要です。設定してあればスキップしてください

あるサーバのコンテナに入って操作します。

$ docker exec -it acl_consul-server-bootstrap_1 /bin/ash

bootstrapします。これは1度きりです。

/ # consul acl bootstrap
AccessorID:   ad22630f-a10d-fad0-3231-26dbb3595d7d
SecretID:     4e98466f-59d3-84e6-5277-4c6fb03bcad0
Description:  Bootstrap Token (Global Management)
Local:        false
Create Time:  2018-12-06 19:04:58.1592181 +0000 UTC
Policies:
   00000000-0000-0000-0000-000000000001 - global-management

このSecretID:4e98466f-59d3-84e6-5277-4c6fb03bcad0はMaster Tokenです。
管理者権限のPolicyである00000000-0000-0000-0000-000000000001 - global-managementと紐付いていますね。
この管理者権限のTokenがないと自前のPolicyの定義ができないため、configで明示していない場合は必ず保存しておいてください。

Agent間通信の許可

簡単のため、環境変数CONSUL_HTTP_TOKENにMaster Tokenを設定しておきます。

/ # export CONSUL_HTTP_TOKEN=b1gs33cr3t

※各CLI操作で-token=b1gs33cr3tをつけるのと同じです

Agent Policyの作成

まずはConsul Agent間で疎通できないとクラスタ構築がされないので、agent間の内部オペレーションを許可するPolicyを定義します。
内部オペレーションで必要な権限は以下なので、agent.hclと定義して、

node_prefix "" {
   policy = "write"
}

service_prefix "" {
   policy = "read"
}

Policyを作成します。

/ # consul acl policy create \
 -name "agent-policy" \
 -description "Agent Token Policy" \
 -rules @/consul/policies/agent.hcl
ID:           6807479a-eb6e-d50c-db10-6a83afc08cba
Name:         agent-policy
Description:  Agent Token Policy
Datacenters:  
Rules:
node_prefix "" {
   policy = "write"
}

service_prefix "" {
   policy = "read"
}

Agent Tokenの生成

↑で作ったPolicyをアタッチしてTokenを生成します。

/ # consul acl token create \
 -description "Agent Token" \
 -policy-name "agent-policy"
AccessorID:   24fe3b36-30d9-f01f-1eed-16d0e8c2dbc2
SecretID:     271970dd-8498-8361-599b-fc90fb65257b
Description:  Agent Token
Local:        false
Create Time:  2018-12-06 19:25:20.0921606 +0000 UTC
Policies:
   6807479a-eb6e-d50c-db10-6a83afc08cba - agent-policy

271970dd-8498-8361-599b-fc90fb65257bがagent tokenです。

Agent Tokenの反映

agent tokenを用意できたので、各configにtokens.agentをつける修正してコンテナを再起動させます。

サーバ

{
  "primary_datacenter": "dc1",
  "acl": {
    "enabled": true,
    "default_policy": "deny",
    "down_policy": "extend-cache",
    "tokens": {
      "master": "b1gs33cr3t",
      "agent": "271970dd-8498-8361-599b-fc90fb65257b"
    }
  }
}

クライアント

{
  "primary_datacenter": "dc1",
  "acl": {
    "enabled": true,
    "down_policy": "extend-cache",
    "tokens": {
      "agent": "271970dd-8498-8361-599b-fc90fb65257b"
    }
  }
}

再起動

$ docker-compose restart

しばらくすると内部の通信ができるようになり、クラスタが構築されます。

/ # consul members
Node          Address          Status  Type    Build  Protocol  DC   Segment
1d561da489ed  172.25.0.4:8301  alive   server  1.4.0  2         dc1  <all>
dccab8780f64  172.25.0.6:8301  alive   server  1.4.0  2         dc1  <all>
e03b703d4345  172.25.0.7:8301  alive   server  1.4.0  2         dc1  <all>
091e7c809fb1  172.25.0.3:8301  alive   client  1.4.0  2         dc1  <default>
e98cc8322113  172.25.0.5:8301  alive   client  1.4.0  2         dc1  <default>
fa3d5ff86fdc  172.25.0.2:8301  alive   client  1.4.0  2         dc1  <default>

Anonymous Tokenに最低限の権限を付与

これまではMaster Tokenを使っていたので何でも操作できましたが、Anonymous Tokenの権限ではconsul infoconsul membersDNS lookupすらできません

agentのコンテナでは

/ # consul members
/ # 

何も表示されませんし、

$ dig @127.0.0.1 -p 8600 consul.service.consul

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 consul.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 18336
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;consul.service.consul.     IN  A

;; AUTHORITY SECTION:
consul.         0   IN  SOA ns.consul. hostmaster.consul. 1544125928 3600 600 86400 0

;; Query time: 11 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Fri Dec 07 04:52:08 JST 2018
;; MSG SIZE  rcvd: 100

このように;; WARNING: recursion requested but not availableと怒られます。

なので、Anonymous Tokenに権限を追加します。

Policy作成

以下を参考にconsul membersDNS lookupができる権限を定義します。

https://www.consul.io/docs/guides/acl.html#configure-the-anonymous-token-optional-

agent_prefix "" {
    policy = "read"
}

node_prefix "" {
   policy = "read"
}

service "consul" {
   policy = "read"
}

Policyを作成します。

/ # consul acl policy create \
 -name "anonymous-policy" \
 -description "Anonymous Token Policy" \
 -rules @/consul/policies/anonymous.hcl
ID:           b4898e08-b19f-ea0d-20d0-f9cc2bcfaabd
Name:         anonymous-policy
Description:  Anonymous Token Policy
Datacenters:  
Rules:
agent_prefix "" {
    policy = "read"
}

node_prefix "" {
   policy = "read"
}

service "consul" {
   policy = "read"
}

Anonymous TokenへPolicyのアタッチ

/ # consul acl token update \
 -id 00000000-0000-0000-0000-000000000002 \
 -policy-name "anonymous-policy" \
 -description "Anonymous Token"
Token updated successfully.
AccessorID:   00000000-0000-0000-0000-000000000002
SecretID:     anonymous
Description:  Anonymous Token
Local:        false
Create Time:  2018-12-06 19:23:40.4129681 +0000 UTC
Policies:
   b4898e08-b19f-ea0d-20d0-f9cc2bcfaabd - anonymous-policy

動作確認

agentにログインしてみて

$ docker exec -it acl_consul-agent-1_1 /bin/ash

consul membersができることを確認できますし、

/ # consul members
Node          Address          Status  Type    Build  Protocol  DC   Segment
1d561da489ed  172.25.0.4:8301  alive   server  1.4.0  2         dc1  <all>
dccab8780f64  172.25.0.6:8301  alive   server  1.4.0  2         dc1  <all>
e03b703d4345  172.25.0.7:8301  alive   server  1.4.0  2         dc1  <all>
091e7c809fb1  172.25.0.3:8301  alive   client  1.4.0  2         dc1  <default>
e98cc8322113  172.25.0.5:8301  alive   client  1.4.0  2         dc1  <default>
fa3d5ff86fdc  172.25.0.2:8301  alive   client  1.4.0  2         dc1  <default>

ローカルにポートマップしたagent経由でDNS lookupもできます。

$ dig @127.0.0.1 -p 8600 consul.service.consul

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 consul.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45022
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 4
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;consul.service.consul.       IN  A

;; ANSWER SECTION:
consul.service.consul.    0   IN  A   172.25.0.6
consul.service.consul.    0   IN  A   172.25.0.7
consul.service.consul.    0   IN  A   172.25.0.4

;; ADDITIONAL SECTION:
consul.service.consul.    0   IN  TXT "consul-network-segment="
consul.service.consul.    0   IN  TXT "consul-network-segment="
consul.service.consul.    0   IN  TXT "consul-network-segment="

;; Query time: 5 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Fri Dec 07 05:06:12 JST 2018
;; MSG SIZE  rcvd: 206

UIリソースの許可

最後の例で、Web UIの機能が使えるためのACL Policyを設定します。

現状だと権限がないので、一部のページで以下のようにエラーがでます。

f:id:quoll00:20181207054815p:plain
権限が足りず失敗

Policyの作成

https://www.consul.io/docs/guides/acl.html#create-tokens-for-ui-use-optional-

を参考に以下の権限を付与します。
xxx_prefixという形式の場合そのリソース全てが対象になります。

key_prefix "" {
   policy = "write"
}

node_prefix "" {
   policy = "read"
}

service_prefix "" {
   policy = "read"
}

Policyを作成します。

/ # consul acl policy create \
 -name "ui-policy" \
 -description "UI Token Policy" \
 -rules @/consul/policies/ui.hcl
ID:           83a7b2cc-6996-fb0e-0d23-11ea1d86b3a7
Name:         ui-policy
Description:  UI Token Policy
Datacenters:  
Rules:
key_prefix "" {
   policy = "write"
}

node_prefix "" {
   policy = "read"
}

service_prefix "" {
   policy = "read"
}

Tokenの生成

/ # consul acl token create \
 -description "UI Token" \
 -policy-name "ui-policy"
AccessorID:   2ea57bc4-5793-eb44-d050-323d107a5993
SecretID:     0689d5fa-f4f0-cf60-eed5-1e1346c7c346
Description:  UI Token
Local:        false
Create Time:  2018-12-06 20:15:36.7292146 +0000 UTC
Policies:
   83a7b2cc-6996-fb0e-0d23-11ea1d86b3a7 - ui-policy

0689d5fa-f4f0-cf60-eed5-1e1346c7c346がtokenです。

UIのACLページ設定

f:id:quoll00:20181207052417p:plain

こちらでトークンをセットします。
ちなみにここでセットしたトークンはlocalStorageに保存されるだけなので、他のブラウザではトークンは反映されません
全体的に反映したい場合は以下のようにconsul configのdefault tokenとして設定してください。

{
  "primary_datacenter": "dc1",
  "acl": {
    "enabled": true,
    "down_policy": "extend-cache",
    "tokens": {
      "agent": "271970dd-8498-8361-599b-fc90fb65257b",
      "default": "0689d5fa-f4f0-cf60-eed5-1e1346c7c346"
    }
  }
}

動作確認

先程のようにkey/valueを登録してみると

f:id:quoll00:20181207054641p:plain
key/valueの登録

ちゃんと成功しました。

f:id:quoll00:20181207054721p:plain
登録成功

まとめ

ConsulはVaultなど秘密情報のバックエンドに利用したり、設定ファイルのリアルタイム更新などに使うため適切にアクセス制御する必要があります。
AWSで完全にPrivate Subnetに閉じている場合はまだいいですが、Public SubnetにConsul agentがいたり、すべてPublicに置かれるGCPで使う場合は必ずACLでアクセス制御を行ってください。

ソース