Consul ACL (Consul 0.4.X)の使い方。
詳細まで正しいかは不明だが、解ったことから書いていく。
概要
- トークン単位で値に対する操作を制限する機能。
- 各トークンはID、名前、どこの値にどのようにアクセスできるかのルールセットを持つ。
- 値へのアクセス時にトークンIDを指定し(指定しなければデフォルトトークン)、認可を行う。
公式ドキュメントはこちら。
対象API
ドキュメントに、
As of Consul 0.4, it is only possible to specify policies for the KV store.
とあるとおり、0.4時点ではKVSだけアクセス制限できる。
※ACLのAPIにはmaster tokenでないとアクセスできない。
Configで書けること
ACLはHTTP APIで登録する。Configファイルにルールを書いておいて、起動したらそれが有効になる、という使い方ではない模様。
Configファイルに書く値
- acl_datacenter
これで指定したdatacenterがACLでアクセス制限する対象になる。この指定があるとACLが有効になるが、指定せずにACLのAPIにアクセスするとエラーが発生する。
$ curl http://localhost:8500/v1/acl/list?token=master_token_test
ACL support disabled
- acl_default_policy
allowかdenyを設定する。ACLの持っているruleに対して適用されるもので、allowの場合はblacklist、denyの場合はwhitelist方式での制限を行う。
デフォルトはallow。
- acl_master_token
Typeがmanagementのトークン。これを指定すると、全ての値にwriteレベルでアクセスでき、ACLの操作ができる。
※対してTypeがclientの場合、ACLの操作はできない。
- acl_token
HTTP APIで値にアクセスする際、?token=を指定しない場合はこのトークンが利用される。
デフォルトはanonymous。
※トークンを指定すれば、このデフォルトをリクエスト単位で上書きできる、ということ。
設定ファイルの例
/etc/consul.d/default.json
{
"datacenter": "testdc",
"data_dir": "/var/lib/consul",
"server": true,
"bootstrap": true,
"acl_datacenter": "testdc",
"acl_default_policy": "deny",
"acl_master_token": "master_token_test",
"acl_token": "anonymous"
}
上記の設定で起動した場合にデフォルトで存在するACLを取得してみると、以下のようになる。
$ curl http://localhost:8500/v1/acl/list
Permission denied
$ curl http://localhost:8500/v1/acl/list?token=anonymous
Permission denied
$ curl http://localhost:8500/v1/acl/list?token=master_token_error
ACL not found
$ curl http://localhost:8500/v1/acl/list?token=master_token_test | jq .
[
{
"Rules": "",
"Type": "client",
"Name": "Anonymous Token",
"ID": "anonymous",
"ModifyIndex": 2,
"CreateIndex": 2
},
{
"Rules": "",
"Type": "management",
"Name": "Master Token",
"ID": "master_token_test",
"ModifyIndex": 3,
"CreateIndex": 3
}
]
ACLの登録
ACLはHTTP APIで登録する。
Rulesは HashiCorp Configuration Language (HCL) を文字列で記述する。 jsonで書くとエラーが発生する。 jsonで書いても大丈夫。
policyにはdeny、read、writeを書くことができ、writeはreadの権限も包含する(readwriteは存在せず、書き込めるが読み込めない、ということは無い)。
例:
$ cat /tmp/acl.txt
{
"Name": "test_token",
"Type": "client",
"Rules": "
key \"testread/\" {
policy = \"read\"
}
key \"testwrite/\" {
policy = \"write\"
}
"
}
$ curl -X PUT http://localhost:8500/v1/acl/create?token=master_token_test -d @/tmp/acl.txt
{"ID":"53b6df4c-7f77-c816-750d-b5eabf5ca582"}
$ curl http://localhost:8500/v1/acl/list?token=master_token_test | jq .
[
{
"Rules": " key \"testread/\" { policy = \"read\" } key \"testwrite/\" { policy = \"write\" } ",
"Type": "client",
"Name": "test_token",
"ID": "53b6df4c-7f77-c816-750d-b5eabf5ca582",
"ModifyIndex": 26,
"CreateIndex": 26
},
{
"Rules": "",
"Type": "client",
"Name": "Anonymous Token",
"ID": "anonymous",
"ModifyIndex": 2,
"CreateIndex": 2
},
{
"Rules": "",
"Type": "management",
"Name": "Master Token",
"ID": "master_token_test",
"ModifyIndex": 3,
"CreateIndex": 3
}
]
createすると、作成されたACLのIDが戻ってくるので、それを使う形になる。
ACLを使ってアクセス
readableに対するアクセス
- readableに対してGETを行い、値が無い場合
$ # no value
$ curl -i http://localhost:8500/v1/kv/testread/value?token=master_token_test
HTTP/1.1 404 Not Found
X-Consul-Index: 44
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:46:22 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
$ curl -i http://localhost:8500/v1/kv/testread/value?token=anonymous
HTTP/1.1 404 Not Found
X-Consul-Index: 44
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:46:30 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
$ curl -i http://localhost:8500/v1/kv/testread/value
HTTP/1.1 404 Not Found
X-Consul-Index: 44
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:46:40 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
$ curl -i http://localhost:8500/v1/kv/testread/value?token=53b6df4c-7f77-c816-750d-b5eabf5ca582
HTTP/1.1 404 Not Found
X-Consul-Index: 44
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:46:54 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
- readableに対してPUTを行う場合
$ curl -i -X PUT http://localhost:8500/v1/kv/testread/value -d 200
HTTP/1.1 403 Forbidden
Date: Thu, 06 Nov 2014 06:47:41 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
Permission denied
$ curl -i -X PUT http://localhost:8500/v1/kv/testread/value?token=anonymous -d 200
HTTP/1.1 403 Forbidden
Date: Thu, 06 Nov 2014 06:47:48 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
Permission denied
$ curl -i -X PUT http://localhost:8500/v1/kv/testread/value?token=53b6df4c-7f77-c816-750d-b5eabf5ca582 -d 200
HTTP/1.1 403 Forbidden
Date: Thu, 06 Nov 2014 06:47:59 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
Permission denied
$ curl -i -X PUT http://localhost:8500/v1/kv/testread/value?token=master_token_test -d 200
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 06 Nov 2014 06:48:31 GMT
Content-Length: 4
true
- readableに対してGETを行い、値がある場合
$ curl -i http://localhost:8500/v1/kv/testread/value?token=master_token_test
HTTP/1.1 200 OK
Content-Type: application/json
X-Consul-Index: 48
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:48:58 GMT
Content-Length: 99
[{"CreateIndex":48,"ModifyIndex":48,"LockIndex":0,"Key":"testread/value","Flags":0,"Value":"MjAw"}]
$ curl -i http://localhost:8500/v1/kv/testread/value?token=anonymous
HTTP/1.1 404 Not Found
X-Consul-Index: 48
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:49:26 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
$ curl -i http://localhost:8500/v1/kv/testread/value
HTTP/1.1 404 Not Found
X-Consul-Index: 48
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:49:34 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
$ curl -i http://localhost:8500/v1/kv/testread/value?token=53b6df4c-7f77-c816-750d-b5eabf5ca582
HTTP/1.1 200 OK
Content-Type: application/json
X-Consul-Index: 48
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 06:49:42 GMT
Content-Length: 99
[{"CreateIndex":48,"ModifyIndex":48,"LockIndex":0,"Key":"testread/value","Flags":0,"Value":"MjAw"}]
master_tokenは何でもできる。
また、readが許可されていないトークンでGETを行った場合、403ではなく404になるらしいので、エラーハンドリングは注意が必要かもしれない。
writableに対するアクセス
master_tokenは上でも似たような実験を行ったため、省略。
- writableに対して書き込み
$ curl -i -X PUT http://localhost:8500/v1/kv/testwrite/value -d 300
HTTP/1.1 403 Forbidden
Date: Thu, 06 Nov 2014 07:00:47 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
Permission denied
$ curl -i -X PUT http://localhost:8500/v1/kv/testwrite/value?token=anonymous -d 300
HTTP/1.1 403 Forbidden
Date: Thu, 06 Nov 2014 07:00:54 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
Permission denied
$ curl -i -X PUT http://localhost:8500/v1/kv/testwrite/value?token=53b6df4c-7f77-c816-750d-b5eabf5ca582 -d 300
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 06 Nov 2014 07:01:03 GMT
Content-Length: 4
true
- writableに対して読み込み
$ curl -i http://localhost:8500/v1/kv/testwrite/value
HTTP/1.1 404 Not Found
X-Consul-Index: 62
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 07:03:09 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
$ curl -i http://localhost:8500/v1/kv/testwrite/value?token=anonymous
HTTP/1.1 404 Not Found
X-Consul-Index: 62
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 07:03:17 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
$ curl -i http://localhost:8500/v1/kv/testwrite/value?token=53b6df4c-7f77-c816-750d-b5eabf5ca582
HTTP/1.1 200 OK
Content-Type: application/json
X-Consul-Index: 62
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 07:03:23 GMT
Content-Length: 100
[{"CreateIndex":62,"ModifyIndex":62,"LockIndex":0,"Key":"testwrite/value","Flags":0,"Value":"MzAw"}]
KVS以外のAPIへのアクセス
先頭で記載したとおり、0.4時点ではKVS以外へのアクセスは制限されない。
$ curl -i -X GET http://localhost:8500/v1/catalog/services?token=53b6df4c-7f77-c816-750d-b5eabf5ca582
HTTP/1.1 200 OK
Content-Type: application/json
X-Consul-Index: 6
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 07:19:51 GMT
Content-Length: 13
{"consul":[]}
$ curl -i -X PUT http://localhost:8500/v1/event/fire/testevent?token=53b6df4c-7f77-c816-750d-b5eabf5ca582
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 06 Nov 2014 07:20:16 GMT
Content-Length: 151
{"ID":"1aa92970-7d1a-d4ae-2334-d8fca7002a26","Name":"testevent","Payload":null,"NodeFilter":"","ServiceFilter":"","TagFilter":"","Versi
on":1,"LTime":0}
$ curl -i -X GET http://localhost:8500/v1/event/list?token=53b6df4c-7f77-c816-750d-b5eabf5ca582
HTTP/1.1 200 OK
Content-Type: application/json
X-Consul-Index: 4151740018762382984
Date: Thu, 06 Nov 2014 07:20:24 GMT
Content-Length: 153
[{"ID":"1aa92970-7d1a-d4ae-2334-d8fca7002a26","Name":"testevent","Payload":null,"NodeFilter":"","ServiceFilter":"","TagFilter":"","Vers
ion":1,"LTime":2}]
ただし、ACLのAPIだけは、masterのみアクセス可能。
$ curl -i -X GET http://localhost:8500/v1/acl/list
HTTP/1.1 403 Forbidden
X-Consul-Index: 0
X-Consul-Knownleader: false
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 07:12:18 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
Permission denied
$ curl -i -X GET http://localhost:8500/v1/acl/list?token=53b6df4c-7f77-c816-750d-b5eabf5ca582
HTTP/1.1 403 Forbidden
X-Consul-Index: 0
X-Consul-Knownleader: false
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 07:12:22 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
Permission denied
$ curl -i -X GET http://localhost:8500/v1/acl/list?token=master_token_test
HTTP/1.1 200 OK
Content-Type: application/json
X-Consul-Index: 26
X-Consul-Knownleader: true
X-Consul-Lastcontact: 0
Date: Thu, 06 Nov 2014 07:12:26 GMT
Content-Length: 449
[{"CreateIndex":26,"ModifyIndex":26,"ID":"53b6df4c-7f77-c816-750d-b5eabf5ca582","Name":"test_token","Type":"client","Rules":" key \"testread/\" { policy = \"read\" } key \"testwrite/\" { policy = \"write\" } "},{"CreateIndex":2,"ModifyIndex":2,"ID":"anonymous","Name":"Anonymous Token","Type":"client","Rules":""},{"CreateIndex":3,"ModifyIndex":3,"ID":"master_token_test","Name":"Master Token","Type":"management","Rules":""}]
細かい動き
クラスタに参加するノードの設定に書かれたacl_master_tokenがすべて異なる場合、どれが有効になるのか?
acl_master_tokenは、Leaderのものが有効になる。
厳密には、Leaderのacl_master_tokenをIDとしたACLがクラスタに追加される。
Leaderがダウンしたらどうなるのか?
- 新しく選出されたLeaderの設定に書かれていたacl_master_tokenがダウンした元Leaderのacl_master_tokenと異なる場合
新しく選出されたLeaderの設定に書かれていたacl_master_tokenをIDとしたACLが追加される。
ダウンした元Leaderのacl_master_tokenのACLはそのまま残るので、master tokenが複数ある状態になる。
(ダウン時も、ダウンした元Leaderの復旧時も、特に削除しにいったりはしない)
- 新しく選出されたLeaderの設定に書かれていたacl_master_tokenがダウンした元Leaderのacl_master_tokenと同一の場合
ACLは増えもせず減りもせず。引き続き同じacl_master_tokenのACLを利用できる。
今のところ、ここまで。