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 CaseResourcePermission
Service self-registrationservice "<name>"write
Read all servicesservice_prefix ""read
KV namespace accesskey_prefix "app/config/"read/write
Node registrationnode "<name>"write
Manage intentionsintention_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.