Issues Observed with Auth Policy Configuration via ZAC in OpenZiti Controller v1.5.4

Hi OpenZiti Team :slight_smile: ,

I’ve encountered two issues after modifying the default authentication policy on my Ziti controller using ZAC, that I would need help with. One is a minor inconsistency, and the other is more concerning from my point of view.

Incorrect JSON Key for Numeric Password Requirement

When enabling the “Numeric” password requirement via ZAC, the UI adds the key requireNumericChar to the JSON object, prepared for sending to the controller. However, based on your API documentation and my testing, the controller expects the key to be requireNumberChar.

Here’s a screenshot illustrating the issue:

Admin Login Blocked After Changing maxAttempts

After updating the maxAttempts setting in the auth policy, I’m unable to log in with the admin user—even when entering the correct password on the first attempt.

The controller logs show:

{"attempts":44,"authMethod":"password","authPolicyId":"default","authenticatorId":"XzXfdTDB3v","file":"github.com/openziti/ziti/controller/model/authenticator_mod_updb.go:127","func":"github.com/openziti/ziti/controller/model.(*AuthModuleUpdb).Process","identityId":"HwHBhpuBO","level":"error","maxAttempts":3,"msg":"updb auth failed, max attempts exceeded","time":"2025-08-13T08:48:15.930Z","username":"admin"}

This results in the Default Admin identity being temporarily disabled (for one minute, for testing). The identity object reflects this state:

{
  "createdAt": "2023-06-30T12:37:39.152Z",
  "id": "HwHBhpuBO",
  "tags": {},
  "updatedAt": "2025-08-13T08:48:15.930Z",
  "appData": {},
  "authPolicy": {
    "_links": {
      "self": {
        "href": "./auth-policies/default"
      }
    },
    "entity": "auth-policies",
    "id": "default",
    "name": "Default"
  },
  "authPolicyId": "default",
  "authenticators": {
    "updb": {
      "id": "XzXfdTDB3v",
      "username": "admin"
    }
  },
  "defaultHostingCost": 0,
  "defaultHostingPrecedence": "default",
  "disabled": false,
  "disabledAt": "2025-08-13T08:48:15.930Z",
  "disabledUntil": "2025-08-13T08:49:15.930Z",
  "edgeRouterConnectionStatus": "offline",
  "enrollment": {},
  "envInfo": {},
  "externalId": null,
  "hasApiSession": false,
  "hasEdgeRouterConnection": false,
  "isAdmin": true,
  "isDefaultAdmin": true,
  "isMfaEnabled": false,
  "name": "Default Admin",
  "roleAttributes": null,
  "sdkInfo": {},
  "serviceHostingCosts": {},
  "serviceHostingPrecedences": {},
  "type": {
    "_links": {
      "self": {
        "href": "./identity-types/Default"
      }
    },
    "entity": "identity-types",
    "id": "Default",
    "name": "Default"
  },
  "typeId": "Default",
  "badges": [
    {
      "label": "Offline",
      "class": "offline",
      "circle": "false"
    }
  ]
}

Even after re-enabling the identity via the API:

curl 'https://xxx/edge/management/v1/identities/HwHBhpuBO/enable'  
-X 'POST'   
-H 'accept: application/json'   
-H 'content-type: application/json'   
-H 'zt-session: XXXXX'
{"data":{},"meta":{}}

I still receive the following error when attempting to log in:

{"authMethod":"password","authenticatorId":"bCCsIpKVa","disabledAt":"2025-08-13T08:53:02.388984605Z","disabledUntil":"2025-08-13T08:54:02.388984605Z","file":"github.com/openziti/ziti/controller/model/authenticator_mod_updb.go:105","func":"github.com/openziti/ziti/controller/model.(*AuthModuleUpdb).Process","identityId":"cjXVI2Kja9","level":"error","msg":"authentication failed, identity is disabled","time":"2025-08-13T08:53:08.774Z","username":"tds-core-services-backend"}

The way on how to recover from this error was to use a different admin user (which I luckily had :wink: ) and set the maxAttempts setting on the auth-policy back to 0.

And one more question, regarding the minPasswordLength, does changing that affect already set passwords, I assume not ? Does it get enforced on future password changes ?

Environment Details

  • Ziti Controller Version: v1.5.4

Please let me know if there’s a workaround or if I’ve missed something in the configuration. I’d be happy to provide more details if needed.

Thanks for your support!

Best regards,
Jan

Hi OpenZiti Team ! :slight_smile:

did you have a chance to look at that ?
It would be helpful to know, whether I made a configuration mistake here, or possibly misunderstood some configuration properties :wink:

If you want to test sth. to validate that behavior, I can absolutely support here ! :slight_smile:

Hi @TheLumberjack,

have you had the time to look into my findings ? :smiley:

According to my latest validation done with the docker-compose setup and versions

controller: v1.6.8
zac: 3.12.5

the issue Incorrect JSON Key for Numeric Password Requirement seems to still exist.

