右往左往ブログ

日々よりみち

consul : aclを利用して特定のdcからのみconsul execを実行可能なようにアクセス制限をかける

consul exec

consulにはconsul execという便利なコマンドがあります。
これは、consulクラスタに属しているメンバに対して一斉にコマンドを発行できるというものです。 しかも、発行先を選択することができ、データセンタ単位やノード単位だったり、それを正規表現で更に絞ったりと柔軟な発行ができます。

このように、consul execは非常に便利なコマンドである一方、権限によっては何でもできてしまうので、限られた条件でしか発行できないように 制限をかけることも考えたくなります。

一方、consulにはacl機能もあり、tokenを発行してこのような権限管理を行うことができます。

aclを利用した制限のかけ方(範囲)には様々なケースが考えられます。
またconsulにはdatacenterという考え方がありますが、dcを管理エリア単位と捉えて、以下のようなイメージで設定を行ってみます。

  • dc1 : マネジメントdc。このdcからは他のdcも含めてどこにでもexecを発行できる。
  • dc2 : 非マネジメントdc。このdcからはconsul execを発行した場合、dc1にもdc2にも発行できない。

イメージとしては以下のとおりです。

f:id:unchemist:20150621015333p:plain

この後に設定を行っていきますが、「擬似的に」その状態にすることは可能です。(どう「擬似的」なのかは後述します)

consul execの仕組み

https://www.consul.io/docs/commands/exec.html https://www.consul.io/docs/internals/acl.html

公式のページにもありますが、consul execは以下のような仕組みで実行されています。

  • consulのkey/valueストア機能を利用
    • デフォルトで_rexecprefixに対してread/writeを行い、この値をノード間で伝播させてそれぞれのノードでコマンドを発行している
      したがって、_rexecprefixに対して読み書きできないとconsul execは実行できない。
      prefixである_rexecという値はコマンド発行時に変更可能。
  • acl的には、デフォルトではanonymous tokenを利用してkey/valueの読み書きを行う。consul execも同様。
    利用するtokenのデフォルトはanonymousだが、acl_tokenの設定により変更することが可能。

基本的な動きは前回のエントリで紹介したので、ここでは設定だけ。

設定

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"
}

/tmp/acl.json

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

設定

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

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_token": "anonymous"
}

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": "anonymous"
}

結果

consul exec実行時は、実行オプションに-prefix="_somerandomstring"を指定しないとエラーになります。

[root@dc1server /]# consul exec hostname
Failed to create job file: Unexpected response code: 403 (Permission denied)
[root@dc1server /]# consul exec -prefix="_somerandomstring" 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 -prefix="_somerandomstring" -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

仕組み

仕組みは単純で、consul execで指定するk/vのprefixを明示的に指定し、そのprefixに対してのみwrite権限を付与するというものです。
そのprefix自体を秘匿してしまうことで、"結果的に"dc1でしかconsul execを発行できなくなります。

  • _somerandomstringという文字列のみwriteを許可しているので、この文字列を推測不能にしておく必要があります。
  • _somerandomstringという文字列を見るためには、dc1のmaster tokenを利用してaclの中を覗き見るしかありません。
    が、dc2から見るとacl_datacenterdc1となっているため、dc2内のサーバにはmaster tokenの設定はありません。
  • 逆に言うと、_somerandomstringを知られてしまい、それを使えば、dc2からであっても実行は可能です。

最後に

必要なのでこういう検証をしたわけですが、そもそもこんな使い方をするところはないような気がします。
あと、結構無理やり設定を活用した気がしてならないので、もっと簡単にできればいいのですが…。