Why ACLs Are Non-Negotiable in Production
By default, Consul allows unauthenticated access to its HTTP API — any process on the network can read the full service catalog, modify KV data, or even deregister services. The Access Control List (ACL) system locks this down by requiring callers to present a valid token that grants only the permissions they need.
Enabling ACLs is one of the most important hardening steps before running Consul in production.
Core Concepts
Tokens
A token is a UUID (or a human-readable "accessor") that a client presents with every API request. Each token is linked to one or more policies that define what it can do.
Policies
Policies are written in HCL or JSON using Consul's rules language. A rule grants or denies a capability (read, write, deny) on a specific resource type and name.
Roles
Roles bundle multiple policies together, making it easy to issue tokens to classes of services without duplicating policy definitions. Think of a role as a named permission set.
The Bootstrap Token
The bootstrap token is a special management token with unrestricted access, created once when ACLs are first enabled. It should be stored securely (e.g., in Vault) and used only for administrative operations.
Step 1: Enable ACLs in the Configuration
Add the following to your server configuration:
acl {
enabled = true
default_policy = "deny"
enable_token_persistence = true
}
Setting default_policy = "deny" means all API calls without a valid token are rejected — this is the recommended approach.
Step 2: Bootstrap the ACL System
consul acl bootstrap
This outputs the bootstrap token. Save it immediately — it cannot be retrieved again. Set it in your shell for subsequent commands:
export CONSUL_HTTP_TOKEN=<bootstrap-token>
Step 3: Write a Policy
Create a policy file web-policy.hcl that allows a service to register itself and read other services:
service "web" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
node_prefix "" {
policy = "read"
}
Apply it:
consul acl policy create -name "web-service" -rules @web-policy.hcl
Step 4: Create a Token for Your Service
consul acl token create -description "Token for web service" -policy-name "web-service"
Pass the resulting token to your service via the CONSUL_HTTP_TOKEN environment variable or the token field in your agent config.
Common Policy Patterns
| Use Case | Resource | Permission |
|---|---|---|
| Service self-registration | service "<name>" | write |
| Read all services | service_prefix "" | read |
| KV namespace access | key_prefix "app/config/" | read/write |
| Node registration | node "<name>" | write |
| Manage intentions | intention_prefix "" | write |
Anonymous Token
The anonymous token is used for requests that don't include any token. With default_policy = "deny", the anonymous token has no permissions. You can grant limited read access to it if you want unauthenticated clients to query the catalog — but be deliberate about what you expose.
Token Rotation and TTLs
Consul supports token expiration via the ExpirationTime or ExpirationTTL fields. Short-lived tokens are ideal for CI/CD pipelines and one-off administrative tasks. For long-running services, pair token rotation with a secrets manager like HashiCorp Vault using Vault's Consul secrets engine.
Auditing ACL Activity
Consul Enterprise includes an audit log that records every ACL token use. For open-source deployments, consider forwarding Consul's structured logs to your SIEM and alerting on permission-denied responses, which may indicate misconfigured tokens or unauthorized access attempts.
Summary
Consul's ACL system follows the principle of least privilege: every token should have exactly the permissions it needs and no more. Start with a deny-all default, define narrow policies per service, and use roles to manage permissions at scale. Combined with TLS encryption, ACLs form the security backbone of any production Consul deployment.