{"id":24754136,"url":"https://github.com/harobed/consul-acl-client-tutorial","last_synced_at":"2025-03-23T05:13:31.496Z","repository":{"id":137219738,"uuid":"105899622","full_name":"harobed/consul-acl-client-tutorial","owner":"harobed","description":"Example how to configure and use Consul client agent with ACL","archived":false,"fork":false,"pushed_at":"2017-10-05T14:26:22.000Z","size":3,"stargazers_count":26,"open_issues_count":0,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-01-28T11:46:01.012Z","etag":null,"topics":["acl","consul","tutorial"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/harobed.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-05T14:25:54.000Z","updated_at":"2022-05-11T22:18:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"dfd21555-5d17-48bc-b235-2adac7ab1183","html_url":"https://github.com/harobed/consul-acl-client-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harobed%2Fconsul-acl-client-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harobed%2Fconsul-acl-client-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harobed%2Fconsul-acl-client-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harobed%2Fconsul-acl-client-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harobed","download_url":"https://codeload.github.com/harobed/consul-acl-client-tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245056906,"owners_count":20553856,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acl","consul","tutorial"],"created_at":"2025-01-28T11:39:07.277Z","updated_at":"2025-03-23T05:13:31.485Z","avatar_url":"https://github.com/harobed.png","language":null,"readme":"# Consul ACL client tutorial\n\n## Configure master token\n\nFirst, generate master token with `uuidgen`:\n\n```\n$ uuidgen\nFBAF54CC-E03D-4763-9F19-376114D3857B\n```\n\nSet `acl_master_token` field with this value in `config/consul.json` file:\n\n```\n{\n  \"datacenter\": \"scaleway-paris\",\n  \"data_dir\": \"/consul/data/\",\n  \"log_level\": \"INFO\",\n  \"node_name\": \"node1\",\n  \"server\": true,\n  \"bootstrap_expect\": 1,\n  \"client_addr\": \"0.0.0.0\",\n  \"ui\": true,\n  \"acl_datacenter\": \"scaleway-paris\",\n  \"acl_master_token\": \"FBAF54CC-E03D-4763-9F19-376114D3857B\",\n  \"acl_default_policy\": \"deny\",\n  \"acl_down_policy\": \"extend-cache\",\n  \"acl_agent_token\": \"f710a920-bb12-b356-ea1f-80f85f88f80b\"\n}\n```\n\nFor greater comfort with next cli operations, put this token in `master.token` file:\n\n```\n$ echo \"FBAF54CC-E03D-4763-9F19-376114D3857B\" \u003e master.token\n```\n\n\n## Start consul server\n\nNow you are ready to start first *consul server*.\n\nFirst, download all images:\n\n```\n$ docker-compose pull\n```\n\nStart `consul1` server:\n\n```\n$ docker-compose up consul1\nStarting testconsul_consul1_1 ...\nStarting testconsul_consul1_1 ... done\nAttaching to testconsul_consul1_1\nconsul1_1     | ==\u003e WARNING: BootstrapExpect Mode is specified as 1; this is the same as Bootstrap mode.\nconsul1_1     | ==\u003e WARNING: Bootstrap mode enabled! Do not enable unless necessary\nconsul1_1     | ==\u003e Starting Consul agent...\nconsul1_1     | ==\u003e Consul agent running!\nconsul1_1     |            Version: 'v0.9.3'\nconsul1_1     |            Node ID: 'da5b0f53-41a4-6710-5e37-86d40e62c0b6'\nconsul1_1     |          Node name: 'node1'\nconsul1_1     |         Datacenter: 'scaleway-paris' (Segment: '\u003call\u003e')\nconsul1_1     |             Server: true (Bootstrap: true)\nconsul1_1     |        Client Addr: 0.0.0.0 (HTTP: 8500, HTTPS: -1, DNS: 8600)\nconsul1_1     |       Cluster Addr: 192.168.192.2 (LAN: 8301, WAN: 8302)\nconsul1_1     |            Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false\nconsul1_1     |\nconsul1_1     | ==\u003e Log data will now stream in as it occurs:\nconsul1_1     |\nconsul1_1     |     2017/10/05 11:32:11 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:192.168.192.2:8300 Address:192.168.192.2:8300}]\nconsul1_1     |     2017/10/05 11:32:11 [INFO] raft: Node at 192.168.192.2:8300 [Follower] entering Follower state (Leader: \"\")\nconsul1_1     |     2017/10/05 11:32:11 [INFO] serf: EventMemberJoin: node1.scaleway-paris 192.168.192.2\nconsul1_1     |     2017/10/05 11:32:11 [INFO] serf: EventMemberJoin: node1 192.168.192.2\nconsul1_1     |     2017/10/05 11:32:11 [INFO] consul: Handled member-join event for server \"node1.scaleway-paris\" in area \"wan\"\nconsul1_1     |     2017/10/05 11:32:11 [INFO] consul: Adding LAN server node1 (Addr: tcp/192.168.192.2:8300) (DC: scaleway-paris)\nconsul1_1     |     2017/10/05 11:32:11 [INFO] agent: Started DNS server 0.0.0.0:8600 (tcp)\nconsul1_1     |     2017/10/05 11:32:11 [INFO] agent: Started DNS server 0.0.0.0:8600 (udp)\nconsul1_1     |     2017/10/05 11:32:11 [INFO] agent: Started HTTP server on [::]:8500\nconsul1_1     |     2017/10/05 11:32:18 [ERR] agent: failed to sync remote state: No cluster leader\nconsul1_1     |     2017/10/05 11:32:21 [WARN] raft: Heartbeat timeout from \"\" reached, starting election\nconsul1_1     |     2017/10/05 11:32:21 [INFO] raft: Node at 192.168.192.2:8300 [Candidate] entering Candidate state in term 2\nconsul1_1     |     2017/10/05 11:32:21 [INFO] raft: Election won. Tally: 1\nconsul1_1     |     2017/10/05 11:32:21 [INFO] raft: Node at 192.168.192.2:8300 [Leader] entering Leader state\nconsul1_1     |     2017/10/05 11:32:21 [INFO] consul: cluster leadership acquired\nconsul1_1     |     2017/10/05 11:32:21 [INFO] consul: New leader elected: node1\nconsul1_1     |     2017/10/05 11:32:21 [INFO] consul: Created ACL master token from configuration\nconsul1_1     |     2017/10/05 11:32:21 [INFO] consul: ACL bootstrap disabled, existing management tokens found\nconsul1_1     |     2017/10/05 11:32:21 [INFO] consul: member 'node1' joined, marking health alive\nconsul1_1     |     2017/10/05 11:32:22 [ERR] agent: failed to sync remote state: ACL not found\nconsul1_1     |     2017/10/05 11:32:34 [ERR] agent: Coordinate update error: ACL not found\n```\n\nAfter the servers are restarted above, you will see new errors in the logs of the Consul servers related to permission denied errors:\n\n```\nconsul1_1     |     2017/10/05 11:32:22 [ERR] agent: failed to sync remote state: ACL not found\nconsul1_1     |     2017/10/05 11:32:34 [ERR] agent: Coordinate update error: ACL not found\n```\n\nThese errors are because the agent doesn't yet have a properly configured `acl_agent_token` that it can use for its own\ninternal operations like updating its node information in the catalog and performing anti-entropy\nsyncing ([more information](https://www.consul.io/docs/guides/acl.html#create-an-agent-token)).\n\nUse *consul-cli* to create Agent Token:\n\n```\n$ docker-compose run --rm consul-cli acl --token=`cat master.token` create --name=\"node1 agent\" --rule=\"node::write\" --rule=\"service::read\" | tr -d \"\\r\" \u003e node1.token\n```\n\nNote: here I use my [harobed/consul-cli](https://hub.docker.com/r/harobed/consul-cli/) Docker image to use *consul-cli* version `0.5.0` because\nI need this patch [Add node rule ACL support (fix #49) #51](https://github.com/mantl/consul-cli/pull/51) to set `node` ACL rules.\n\nCheck configuration:\n\n```\n$ docker-compose run --rm consul-cli acl --token=`cat master.token` list\n[\n  {\n    \"CreateIndex\": 10,\n    \"ModifyIndex\": 10,\n    \"ID\": \"3c694360-9a6d-9609-cd95-d98d7536418c\",\n    \"Name\": \"node1 agent\",\n    \"Type\": \"client\",\n    \"Rules\": \"{\\\"node\\\":{\\\"\\\":{\\\"Policy\\\":\\\"write\\\"}},\\\"service\\\":{\\\"\\\":{\\\"Policy\\\":\\\"read\\\"}}}\"\n  },\n  {\n    \"CreateIndex\": 5,\n    \"ModifyIndex\": 5,\n    \"ID\": \"FBAF54CC-E03D-4763-9F19-376114D3857B\",\n    \"Name\": \"Master Token\",\n    \"Type\": \"management\",\n    \"Rules\": \"\"\n  },\n  {\n    \"CreateIndex\": 4,\n    \"ModifyIndex\": 4,\n    \"ID\": \"anonymous\",\n    \"Name\": \"Anonymous Token\",\n    \"Type\": \"client\",\n    \"Rules\": \"\"\n  }\n]\n```\n\nWe can now add this to our Consul server configuration:\n\n```\n$ curl --request PUT --header \"X-Consul-Token: `cat master.token`\" --data '{\"Token\": \"'`cat node1.token`'\"}' http://127.0.0.1:8500/v1/agent/token/acl_agent_token\n```\n\nAfter few seconds, we can see in *consul1* logs:\n\n```\nconsul1_1     |     2017/10/05 14:00:15 [INFO] Updated agent's ACL token \"acl_agent_token\"\nconsul1_1     |     2017/10/05 14:00:37 [INFO] agent: Synced node info\n```\n\n## Explore Consul Admin UI\n\nBrowse http://127.0.0.1:8500/ui/\n\n```\n$ open -a firefox http://127.0.0.1:8500/ui/\n```\n\nYou can set *ACL Master token* in Settings (http://127.0.0.1:8500/ui/#/settings) page.\n\n\n## Create other agent client token to read / write keys values\n\nCreate *bob* and *alice* client agent tokens, and allow write access in their namespaces:\n\n```\n$ docker-compose run --rm consul-cli acl --token=`cat master.token` create --name=\"bob\" --rule=\"key:bob:write\" | tr -d \"\\r\" \u003e bob.token\n$ docker-compose run --rm consul-cli acl --token=`cat master.token` create --name=\"alice\" --rule=\"key:alice:write\" | tr -d \"\\r\" \u003e alice.token\n```\n\n## Write and read datas\n\n*bob* can write in *bob* namespace but not in *alice* namespace:\n\n```\n$ docker-compose run --rm consul-cli kv --token=`cat bob.token` write bob/foo bar\n$ docker-compose run --rm consul-cli kv --token=`cat bob.token` write alice/foo bar\nUnexpected response code: 403 (Permission denied)\n$ docker-compose run --rm consul-cli kv --token=`cat alice.token` write alice/foo bar\n```\n\n*master token* can read all key records:\n\n```\n$ docker-compose run --rm consul-cli kv --token=`cat master.token` read --recurse / --format prettyjson\n[\n  {\n    \"Key\": \"alice/foo\",\n    \"CreateIndex\": 83,\n    \"ModifyIndex\": 83,\n    \"LockIndex\": 0,\n    \"Flags\": 0,\n    \"Value\": \"YmFy\",\n    \"Session\": \"\"\n  },\n  {\n    \"Key\": \"bob/foo\",\n    \"CreateIndex\": 59,\n    \"ModifyIndex\": 78,\n    \"LockIndex\": 0,\n    \"Flags\": 0,\n    \"Value\": \"YmFy\",\n    \"Session\": \"\"\n  }\n]\n```\n\n*bob* can only read its records:\n\n```\n$ docker-compose run --rm consul-cli kv --token=`cat bob.token` read --recurse / --format prettyjson\n[\n  {\n    \"Key\": \"bob/foo\",\n    \"CreateIndex\": 59,\n    \"ModifyIndex\": 78,\n    \"LockIndex\": 0,\n    \"Flags\": 0,\n    \"Value\": \"YmFy\",\n    \"Session\": \"\"\n  }\n]\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharobed%2Fconsul-acl-client-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharobed%2Fconsul-acl-client-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharobed%2Fconsul-acl-client-tutorial/lists"}