How to login using external jwt signer with Linux tunneler

I set up an Entra ID app registration, an external JWT signer and an auth policy as per the docs. I assigned the auth policy to the identity I’m testing with.

When I try to log in with sudo ziti-edge-tunnel ext-jwt-login -i roche-ziti-client -p entra I get the following error:

{"Success":false,"Error":"ziti context not found","Code":500}

I’m very likely missing steps since I didn’t find any docs on how to set up auth with an external JWT signer under Linux Tunneller | NetFoundry Documentation

The tunneler logs show the following:
(161395)[ 1.019] DEBUG
ziti-sdk:ziti_ctrl.c:503 ctrl_body_cb()
ctrl[``https://ziti-api.mydomain.com:443``] completed
POST[/authenticate?method=cert] in 0.360 s(161395)[ 1.019] ERROR
ziti-sdk:ziti_ctrl.c:525 ctrl_body_cb()
ctrl[``https://ziti-api.mydomain.com:443``] API
request[/authenticate?method=cert] failed code[INVALID_AUTH] messageThe
authentication request failed
[ 1.019] ERROR
ziti-sdk:ziti_ctrl.c:389 ctrl_login_cb()
ctrl[``https://ziti-api.mydomain.com:443``] INVALID_AUTH(The authentication
request failed)(161395)[ 1.019] DEBUG ziti-sdk:ziti_ctrl.c:380
ziti_ctrl_clear_api_session() ctrl[``https://ziti-api.mydomain.com:443``]
clearing api session token for ziti_controller(161395)[ 1.019] WARN
ziti-sdk:legacy_auth.c:183 login_cb() failed to login to
ctrl[``https://ziti-api.mydomain.com:443``] INVALID_AUTH[-14] The authentication
request failed(161395)[ 1.019] DEBUG ziti-sdk:ziti.c:271
ziti_set_impossible_to_authenticate() ztx[0] setting api_session_state[0] to
4(161395)[ 1.019] DEBUG ziti-sdk:ziti_ctrl.c:380
ziti_ctrl_clear_api_session() ctrl[``https://ziti-api.mydomain.com:443``] clearing api session token for ziti_controller(161395)[ 1.019] WARN tunnel-cbs:ziti_tunnel_ctrl.c:1018 on_ziti_event() ziti_ctx controller connections failed: failed to authenticate(161395)[ 1.019] INFO ziti-edge-tunnel:ziti-edge-tunnel.c:460 on_event() ztx[/opt/openziti/etc/identities/roche-ziti-client.json] context event : status is failed to authenticate(161395)[ 1.019] ERROR ziti-edge-tunnel:ziti-edge-tunnel.c:514 on_event() ztx[/opt/openziti/etc/identities/roche-ziti-client.json] failed to connect to controller due to failed to authenticate(161395)[ 1.019] DEBUG ziti-edge-tunnel:ipc_event.c:119 send_events_message() Events Message => {"Op":"identity","Action":"added","Fingerprint":"roche-ziti-client","Id":{"Name":"roche-ziti-client","Identifier":"/opt/openziti/etc/identities/roche-ziti-client.json","FingerPrint":"roche-ziti-client","Active":true,"Loaded":true,"IdFileStatus":false,"NeedsExtAuth":false,"MfaEnabled":false,"MfaNeeded":false,"Metrics":{"Up":0,"Down":0},"MfaMinTimeout":0,"MfaMaxTimeout":0,"MfaMinTimeoutRem":0,"MfaMaxTimeoutRem":0,"MinTimeoutRemInSvcEvent":0,"MaxTimeoutRemInSvcEvent":0,"Deleted":false,"Notified":false}}

I see it’s trying to use method=cert. How do I instruct it to use JWT?

FWIW, the tunneler connects and works perfectly when using a manually created JWT file with:
ziti edge create identity "roche-ziti-client" --jwt-output-file /tmp/roche-ziti-client.jwt

I appreciate any help getting this working!

You can do it but with linux it's a bit more involved. First download the external jwt signer from ZAC's jwt signers page

Enroll an identity using that jwt:

ziti-edge-tunnel enroll --jwt /mnt/c/temp/ctrl.cdaws.clint.demo.openziti.org.jwt2 --identity /opt/openziti/etc/identities/ClintWSLOIDC.json

Run your ziti-edge-tunnel (service or however you do it, here i'm doing it directly):

ziti-edge-tunnel run -i /opt/openziti/etc/identities/ClintWSLOIDC.json

After it's running initiate the ext-jwt-login flow:

$ sudo ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/ClintWSLOIDC.json -p keycloak
{
  "Success":true,
  "Data":{
    "identifier":"/opt/openziti/etc/identities/ClintWSLOIDC.json",
    "url":"https://keycloak.zrok.clint.demo.openziti.org:8446/realms/zitirealm/protocol/openid-connect/auth?client_id=browzerBootstrapClient&scope=openid%20email%20profile&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A20314%2Fauth%2Fcallback&code_challenge=podaPlMdZeaf1x3melz355XggGWli5abU1N8DaVEZ6Px1ctM&code_challenge_method=S256&state=PKIrgx470mjer9qzrV11222T1VZSgUobIz2iAk-hivK&audience=browzerBootstrapClient"
  },
  "Code":0
}

Notice you get a json reply, but your browser won't open automatically. Take that url and paste it into your browser... Auth through keycloak (or whatever) and off you go.

Some among us have automated that to open your browser using something like:

sudo ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/ClintWSLOIDC.json -p keycloak | jq -r '.Data.url' | xargs xdg-open

And off you go... Hope that helps

ADDENDUM
If you have a url that's externally valid, you could use the external url to enroll but using the JWT is guaranteed to work so I went that way for the example. here's how by url works:

ziti-edge-tunnel enroll --url https://ctrl.cdaws.clint.demo.openziti.org:8441/ --identity /opt/openziti/etc/identities/ClintWSLOIDC_byurl.json

Thank you!

Previously, with a manual JWT, I used enroll to generate the identity JSON with ziti edge enroll --jwt /tmp/roche-ziti-client.jwt --identity --out /opt/openziti/etc/roche-ziti-client.json.

How do I generate the JSON file for the identity I want to enroll when I’m not using a JWT for the identity itself, but the one from the external JWT signer?

You would just assign the appropriate external Id to the identity that you originally created and enrolled. Then you ONLY use the identity file for the external auth (not the cert-based identity file you originally enrolled).

If the JWT that you supply to the controller after authenticating to your IdP contains the proper claim, you will get an api session for that identity.

You don't need to generate a json file for that particular identity, it'll be driven by the security tokenr returned from the idp (and submitted to the controller for auth)

Thanks again!

I made some progress, but after starting the tunnel and running:
sudo ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/entra.json -p entra

I get this error:
(2366643)[ 0.643] INFO ziti-sdk:ziti.c:2016 version_pre_auth_cb() ztx[0] connected to Legacy controller ``https://ziti-api.mydomain.com:443/`` version v1.6.5(0efb60b21134 2025-07-09T15:15:36Z)
(2366643)[ 0.996] INFO tunnel-cbs:ziti_tunnel_ctrl.c:1128 on_ziti_event() ztx[/opt/openziti/etc/identities/entra.json//opt/openziti/etc/identities/entra.json] ext auth event received
(2366643)[ 0.996] INFO ziti-edge-tunnel:ziti-edge-tunnel.c:808 on_event() ztx[/opt/openziti/etc/identities/entra.json] ext auth: login_with_ext_signer
(2366643)[ 43.055] ERROR tlsuv:engine.c:821 openssl: handshake was terminated: error:00000005:lib(0)::reason(5)
(2366643)[ 43.055] ERROR tlsuv:tls_link.c:113 TLS(0x55a232f89cc8) handshake error error:00000005:lib(0)::reason(5)
(2366643)[ 43.055] ERROR tlsuv:http.c:188 handshake failed status[3]: error:00000005:lib(0)::reason(5)
(2366643)[ 43.055] ERROR ziti-sdk:external_auth.c:33 ext_oath_cfg_cb() ztx[0] OIDC provider configuration failed: (null)
(2366643)[ 43.055] ERROR tunnel-cbs:ziti_tunnel_ctrl.c:1098 on_ziti_event() ztx[/opt/openziti/etc/identities/entra.json//opt/openziti/etc/identities/entra.json] authorization flow cannot continue: (null)
(2366643)[ 43.055] INFO ziti-edge-tunnel:ziti-edge-tunnel.c:460 on_event() ztx[/opt/openziti/etc/identities/entra.json] context event : status is (null)
(2366643)[ 43.055] ERROR ziti-edge-tunnel:ziti-edge-tunnel.c:514 on_event() ztx[/opt/openziti/etc/identities/entra.json] failed to connect to controller due to (null)

I can't tell with this information. My guess is that your IdP isn't trusted by the OS and thus the tunneler won't connect to the IdP. Since you mention it's MS Entra, that seems hard to believe, but it's possible I suppose.

It's also possible you somehow don't have things configured correctly.

Let's change to using the ziti cli. Try running a command similar to mine:

ziti ops verify ext-jwt-signer oidc \
  --username $ZITI_USER \
  --password $ZITI_PWD \
  --authenticate \
  keycloak 

You should see your access token, ID token and refresh token shown to you (only the contents) and you'll see the ziti CLI will try to authenticate. If you see a success message, well then there's something else to dig into. If you see a failure, you'll have some sort of configuration problem to figure out.

Can you try the ziti CLI and get a success message to make sure everything is setup properly?

Yeah, something is up with my local config.

When I try to auth with ziti on my local host, auth fails:
kubectl get secrets "openziti-controller-admin-secret"
--namespace openziti
--output go-template='{{index .data "admin-password" | base64decode }}'
| xargs -rl ziti edge login ``ziti-web.mydomain.com:443``
--yes --username "admin"
--password
error: unable to authenticate to ``https://ziti-web.mydomain.com:443/edge/management/v1``. Status code: 401 Unauthorized, Server returned: {
"error": {
"code": "INVALID_AUTH",
"message": "The authentication request failed",
"requestId": "jmViNp6yA"
},
"meta": {
"apiEnrollmentVersion": "0.0.1",
"apiVersion": "0.0.1"
}
}

If I run the command you provided inside the controller in my kubernetes cluster, I seem to get a sensible response, which at least seems to suggest that the JWT config is accurate.
[ziggy@openziti-controller-7dbf857595-cmc7f ~]$ ziti ops verify ext-jwt-signer oidc entra
Using controller url: ``https://openziti-controller-client.openziti.svc.cluster.local:443/edge/management/v1`` from identity 'default' in config file: /home/ziggy/.config/ziti/ziti-cli.json
Untrusted certificate authority retrieved from server
Verified that server supplied certificates are trusted by server
Server supplied 4 certificates
Trust server provided certificate authority [Y/N]: Y
Server certificate chain written to /home/ziggy/.config/ziti/certs/openziti-controller-client.openziti.svc.cluster.local
Saving identity 'default' to /home/ziggy/.config/ziti/ziti-cli.json
INFO using supplied redirect url: ``http://localhost:20314/auth/callback
INFO found external JWT signer
INFO - issuer: ``https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0
INFO - clientId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
INFO supplied issuer matches discovered issuer: ``https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0
INFO attempting to authenticate to external provider

It’s late for me, and I’ll debug this further tomorrow. Thanks so much for your help so far!

You won't be able to run it (i don't think?) from within the cluster itself as you'll have to complete the OIDC flow using a browser. So if you can do that from a machine IN the cluster - well cool. :slight_smile: It would just surprise me. It likely will get hung up at Waiting up to 30s for external auth...

When you look at it tomorrow, make sure to look at the logs of the controller. It will likely have a hint as to what is going on and misconfigured.

My guess is still that you have a trust problem with the local OS not trusting the Entra instance, but maybe it's past that. If you get through the OIDC flow and auth to the IdP then it's not a trust issue, and instead it's some sort of configuration issue on the controller for the ext-jwt-signer, or for the identity.

Common problems are: "ID" token vs "Access" tokens. Some IdPs just refuse to return access tokens that are externally verifiable (aka a JWT) so you're forced to use an ID token instead. Often the audience doesn't match what you expect, for example it'll have a trailing slash on it where it MUST NOT have a trailing slash. Sometimes the controller's OS isn't updated and the CA bundle on that OS isn't updated enough for the openziti controller to connect to the IdP... There's many issues that can go wrong and it's unfortunately a tricky thing to get right... Hopefully you can find your issue. good luck!

Yes, I wasn’t expecting to authenticate using the controller in the cluster, only to verify the JWT signer can be found.

Either way, I made some progress. First, my controller uses a Let’s Encrypt cert and I had to download the root cert and use it with the –ca flag:
ziti --ca isrgrootx1.pem ops verify ext-jwt-signer oidc --username $ZITI_USER --password $ZITI_PWD --authenticate entra

Why would this be necessary? The Let’s Encrypt root cert is certainly in my OSes trust store. Why isn’t ziti using it?

With the –ca flag, I could authenticate successfully with the external provider and received a token back!

Unfortunately, it failed on the last hurdle:
INFO attempting to authenticate to controller with specified target token type: ACCESS
FATAL error authenticating with token: unable to authenticate to ``https://ziti-web.agibase.com:443/edge/management/v1``. Status code: 401 Unauthorized, Server returned: {
"error": {
"code": "INVALID_AUTH",
"message": "The authentication request failed",
"requestId": "uAqfBx8HA"
},
"meta": {
"apiEnrollmentVersion": "0.0.1",
"apiVersion": "0.0.1"
}
}

I’ll debug the payload next. Thanks for being so incredibly helpful!

ziti cli login works!

First, I added “email” as an additional claim to the token. Then I configured the external JWT signer to use email as the claims property and to use an external ID. Finally, I added my email to the identity external id. After making these changes running ziti --ca isrgrootx1.pem ops verify ext-jwt-signer oidc --username $ZITI_USER --password $ZITI_PWD --authenticate entra returns:

INFO attempting to authenticate to controller with specified target token type: ID
Token: xxxx-xxxx-xxxx-xxxx-xxxx
INFO login succeeded

Unfortunately, the tunneler still fails to authenticate:
(1710929)[ 0.642] INFO ziti-sdk:ziti.c:2016 version_pre_auth_cb() ztx[0] connected to Legacy controller ``https://ziti-api.mydomain.com:443/`` version v1.6.5(0efb60b21134 2025-07-09T15:15:36Z)
(1710929)[ 0.995] INFO tunnel-cbs:ziti_tunnel_ctrl.c:1128 on_ziti_event() ztx[/opt/openziti/etc/identities/entra.json//opt/openziti/etc/identities/entra.json] ext auth event received
(1710929)[ 0.995] INFO ziti-edge-tunnel:ziti-edge-tunnel.c:808 on_event() ztx[/opt/openziti/etc/identities/entra.json] ext auth: login_with_ext_signer
(1710929)[ 11.207] ERROR tlsuv:engine.c:821 openssl: handshake was terminated: error:00000005:lib(0)::reason(5)
(1710929)[ 11.207] ERROR tlsuv:tls_link.c:113 TLS(0x559fcc24ee38) handshake error error:00000005:lib(0)::reason(5)
(1710929)[ 11.207] ERROR tlsuv:http.c:188 handshake failed status[3]: error:00000005:lib(0)::reason(5)
(1710929)[ 11.207] ERROR ziti-sdk:external_auth.c:33 ext_oath_cfg_cb() ztx[0] OIDC provider configuration failed: (null)
(1710929)[ 11.207] ERROR tunnel-cbs:ziti_tunnel_ctrl.c:1098 on_ziti_event() ztx[/opt/openziti/etc/identities/entra.json//opt/openziti/etc/identities/entra.json] authorization flow cannot continue: (null)
(1710929)[ 11.207] INFO ziti-edge-tunnel:ziti-edge-tunnel.c:460 on_event() ztx[/opt/openziti/etc/identities/entra.json] context event : status is (null)
(1710929)[ 11.207] ERROR ziti-edge-tunnel:ziti-edge-tunnel.c:514 on_event() ztx[/opt/openziti/etc/identities/entra.json] failed to connect to controller due to (null)

I tried to add the Let’s Encrypt root certificate to /opt/openziti/etc/identities/entra.json but that didn’t fix the issue.

I start the tunnel manually with sudo ziti-edge-tunnel run --identity-dir /opt/openziti/etc/identities and then I try to auth with sudo ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/entra.json -p entra.

FWIW, I would gladly avoid using Let’s Encrypt as a CA as long as I can access ZAC on a public URL and I don’t have to ask users to manually configure DNS to point to the controller. I’m not sure exactly how.

Let me clarify: I have two domains configured for the controller: ziti-api.mydomain.com and ziti-web.mydomain.com. Ziti-api uses the controller CA, and ziti-web uses Let’s Encrypt as the CA.

If I modify my ziti cli config to point to ziti-api.mydomain.com, I can successfully log in with ziti ops verify ext-jwt-signer oidc --username $ZITI_USER --password $ZITI_PWD --authenticate entra without the –ca flag. This is one less point of friction compared to before, which is great! For all intents and purposes, I’m now only using ziti-web & Let’s encrypt for ZAC and it isn’t relevant to the cli and tunneler setup.

So ziti cli works great with an external jwt signer with the controller generated CA, ziti-edge-tunnel doesn't.

Enrolling a new identity has this warning that I haven’t reported on before:
(2938686)[ 0.000] WARN ziti-sdk:ziti_ctrl.c:336 internal_version_cb() ctrl[] ziti context is disabled(operation canceled)

Are you entirely sure that's true? It's kinda surprising how often linux distros need to update their ca store for LetsEncrypt in particular. Shortening the Let's Encrypt Chain of Trust - Let's Encrypt It's actually come up here in the forums before (and it's bit me personally before).

Try a curl https://your.controller:port/network-jwts and see if it connects properly to be sure. That's what the tunneler will effectively do. Here's mine:

$ curl https://ctrl.cdaws.clint.demo.openziti.org:8441/network-jwts
{"data":[{"name":"default","token":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImFmMDk1YzIxYTMyZWFjMzU3YmEwZGFkNzhlZTI5NjM1NTUwMzFhMzciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2VjMi0zLTE4LTExMy0xNzIudXMtZWFzdC0yLmNvbXB1dGUuYW1hem9uYXdzLmNvbTo4NDQxLyIsInN1YiI6Imh0dHBzOi8vZWMyLTMtMTgtMTEzLTE3Mi51cy1lYXN0LTIuY29tcHV0ZS5hbWF6b25hd3MuY29tOjg0NDEvIiwiYXVkIjpbIm9wZW56aXRpLWVucm9sbGVyIl0sImp0aSI6Ijk2NzIwMzAwLTRjNDQtNDJlZi1hMTA4LTljYWI4ZTYyNTg3YSIsImVtIjoibmV0d29yayIsImN0cmxzIjpudWxsfQ.RMyNo2E5Ym9GUz6pcLbG4BDYan87U8csseBCRDJjkt7VEAdGlR0URk1noOGHZM2gulA49ZgCESDQr0FJMJTrv4pxLi8aI34NAsUYqXRKax68LYNyBOgbHsEiMT_vLS0WOF6uqwi8ZbNec0A0PArQJFIirpRyA7h9Py7jK6xPJzmHNrfnQy3Y1hYJe787fRu8mKlsb3qZoTLje8pnNYzsICbaomJCAZhfiBKN3ytKIlyXQNK5F9uEv3c2Wc4PPIlG0y7dYRFke9NCKVK-R0fmK5-60wlkY8SjYkdkeHs3sNGuRStRsrsGTFX5LfxKNoD8_4Fh6LstPheliXzFsl_qmYHfqOEb7p-rcDxjL7kwRa9zZjoxSvpZCFh2k3NSMFMxNS78vo9fZWkiIBybe3FSDBl6WFKeQ7lvVV5xO0_IeM9aVgqq81UMVhlI3igLVrt9WWqDdrvQ1khBFTSG4kMzUBlecgNJidhoo7iJXPXMHP3j0zE3Dsa1FP7VBW56m4FM8Dfelf4NKZuGgStvsZo9pW-s2-6fMzOleR2-rMr_KT1A2FF66l1hiHjvgjoEFS2s-iekju5KaXTUthQr_8ezi786K3cs3sOIEnpj74WD1gzVqvpNyTbu_Mo6Iew1nbVr8lGAMubCkT8WzEI3Tz9gWJTths7SJxrLlmgYzNd0l_4"}],"meta":{}}

Be very careful here. If you have setup OpenZiti's alt_server_certs, you're very possibly overlapping the domains and that can lead to difficult to diagnose behavior and might be part of the problem here. I would start first by removing the alt_server_cert/domain from the equation entirely. Just to be sure. I would download the network jwt using zac/curl and then I would enroll an identity using that jwt and then authenticate successfully and make sure things work. That will eliminate some sources of confusion (imo). It's easy to use a wildcard domain cert and get this wrong (I've also done that myself lol). If you look at my current setup i DO use a wildcard domain cert for *.cdaws.clint.demo.openziti.org. I hang my stuff off that url BUT my controller and all my private PKI stuff is at ec2-3-18-113-172.us-east-2.compute.amazonaws.com just to avoid any confusion. You can configure it correctly to not overlap, it's just 'more effort' and shockingly easy as a human to goof up.... lol

That "ziti context is disabled" warn I think you can ignore safely for now. It just means one or more of your identies is disable for some reason. In the /opt/openziti/etc/identities folder there should be a config.json file. You probably have an identity you added/removed in there is my guess? Or it's somehow marked as Active:false: like this

$ grep -E "Active.*false" /opt/openziti/etc/identities/config.json
        "Active":false,
                "Active":false,
                "Active":false,
                "Active":false,

Are you entirely sure that's true? It's kinda surprising how often linux distros need to update their ca store for LetsEncrypt in particular.

My browser recognises Let’s Encrypt certs across many projects, including the one I generated for ZAC. I also confirmed that it is in my OS’s ca-bundle:
grep -A 10 "ISRG Root X1" /etc/ssl/certs/ca-bundle.crt
ISRG Root X1
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
...

Let me remove the alternative cert and try again.

There are only two keys labelled “Active” in config.json. The one for the entra identity is set to true.

{
        "Active":false,
        "Duration":9,
        "StartTime":"2025-09-30T11:07:41.955819Z",
        "Identities":[
                {
                "Name":"entra",
                "Identifier":"/opt/openziti/etc/identities/entra.json",
                "FingerPrint":"entra",
                "Active":true,
                "Loaded":false,
                 ...

Please use curl not your browser. Browsers have their own CA store. It's important to use curl to test something that will be similar to the ziti-edge-tunnel process.

I haven't removed the alt cert yet, but FWIW, curl to the OpenZiti controller CA returns:

 curl https://ziti-api.mydomain.com:443/network-jwts
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.

curl to the Let's Encrypt domain has a valid response:

curl https://ziti-web.mydomain.com:443/network-jwts
{"data":[{"name":"default","token":"..."],"meta":{}}

Is one supposed to import the controller-generated CA into your OS's trust store for the tunneler to work?

Good. If the curl to the LE protected domain works, that would indicate to me that the tunneler should work fine as well. The curl to the non-LE domain is expected to be self-signed so that's fine.

You do not need to import the self-signed PKI CA unless you really want to. There's generally no need for that other than bypassing the annoyance of browsers telling you it's a self-signed cert and 'not trusted'. Since you'll have a LE domain as well, I would not worry about that first curl failure.

That should rule out the LE cert being a problem blocking your tunneler. Looking back at your logs one other thing catches my eye right now:

/opt/openziti/etc/identities/entra.json//opt/openziti/etc/identities/entra.json

You should not need to supply the -ca flag to the tunneler when enrolling nor when trying to login.

Can you enable debug and then show the snippet of logs after you try to auth from ziti edge tunnel again? With the additional back/forth here, the thread is getting long and I'm losing context ... :slight_smile:

Also can you use three backslashes when pasting the logs or upload a file (i think discourse allows that) so the logs don't get goofy?

looks like this

:down_arrow: like that
image

Here are the logs for ziti-edge-tunnel run:

> sudo ziti-edge-tunnel run --identity-dir /opt/openziti/etc/identities
About to run tunnel service... .ziti-edge-tunnel-unwrapped
(4054369)[        0.000]    INFO ziti-sdk:utils.c:196 ziti_log_set_level() set log level: root=3/IN
(4054369)[        0.000]    INFO ziti-sdk:utils.c:167 ziti_log_init() Ziti C SDK version 1.6.6 @g22
(4054369)[        0.000]    INFO ziti-edge-tunnel:instance-config.c:72 load_tunnel_status_from_file
(4054369)[        0.000]    INFO ziti-sdk:utils.c:196 ziti_log_set_level() set log level: root=3/IN
(4054369)[        0.000]    INFO tunnel-sdk:ziti_tunnel.c:60 create_tunneler_ctx() Ziti Tunneler SD
(4054369)[        0.000]    INFO tunnel-cbs:ziti_dns.c:176 seed_dns() DNS configured with range 100
(4054369)[        0.000]    INFO ziti-edge-tunnel:ziti-edge-tunnel.c:1071 run_tunneler_loop() Loadi
(4054369)[        0.000]    INFO ziti-edge-tunnel:ziti-edge-tunnel.c:407 load_identities() loading
(4054369)[        0.000]    INFO ziti-edge-tunnel:ziti-edge-tunnel.c:950 make_socket_path() effecti
(4054369)[        0.009]    INFO ziti-edge-tunnel:resolvers.c:68 init_libsystemd() Initializing libsystemd
(4054369)[        0.009]    INFO tunnel-cbs:ziti_tunnel_ctrl.c:1181 load_ziti_async() attempting to load ziti instance[/opt/openziti/etc/identities/roche-oidc.json]
(4054369)[        0.009]    INFO tunnel-cbs:ziti_tunnel_ctrl.c:1188 load_ziti_async() loading ziti instance[/opt/openziti/etc/identities/roche-oidc.json]
(4054369)[        0.009]    INFO ziti-edge-tunnel:ziti-edge-tunnel.c:424 load_id_cb() identity[/opt/openziti/etc/identities/roche-oidc.json] loaded
(4054369)[        0.009]    INFO ziti-sdk:ziti.c:506 ziti_start_internal() ztx[0] enabling Ziti Context
(4054369)[        0.009]    INFO ziti-sdk:ziti.c:523 ziti_start_internal() ztx[0] using tlsuv[v0.35.0/OpenSSL 3.4.0 22 Oct 2024]
(4054369)[        0.009]    INFO ziti-sdk:ziti_ctrl.c:637 ziti_ctrl_init() ctrl[https://ziti-api.mydomain.com:443/] controller initialized
(4054369)[        0.009]    INFO ziti-sdk:ziti.c:600 ztx_init_controller() ztx[0] Loading ziti context with controller[https://ziti-api.mydomain.com:443/]
(4054369)[        0.463]    INFO ziti-edge-tunnel:resolvers.c:402 try_libsystemd_resolver() systemd-resolved selected as DNS resolver manager
(4054369)[        0.636]    INFO ziti-sdk:ziti.c:2016 version_pre_auth_cb() ztx[0] connected to Legacy controller https://ziti-api.mydomain.com:443/ version v1.6.5(0efb60b21134 2025-07-09T15:15:36Z)
(4054369)[        0.997]    INFO tunnel-cbs:ziti_tunnel_ctrl.c:1128 on_ziti_event() ztx[/opt/openziti/etc/identities/roche-oidc.json//opt/openziti/etc/identities/roche-oidc.json] ext auth event received
(4054369)[        0.997]    INFO ziti-edge-tunnel:ziti-edge-tunnel.c:808 on_event() ztx[/opt/openziti/etc/identities/roche-oidc.json] ext auth: login_with_ext_signer
(4054369)[      103.763]   ERROR tlsuv:engine.c:821 openssl: handshake was terminated: error:00000005:lib(0)::reason(5)
(4054369)[      103.763]   ERROR tlsuv:tls_link.c:113 TLS(0x563fffdc69b8) handshake error error:00000005:lib(0)::reason(5)
(4054369)[      103.763]   ERROR tlsuv:http.c:188 handshake failed status[3]: error:00000005:lib(0)::reason(5)
(4054369)[      103.763]   ERROR ziti-sdk:external_auth.c:33 ext_oath_cfg_cb() ztx[0] OIDC provider configuration failed: (null)
(4054369)[      103.763]   ERROR tunnel-cbs:ziti_tunnel_ctrl.c:1098 on_ziti_event() ztx[/opt/openziti/etc/identities/roche-oidc.json//opt/openziti/etc/identities/roche-oidc.json] authorization flow cannot continue: (null)
(4054369)[      103.763]    INFO ziti-edge-tunnel:ziti-edge-tunnel.c:460 on_event() ztx[/opt/openziti/etc/identities/roche-oidc.json] context event : status is (null)
(4054369)[      103.763]   ERROR ziti-edge-tunnel:ziti-edge-tunnel.c:514 on_event() ztx[/opt/openziti/etc/identities/roche-oidc.json] failed to connect to controller due to (null)

I notice the same path doubling you saw before, but only this one log line:

(4054369)[        0.997]    INFO tunnel-cbs:ziti_tunnel_ctrl.c:1128 on_ziti_event() ztx[/opt/openziti/etc/identities/roche-oidc.json//opt/openziti/etc/identities/roche-oidc.json] ext auth event received

I try to log in with:

sudo ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/roche-oidc.json -p entra

Unless you have other ideas, I will try to remove the LE domain and try using the controller-generated CA and cert.

I'm hitting the same wall without the LE domain.

Is enrolling an identity the same as creating an identity? They seem like distinct steps from the docs.

In an earlier post you said:

I don't see a new identity appear in the list of identities after running the above command. Should I?

What is strange is that I don't see anything in the controller logs after running:
sudo ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/entra.json -p entra

Just to make sure no other steps are assumed or required, here is the complete list of commands I run, just like you shared in an earlier post:

# this creates a new file in the identities dir
1. sudo ziti-edge-tunnel enroll --jwt ~/Downloads/ziti-entra.jwt --identity /opt/openziti/etc/identities/entra.json 
2. sudo ziti-edge-tunnel run --identity-dir /opt/openziti/etc/identities
3. sudo ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/entra.json -p entra

Any ideas?

You don't have any form of antivirus type of software on your machine do you? We have seen AV/endpoint protection software cause this type of problem in the past. Are you willing to send me an identity i can try with on my side? I don't need access to services. If so, DM me here on discourse using their PM feature. If ziti cli works, i would expect the ziti-edge-tunnel to work... I'll ask around to see if there is anything I'm overlooking.