Hi OpenZiti team, I need help investigating a possible posture/session authorization issue in OpenZiti 1.7.0.
Environment
OpenZiti: 1.7.0
Client: Ziti Desktop Edge
Identity: test-client
Identity ID: 38iAeJ9CY
Client OS: macOS 26.5.1
Test topology
I have two services. Both services are terminated by the same OpenZiti router, but they have completely separate Dial and Bind policies.
┌── Jenkins Service
│ ├── Jenkins Dial Policy
│ │ └── Windows posture check
│ └── Jenkins Bind Policy
│
Test identity ─────┤
│
└── Nginx Service
├── Nginx Dial Policy
│ └── No posture check
└── Nginx Bind Policy
Both services terminate on:
f028fa93-9ddc-4e39-8c52-0ad05b4b899c-pvt-router
Router
Name: f028fa93-9ddc-4e39-8c52-0ad05b4b899c-pvt-router
Router ID: ARxFVguc1
Jenkins service
{
"id": "1ZJnCYcwGeYoI4tFpXrAMn",
"name": "62fe107d-2c7e-4b71-b6ab-59d453e9861f-service",
"intercept": "jenkins.uney-int.com:8080/tcp",
"terminatingRouter": "f028fa93-9ddc-4e39-8c52-0ad05b4b899c-pvt-router"
}
Jenkins Dial Policy
{
"id": "1QaPTpXzA3XWf929WnpFPo",
"name": "fa0736aa-f089-44e2-b307-838cb962780a-sdp",
"identityRoles": [
"#f69fa2fd-5d89-4e08-b3b9-5e53e9b1d418-team"
],
"postureCheckRoles": [
"#jenkins-windows"
],
"serviceRoles": [
"#62fe107d-2c7e-4b71-b6ab-59d453e9861f-service"
],
"semantic": "AnyOf",
"type": "Dial"
}
Jenkins Bind Policy
{
"id": "2lOadTn2eM9TxQKPWh4Tjo",
"identityRoles": [
"@ARxFVguc1"
],
"postureCheckRoles": null,
"serviceRoles": [
"#62fe107d-2c7e-4b71-b6ab-59d453e9861f-service"
],
"semantic": "AnyOf",
"type": "Bind"
}
Jenkins posture check
{
"id": "FdYrNdftf1jJpyvMivDuL",
"name": "jenkins-windows",
"typeId": "OS",
"roleAttributes": [
"jenkins-windows"
],
"operatingSystems": [
{
"type": "Windows",
"versions": [
">=10.0.0"
]
}
]
}
The test machine is macOS, so this posture check must fail.
Nginx service
{
"id": "5tRlKsUWaEqJoiTMeLTc6C",
"name": "853720e7-b547-47f6-9005-00217428c10d-service",
"intercept": "10.21.2.66:80/tcp",
"terminatingRouter": "f028fa93-9ddc-4e39-8c52-0ad05b4b899c-pvt-router"
}
Nginx Dial Policy
{
"id": "6MgyQmRfRLbRGfv0TYLx9A",
"identityRoles": [
"#9534ab6f-d31a-4587-9575-546261331334-team",
"#f69fa2fd-5d89-4e08-b3b9-5e53e9b1d418-team"
],
"postureCheckRoles": null,
"serviceRoles": [
"#853720e7-b547-47f6-9005-00217428c10d-service"
],
"semantic": "AnyOf",
"type": "Dial"
}
Nginx Bind Policy
{
"id": "4Nb4mWq0R3kr4g2B7Lesyo",
"identityRoles": [
"@ARxFVguc1"
],
"postureCheckRoles": null,
"serviceRoles": [
"#853720e7-b547-47f6-9005-00217428c10d-service"
],
"semantic": "AnyOf",
"type": "Bind"
}
Test 1 – Jenkins only
I removed all other services for test-client and left only Jenkins.
Result:
macOS client
↓
Jenkins Dial Policy
↓
jenkins-windows posture check
↓
macOS != Windows
↓
Jenkins denied
This is the expected behavior.
Test 2 – Add Nginx
I then added the Nginx service.
Important: Nginx uses a different service role, different Dial policy and different Bind policy. The only infrastructure shared by the two services is the terminating router.
Result:
Nginx → accessible
Jenkins → ALSO accessible
This is the unexpected behavior.
Adding a posture-free service changes the authorization result for Jenkins.
Client posture information
When dialing Jenkins, the client correctly identifies the Jenkins posture requirement:
using default OS cb for:
service 62fe107d-2c7e-4b71-b6ab-59d453e9861f-service
policy: 1QaPTpXzA3XWf929WnpFPo
check: FdYrNdftf1jJpyvMivDuL
It then sends:
{
"id": "FdYrNdftf1jJpyvMivDuL",
"typeId": "OS",
"type": "macOS",
"version": "26.5.1",
"build": "25.5.0"
}
So the client is definitely reporting macOS and should fail the Windows posture requirement.
Controller response is also interesting
After the posture submission, the controller reports Jenkins as:
{
"permissions": [
"Dial"
],
"postureQueries": [
{
"isPassing": false,
"policyId": "1QaPTpXzA3XWf929WnpFPo",
"policyType": "Dial",
"postureQueries": [
{
"id": "FdYrNdftf1jJpyvMivDuL",
"isPassing": false,
"queryType": "OS"
}
]
}
]
}
So the controller knows that the Jenkins posture is not passing.
However, the controller subsequently returns a valid Jenkins Dial session:
{
"identityId": "38iAeJ9CY",
"serviceId": "1ZJnCYcwGeYoI4tFpXrAMn",
"type": "Dial"
}
The client then connects through the router successfully and Jenkins traffic works.
Relevant SDK sequence
The important sequence in the logs is approximately:
9.271 POST /posture-response-bulk
9.271 POST /sessions
10.031 POST /posture-response-bulk
response: {"data":{"services":[]},"meta":{}}
10.272 POST /sessions
identityId = 38iAeJ9CY
serviceId = 1ZJnCYcwGeYoI4tFpXrAMn
type = Dial
10.272 Jenkins Dial connection starts
10.272 router selected:
f028fa93-9ddc-4e39-8c52-0ad05b4b899c-pvt-router
10.508 connection becomes Connected
There is also another Jenkins /sessions response shortly afterward, and the SDK logs:
discarding existing session[cmtvfjc5k0va90dirqjj29sqj]
for service[62fe107d-2c7e-4b71-b6ab-59d453e9861f-service]
before using the newer session.
Why I don't think this is a normal policy overlap
The Jenkins and Nginx policies match different service roles:
Jenkins:
#62fe107d-2c7e-4b71-b6ab-59d453e9861f-service
Nginx:
#853720e7-b547-47f6-9005-00217428c10d-service
Jenkins has an explicit posture requirement.
Nginx does not.
They have separate Dial and Bind policies.
Both simply happen to terminate on the same router.
Most importantly, the controller still reports Jenkins posture as:
isPassing: false
yet a Jenkins Dial session is issued and accepted.
Additional observation
The behavior is consistently dependent on the number of services:
Jenkins only
→ posture fails
→ Jenkins denied
Jenkins + Nginx
→ Jenkins posture still reported as failing
→ Jenkins Dial session issued
→ Jenkins accessible
I also have a router-side test with another identity where the same Jenkins posture check is correctly enforced and the router logs:
policies provided access but posture checks failed
posture check jenkins-windows ... failed
given: [macOS]
valid: [Windows]
no access to service, failed dial access check
So the router is capable of enforcing the posture check correctly.
What could explain this?
At this point I am trying to determine whether this is:
1. Expected OpenZiti behavior
2. A policy evaluation issue
3. Stale posture/session state
4. Controller/router posture state synchronization issue
5. An OpenZiti bug
My current suspicion is posture/session authorization state being incorrectly reused or evaluated when multiple services are present, because adding an unrelated service changes the outcome for Jenkins even though Jenkins' own policy and posture configuration remain unchanged.
Could someone from the OpenZiti team please advise:
-
How posture state is associated with API sessions/services?
-
Whether posture evaluation can be affected by another service on the same identity/router?
-
Why
/sessionscan return a Jenkins Dial session while Jenkins posture is reported asisPassing:false? -
Whether there are known issues around multiple services and posture state in OpenZiti 1.7.x?
-
What controller/router debug logs would show the exact policy + posture decision used when the Jenkins session is created?
I can provide the complete client and router logs from both test cases.