右往左往ブログ

日々よりみち

consulのacl管理とconsul execの関係

consulのacl

consulは、aclによって実行制限を行うことができます。
ここでは、multi datacenterでacl管理を行うための、設定と効果を見てみます。

きっかけは、consulの設定の中にacl_datacenterというものがあるのですが、ネット上のいろいろなページを見てもいまいち意味がわからなかったためです。 datacenteracl_datacenter を同じ値にしていたり、では別の値だとどうなるのか?など…。

consul接続イメージ

今回構築しているのは以下のようなイメージです。(前回と同じイメージです)

  • dc1,dc2の2つのデータセンタがあり、それぞれにconsulサーバ1台、consulクライアント1台が属する
  • dc1とdc2間は接続されている

f:id:unchemist:20150620190747p:plain

構築

前回の設定に加えて、aclの設定を入れてみます。

dc1 server

consul agent -config-file=/etc/consul/conf.d/dc1_server.json

/etc/consul/conf.d/dc1_server.json

{
  "datacenter": "dc1",
  "data_dir": "/tmp/consul",
  "server": true,
  "bootstrap_expect": 1
  "acl_datacenter": "dc1",
  "acl_default_policy": "deny",
  "acl_master_token": "master_token",
  "acl_token": "anonymous"
}

acl_default_policydenyとしています。

dc1 client

consul agent -config-file=/etc/consul/conf.d/dc1_client.json -join=dc1server

/etc/consul/conf.d/dc1_client.json

{
  "datacenter": "dc1",
  "data_dir": "/tmp/consul",
  "server": false,
  "acl_token": "anonymous"
}

dc2 server

consul agent -config-file=/etc/consul/conf.d/dc2_server.json -join-wan=dc1server

/etc/consul/conf.d/dc2_server.json

{
  "datacenter": "dc2",
  "data_dir": "/tmp/consul",
  "server": true,
  "bootstrap_expect": 1,
  "acl_datacenter": "dc1",
  "acl_default_policy": "allow",
  "acl_master_token": "master_token",
  "acl_token": "anonymous"
}

ここでは、datacenterdc2ですが、acl_datacenterdc1としています。
また、acl_default_policyallowとしています。

dc2 client

consul agent -config-file=/etc/consul/conf.d/dc2_client.json -join=dc2server

/etc/consul/conf.d/dc2_client.json

{
  "datacenter": "dc2",
  "data_dir": "/tmp/consul",
  "server": false,
  "acl_token": "z"
}

他のノードと異なり、acl_token名を異なるものにしています。
(anonymousではなくsome_tokenとしています)

各設定の意味

  • acl_datacenter:
    自dcが従いたいdc名を指定します。ここで設定したdcでのacl設定の影響を受けます。
    他dcを指定した場合、自dcでのacl設定は意味を成さなくなり、指定したdcのacl設定に従います。
  • acl_default_policy:
    acl設定が、blacklistモードかwhitelistモードかを指定します。
    allowと指定した場合、基本的にあらゆる操作を許容し、別途拒否したい操作を追加で設定していきます。
    denyと指定した場合、逆に基本的にあらゆる操作を拒否し、別途許容したい操作を追加で設定していきます。
  • acl_master_token:
    key/valueに対して、read/write(=あらゆる操作)が可能になるtoken名を指定します。
  • acl_token:
    tokenを指定しない場合に、デフォルトで(暗黙的に)指定するtokenを記載します。

consul exec実行によるaclの影響確認

consul exec コマンドも、暗黙的にk/vで_rexecprefixを利用しているため、aclの影響を受けます。
上記設定の場合、どのような振る舞いになるのか確認してみます。

dc1 server

[root@dc1server /]# consul exec hostname
Failed to create job file: Unexpected response code: 403 (Permission denied)

dc2 server

[root@dc2server /]# consul exec hostname
Failed to create job file: Unexpected response code: 403 (Permission denied)

ポイントは、dc2 serverではconfigで"acl_default_policy": "allow"と設定しているにも関わらず、実行に失敗している点です。
これは、acl_datacenterdc1であるために、dc1側の設定である"acl_default_policy": "deny"が有効になっているためと思われます。

