binding-rule selectors are not evaluating correctly
- Dominant language
- Go
- Stars
- 30.1k
- Forks
- 4.6k
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 39
Description
#### Overview of the Issue
Binding rules are being applied regardless of the selector expression.
In some cases they are not applying where they should be.
---
#### Reproduction Steps
Here are some steps I've used to reproduce this minimally.
1. Start a consul cluster
```sh
docker run -d --name test-consul \
-e CONSUL_LOCAL_CONFIG='{"datacenter":"test", "server":true, "enable_debug":true, "acl": {"enabled":true} }' \
hashicorp/consul agent -server -bootstrap-expect=1
```
2. Get a shell
```sh
docker exec -it test-consul sh
```
3. Bootstrap ACL and install three binding rules using different selectors
```sh
consul acl bootstrap
MANAGEMENT_TOKEN=4e35e0b2-60d6-d1cd-1d83-e7e8eebf89b7
consul acl auth-method create -name gce-jwt -type jwt -config '{
"BoundIssuer": "https://accounts.google.com",
"BoundAudiences": ["dummy"],
"JWKSURL": "https://www.googleapis.com/oauth2/v3/certs",
"ClaimMappings": {
"google.compute_engine.instance_name": "instance_name"
}
}' -token $MANAGEMENT_TOKEN
cat <<-EOL | consul acl policy create -name test-policy -token $MANAGEMENT_TOKEN -rules -
key_prefix "" {
policy = "deny"
}
EOL
consul acl role create -name="non-match-role" -policy-name="test-policy" -token $MANAGEMENT_TOKEN
consul acl role create -name="match-role" -policy-name="test-policy" -token $MANAGEMENT_TOKEN
consul acl role create -name="eq-role" -policy-name="test-policy" -token $MANAGEMENT_TOKEN
consul acl binding-rule create -method gce-jwt -bind-type role -bind-name non-match-role \
-selector 'value.instance_name matches "^db-foobar-[0-9]+$"' -token $MANAGEMENT_TOKEN
consul acl binding-rule create -method gce-jwt -bind-type role -bind-name match-role \
-selector 'value.instance_name matches "^db-devops-[0-9]+$"' -token $MANAGEMENT_TOKEN
consul acl binding-rule create -method gce-jwt -bind-type role -bind-name eq-role \
-selector 'value.instance_name == "db-devops-1"' -token $MANAGEMENT_TOKEN
```
4. Login using the JWT
I'm using a JWT token generated form a real GCE instance with the following data:
```json
{
"aud": "dummy",
"azp": "106561459854436270623",
"email": "sa-db-devops@.iam.gserviceaccount.com",
"email_verified": true,
"exp": 1753367383,
"google": {
"compute_engine": {
"instance_creation_timestamp": 1753288965,
"instance_id": "",
"instance_name": "db-devops-1",
"project_id": "",
"project_number": "",
"zone": ""
}
},
"iat": 1753363783,
"iss": "https://accounts.google.com",
"sub": ""
}
```
```sh
consul login -type=jwt -method=gce-jwt -bearer-token-file=/tmp/jwt -token-sink-file=/tmp/token
```
5. Now when we check the token, we see that it's been bound to roles I wouldn't expect it to:
```sh
/ # consul acl token read -self -token=$(cat /tmp/token)
AccessorID: a94844b8-5131-40e7-05c8-835577d9735c
SecretID: 81426925-099b-9870-6528-bc2c66008406
Description: token created via login
Local: true
Auth Method: gce-jwt (Namespace: )
Create Time: 2025-07-24 13:32:01.076757206 +0000 UTC
Roles:
3e93c456-5059-b9cb-b40a-201d4ee37b62 - non-match-role
e5a6d11e-4386-754f-8d99-0b99a66c9c4b - match-role
```
The `non-match-role` is matched when it shouldn't be.
The `match-role` is matched as expected.
The `eq-role` is not matched when it should be.
### Consul info for both Client and Server
Client info
```
/ # consul info
agent:
check_monitors = 0
check_ttls = 0
checks = 0
services = 0
build:
prerelease =
revision = 24bf2b62
version = 1.21.3
version_metadata =
consul:
acl = enabled
bootstrap = true
known_datacenters = 1
leader = true
leader_addr = 172.17.0.3:8300
server = true
raft:
applied_index = 130
commit_index = 130
fsm_pending = 0
last_contact = 0
last_log_index = 130
last_log_term = 2
last_snapshot_index = 0
last_snapshot_term = 0
latest_configuration = [{Suffrage:Voter ID:ae7f9719-2e97-de99-ca2b-c585d3e8e36a Address:172.17.0.3:8300}]
latest_configuration_index = 0
num_peers = 0
protocol_version = 3
protocol_version_max = 3
protocol_version_min = 0
snapshot_version_max = 1
snapshot_version_min = 0
state = Leader
term = 2
runtime:
arch = arm64
cpu_count = 2
goroutines = 195
max_procs = 2
os = linux
version = go1.23.10
serf_lan:
coordinate_resets = 0
encrypted = false
event_queue = 1
event_time = 2
failed = 0
health_score = 0
intent_queue = 0
left = 0
member_time = 1
members = 1
query_queue = 0
query_time = 1
serf_wan:
coordinate_resets = 0
encrypted = false
event_queue = 0
event_time = 1
failed = 0
health_score = 0
intent_queue = 0
left = 0
member_time = 1
members = 1
query_queue = 0
query_time = 1
```
```
Client agent HCL config
```
Server info
```
/ # consul info
agent:
check_monitors = 0
check_ttls = 0
checks = 0
services = 0
build:
prerelease =
revision = 24bf2b62
version = 1.21.3
version_metadata =
consul:
acl = enabled
bootstrap = true
known_datacenters = 1
leader = true
leader_addr = 172.17.0.3:8300
server = true
raft:
applied_index = 130
commit_index = 130
fsm_pending = 0
last_contact = 0
last_log_index = 130
last_log_term = 2
last_snapshot_index = 0
last_snapshot_term = 0
latest_configuration = [{Suffrage:Voter ID:ae7f9719-2e97-de99-ca2b-c585d3e8e36a Address:172.17.0.3:8300}]
latest_configuration_index = 0
num_peers = 0
protocol_version = 3
protocol_version_max = 3
protocol_version_min = 0
snapshot_version_max = 1
snapshot_version_min = 0
state = Leader
term = 2
runtime:
arch = arm64
cpu_count = 2
goroutines = 195
max_procs = 2
os = linux
version = go1.23.10
serf_lan:
coordinate_resets = 0
encrypted = false
event_queue = 1
event_time = 2
failed = 0
health_score = 0
intent_queue = 0
left = 0
member_time = 1
members = 1
query_queue = 0
query_time = 1
serf_wan:
coordinate_resets = 0
encrypted = false
event_queue = 0
event_time = 1
failed = 0
health_score = 0
intent_queue = 0
left = 0
member_time = 1
members = 1
query_queue = 0
query_time = 1
```
```
{"datacenter":"test", "server":true, "enable_debug":true, "acl": {"enabled":true} }
```
### Operating system and Environment details
Host: Macbook Pro M1, Squoia 15.5
Running docker using colima
```
⋊> ~/workspace colima version
colima version 0.8.1
git commit: 96598cc5b64e5e9e1e64891642b91edc8ac49d16
runtime: docker
arch: aarch64
client: v28.1.1
server: v27.4.0
⋊> ~/workspace docker version
Client: Docker Engine - Community
Version: 28.1.1
API version: 1.47 (downgraded from 1.49)
Go version: go1.24.2
Git commit: 4eba377327
Built: Fri Apr 18 09:44:47 2025
OS/Arch: darwin/arm64
Context: colima
Server: Docker Engine - Community
Engine:
Version: 27.4.0
API version: 1.47 (minimum version 1.24)
Go version: go1.22.10
Git commit: 92a8393
Built: Sat Dec 7 10:39:01 2024
OS/Arch: linux/arm64
Experimental: false
containerd:
Version: 1.7.24
GitCommit: 88bf19b2105c8b17560993bee28a01ddc2f97182
runc:
Version: 1.2.2
GitCommit: v1.2.2-0-g7cb3632
docker-init:
Version: 0.19.0
GitCommit: de40ad0
```
This occurs in other environments running in GCE on ubuntu VMs, but I can't share the details.
This reproduction should hopefully suffice.
### Log Fragments
https://gist.github.com/d0x2f/801cd7d9e4fec79e7357bbcdde0a9fd0
Contributor guide
Research direction
Reproduce the issue with the Consul 1.21.3 Docker setup and the three ACL binding rules shown. Start by tracing the ACL binding-rule selector evaluation during JWT login, then add regression coverage for the non-matching, matching, and equality selectors. Done means only the expected roles are bound to the resulting token.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, go
- Domain
- authentication, authorization, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100