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にも発行できない。
イメージとしては以下のとおりです。

この後に設定を行っていきますが、「擬似的に」その状態にすることは可能です。(どう「擬似的」なのかは後述します)
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"
}
{
"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_datacenterはdc1となっているため、dc2内のサーバにはmaster tokenの設定はありません。- 逆に言うと、
_somerandomstringを知られてしまい、それを使えば、dc2からであっても実行は可能です。
最後に
必要なのでこういう検証をしたわけですが、そもそもこんな使い方をするところはないような気がします。
あと、結構無理やり設定を活用した気がしてならないので、もっと簡単にできればいいのですが…。