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

**URL:** <https://openziti.discourse.group/t/posture-check-bypassed-when-multiple-services-share-the-same-identity-and-router/6080>\
**Category:** Ziti Overlay\
**Created:** [September 11, 2026, 7:23am UTC](https://openziti.discourse.group/t/posture-check-bypassed-when-multiple-services-share-the-same-identity-and-router/6080 "2026-09-11T07:23:49Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![shoaib-ziti](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@shoaib-ziti](https://openziti.discourse.group/u/shoaib-ziti)\
**Post date:** [September 11, 2026, 7:23am UTC](https://openziti.discourse.group/t/posture-check-bypassed-when-multiple-services-share-the-same-identity-and-router/6080/1 "2026-09-11T07:23:49Z")

</div>

Hi OpenZiti team, I need help investigating a possible posture/session authorization issue in OpenZiti 1.7.0.

## Environment

```auto
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.

```auto
                    ┌── 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

```auto
Name: f028fa93-9ddc-4e39-8c52-0ad05b4b899c-pvt-router
Router ID: ARxFVguc1

```

* * *

## Jenkins service

```auto
{
  "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

```auto
{
  "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

```auto
{
  "id": "2lOadTn2eM9TxQKPWh4Tjo",
  "identityRoles": [
    "@ARxFVguc1"
  ],
  "postureCheckRoles": null,
  "serviceRoles": [
    "#62fe107d-2c7e-4b71-b6ab-59d453e9861f-service"
  ],
  "semantic": "AnyOf",
  "type": "Bind"
}

```

* * *

## Jenkins posture check

```auto
{
  "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

```auto
{
  "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

```auto
{
  "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

```auto
{
  "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:

```auto
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:

```auto
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:

```auto
using default OS cb for:
service 62fe107d-2c7e-4b71-b6ab-59d453e9861f-service
policy: 1QaPTpXzA3XWf929WnpFPo
check: FdYrNdftf1jJpyvMivDuL

```

It then sends:

```auto
{
  "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:

```auto
{
  "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:

```auto
{
  "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:

```auto
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:

```auto
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:

```auto
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:

```auto
isPassing: false

```

yet a Jenkins Dial session is issued and accepted.

* * *

# Additional observation

The behavior is consistently dependent on the number of services:

```auto
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:

```auto
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:

```auto
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.

---

<div class="post-metadata">

**Author:** ![TheLumberjack](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/thelumberjack/32/113_2.png) [@TheLumberjack](https://openziti.discourse.group/u/TheLumberjack)\
**Post date:** [September 15, 2026, 3:03pm UTC](https://openziti.discourse.group/t/posture-check-bypassed-when-multiple-services-share-the-same-identity-and-router/6080/2 "2026-09-15T15:03:48Z")

</div>

Hi @shoaib-ziti, thanks for the details. I'll be trying to reproduce the problem to confirm (or deny) it's a problem... I just wanted to leave a note that I'll have a look but it'll have to be when I can get to it. Just setting expectations 😉 Thanks again for the report.