I have also cross-checked with the latest available mgmt-api openapi spec and this spec also defines *requireNumberChar" not requireNumericChar :slight_smile:

authPolicyPrimaryUpdb:
    type: object
    required:
    - allowed
    - minPasswordLength
    - requireSpecialChar
    - requireNumberChar
    - requireMixedCase
    - maxAttempts
    - lockoutDurationMinutes
    properties:
      allowed:
        type: boolean
      lockoutDurationMinutes:
        type: integer
      maxAttempts:
        type: integer
      minPasswordLength:
        type: integer
      requireMixedCase:
        type: boolean
      requireNumberChar:
        type: boolean
      requireSpecialChar:
        type: boolean

I am also planning to retest the issue Admin Login Blocked After Changing maxAttempts with the same setup and newest versions and will then update this post :slight_smile:

Edit:
I have now been able to also reproduce the login issue mentioned above.

It can be reproduced as follows:

  1. Create auth policy like:
{
  "_links": {
    "self": {
      "href": "./auth-policies/51fcuP5ljhAZXNSdVLVsfS"
    }
  },
  "createdAt": "2025-09-16T10:54:17.126Z",
  "id": "51fcuP5ljhAZXNSdVLVsfS",
  "tags": {},
  "updatedAt": "2025-09-16T10:54:17.126Z",
  "name": "myauthpol",
  "primary": {
    "cert": {
      "allowExpiredCerts": false,
      "allowed": false
    },
    "extJwt": {
      "allowed": false,
      "allowedSigners": []
    },
    "updb": {
      "allowed": true,
      "lockoutDurationMinutes": 1,
      "maxAttempts": 3,
      "minPasswordLength": 5,
      "requireMixedCase": false,
      "requireNumberChar": false,
      "requireSpecialChar": false
    }
  },
  "secondary": {
    "requireTotp": false
  }
}
  1. Create a new identity with admin rights and create a updb authenticator for it:
ziti edge create identity user JohnDoe -A --updb JohnDoe
ziti edge create authenticator updb JohnDoe JohnDoe test1234
  1. Assign the auth policy to the newly created identity.
    -> I did this via the ZAC
{
  "createdAt": "2025-09-16T11:12:55.398Z",
  "id": "kVFAopfiMm",
  "tags": {},
  "updatedAt": "2025-09-16T11:17:47.254Z",
  "appData": {},
  "authPolicy": {
    "_links": {
      "self": {
        "href": "./auth-policies/51fcuP5ljhAZXNSdVLVsfS"
      }
    },
    "entity": "auth-policies",
    "id": "51fcuP5ljhAZXNSdVLVsfS",
    "name": "myauthpol"
  },
  "authPolicyId": "51fcuP5ljhAZXNSdVLVsfS",
  "authenticators": {
    "updb": {
      "id": "1g8CfFrtMBSPFumYTysmaS",
      "username": "JohnDoe"
    }
  },
 ....
  ...
  "isAdmin": true,
  "isDefaultAdmin": false,
  "isMfaEnabled": false,
  "name": "JohnDoe",
  "roleAttributes": null,
  "sdkInfo": {},
  "serviceHostingCosts": {},
  "serviceHostingPrecedences": {},
  "type": {
    "_links": {
      "self": {
        "href": "./identity-types/Default"
      }
    },
    "entity": "identity-types",
    "id": "Default",
    "name": "Default"
  },
  "typeId": "Default",
....
  ]
}
  1. Login using the newly created identity and updb authenticator to the mgmt-api:
curl -X POST -k "https://ziti-edge-controller:1280/edge/management/v1/authenticate?method=password" -H "Content-Type: application/json" --data '{"username":"JohnDoe", "password":"test1234"}'

This will initially work, but if you try to login more than 4 times in total using this curl command, your login is denied upon the 5th login attempt and the following response is send by the controller:

{
  "error": {
    "code": "INVALID_AUTH",
    "message": "The authentication request failed",
    "requestId": "IvJkALfhe"
  },
  "meta": {
    "apiEnrollmentVersion": "0.0.1",
    "apiVersion": "0.0.1"
  }
}

Note: All the login attempts have used the correct combination of username & password.

The logs in the controller say:

ERROR ziti/controller/model.(*AuthModuleUpdb).Process: {username=[JohnDoe] authenticatorId=[1g8CfFrtMBSPFumYTysmaS] authMethod=[password] identityId=[kVFAopfiMm] authPolicyId=[51fcuP5ljhAZXNSdVLVsfS] attempts=[4] maxAttempts=[3]} updb auth failed, max attempts exceeded, attempts: 4, maxAttempts: 3

To me it seems like the succeeded login attempts are also accounted for failed login attempts and additionally the related counter is never reset, also not after a configured lockout period has elapsed. This effectively blocks a user from logging in using this identity forever.

I think that this is sth. which is worth to be investigated further :slight_smile:

BR
Jan

:sewing_needle: This topic continues in a GitHub pull request: Fix UPDB Authentication Lockout Logic and Improve Test Coverage by jan94 · Pull Request #3273 · openziti/ziti · GitHub