Posture Check Bypassed When Multiple Services Share the Same Identity and Router

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 /sessions can return a Jenkins Dial session while Jenkins posture is reported as isPassing: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.