aclの設定

ここから、acl設定を追加することでどのような挙動になるのかを確認してみます。
最終的には、consul execが実行でき、結果が返ってくるようにします。

aclの登録

まずはpolicy設定を作成します。 公式(https://www.consul.io/docs/internals/acl.html)によるとjson形式で記述できるとあるのですが、少しだけ注意が必要です。 下記を見るとわかる通り、jsonjsonなのですが、jsonの中にjsonを書くという、特殊な記載になります…。
(全体がjson形式で、さらにRulesの中が更にjsonになっていることがわかると思います)

/tmp/acl.json

{
  "ID": "anonymous", 
  "Type": "client", 
  "Rules": "{
    \"key\": {
      \"_rexec/\": { 
        \"policy\":\"write\"
      }
    }
  }" 
}

このjsonファイルを登録します。登録時はacl_master_tokenで指定したtokenを指定します。

[root@dc1server /]# curl -X PUT http://localhost:8500/v1/acl/update?token=master_token -d @/tmp/acl.json
{"ID":"anonymous"}

効果の確認

先ほど、anonymous tokenの_rexec prefixに対してwrite権を付与したので、consul execが実行できるはずです。
まずはdc1 serverから。

[root@dc1server /]# consul exec hostname
    dc1server: dc1server
    dc1server:
==> dc1server: finished with exit code 0
    dc1client: dc1client
    dc1client:
==> dc1client: finished with exit code 0
2 / 2 node(s) completed / acknowledged
[root@dc1server /]# consul exec -datacenter="dc2" hostname
    dc2server: dc2server
    dc2server:
==> dc2server: finished with exit code 0
1 / 1 node(s) completed / acknowledged
[root@dc1server /]#

実行できています。ただし、dc2 clientの結果は返ってきていません。
次にdc2 serverから。

[root@dc2server /]# consul exec hostname
    dc2server: dc2server
    dc2server:
==> dc2server: finished with exit code 0
1 / 1 node(s) completed / acknowledged
[root@dc2server /]# consul exec -datacenter="dc1" hostname
    dc1server: dc1server
    dc1server:
==> dc1server: finished with exit code 0
    dc1client: dc1client
    dc1client:
==> dc1client: finished with exit code 0
2 / 2 node(s) completed / acknowledged

上記の通り、実行できました。ただし、こちらもdc2 clientだけ結果が返って来ません。
これは、dc2 clientのacl_tokenanonymousではなく別の名前(ここではsome_token)にしているためです。
先ほどaclの設定でwrite権を設定した対象は、anonymousでした。

dc2 clientでも以下のログが出力されています。

2015/06/20 15:09:24 [ERR] agent: failed to get remote exec job: rpc error: ACL not found

aclの登録(2回目)

そこで、some_tokenに対しても_rexecに対してwrite権を付与してみます。

/tmp/acl_2.json

{
  "ID": "some_token",
  "Type": "client",
  "Rules": "{
    \"key\": {
      \"_rexec/\": {
        \"policy\":\"write\"
      }
    }
  }" 
}

登録します。

[root@dc1server /]# curl -X PUT http://localhost:8500/v1/acl/create?token=master_token -d @/tmp/acl_2.json
{"ID":"some_token"}

最初と異なり、今回のhttp apiupdateではなくcreateを指定します。これは、anonymousはデフォルトで既存の設定が付与されているためです。

aclcreateは、通常はIDを指定せずに実行することで、ランダム文字列となるtokenが返ってきます。
通常、httpのapiを利用するときには、ここで返却されるIDを利用してtokenに指定することで権限を利用して操作を行いますが、
今回はacl_tokenで既にtoken名を指定してしまっているので、決め打ちでaclを設定しています。

効果の確認(2回目)

これで、dc2 clientでもconsul execが実行できているはずです。

[root@dc1server /]# consul exec -datacenter="dc2" hostname
    dc2server: dc2server
    dc2server:
==> dc2server: finished with exit code 0
    dc2client: dc2client
    dc2client:
==> dc2client: finished with exit code 0
2 / 2 node(s) completed / acknowledged

無事に実行できました。

これで、aclの効果と設定方法が少しだけ分かった気がします。