How can the authenticate API prevent brute force attacks?

I found that by using the authenticate API, it is possible to obtain the zt_session token, which can manage the entire OpenZiti network. So how can I protect it from external attacks? One common attack vector is brute force or weak password guessing.

I noticed that the API supports three authentication methods: "password", "cert", and "ext-jwt". Can I disable two of these methods and only use one? Specifically, can I disable the "password" method? Or should I simply disable the edge-management service and manage it entirely internally?
Besides disabling it, are there any other good ways to address its security risks, such as enabling MFA, etc.? But MFA may not be ideal for automation use of the API.

Phew - so many questions packed into such a small paragraph!!! :slight_smile: Bear with me while I answer them all below :point_down:

OpenZiti is expected to terminate TLS on its own. You would want to find something that can prevent the common TCP-type attacks, syn flood, ack flood etc. Something more akin to a firewall for those style attacks.

Most identities are not password protected, the simplest answer to this is to:

  • make a new admin identity after the initial default admin is created: ziti edge create identity new-admin -o new-admin.jwt --admin
  • enroll the admin: ziti edge enroll new-admin.jwt
  • use the new-admin identity to auth to the overlay (ensuring it works): ziti edge login --file new-admin.json
  • use the new-admin to delete any updb authenticators: ziti edge delete authenticators where 'method = "updb"'

Now you only can login with certificate-based auth.

You could do that. You could entirely turn off the edge-management service when not needed and only turn it back on when you want it but that means you can't change the network. A more common technique is to "separate" that API and surface it only on 127.0.0.1 (or some private network address). Then you would only be able to access the management API if on the same network as the binding. I will often bind to 127.0.0.1 (instead of 0.0.0.0) and use ziti itself to access the mgmt api.

MFA is without a doubt "the best way" imo. Yes but you should also explore auth-policies and posture checks.

hth

1 Like

After deleting everything in updb, ZAC is unable to log in, and I have to re-add the identity using the ziti edge create authenticator updb identityName username pass command :sweat_smile: Currently, it seems that updb is only used on the management side. For example, if ZAC supported CA or JWT login, updb could be removed. It also seems that the Ziti Edge client (ziti-edge-tunnel) does not support registration via updb at the moment.

ZAC is supposed to support cert-based authentication but it's really nascent and I've not used it. If you add the cert to your browser, when you login it should "just work" but to be honest I haven't done this yet. @qrkourier might have the exact steps to follow.

You sound like the kind of person that would want to split the management api away from the public internet anyway to harden the controller a bit more. Once you do that, you can use ziti to access the management API. There are videos and forum posts on this topic if interested.

As for our clients, they don't support updb, correct. I hope they never do :). We are actively working on adding OIDC support to the tunnelers anyway, so updb will hopefully never be needed on tunnelers.

Yes, the basic idea is to import the client cert in your web browser and tell your web browser to use it with the Ziti console URL the first time you visit. Thereafter, you can click the LOGIN button to proceed without a username or password.

How to Authenticate to Ziti Console with a Client Cert

Here's an overview of the procedure that assumes the OS is Linux and uses the ziti CLI as much as possible for API and file operations.

  1. use CLI to create admin identity

    ziti edge create identity "admin2" --admin --jwt-output-file "admin2.jwt"
    
  2. use CLI to enroll the admin identity and create the identity config file containing the cert

    ziti edge enroll --jwt "admin2.jwt" --out "admin2.json"
    
  3. use CLI to extract the client cert and private key from the config as separate files

    This creates admin2.cert, admin2.key in current dir.

    ziti ops unwrap "admin2.json"
    
  4. work around a bug in the CLI that sets unusable POSIX filemodes

    chmod -c 0600 "admin2."*
    
  5. compose a keystore containing the client cert and private key

    If copying the keystore to another device for import, set an encryption passphrase when prompted by openssl to ensure confidentiality of the private key.

    openssl pkcs12 -export -in "admin2.cert" -inkey "admin2.key" -out "admin2.p12" -name "admin2"
    
  6. In your web browser, import the keystore file, e.g. "admin2.p12", in the browser settings where it says something like "my certificates" or "client certificates."

  7. Visit the Ziti console in your web browser and select the imported certificate when prompted by the web browser. Optionally, allow the web browser to remember your choice and always present the selected certificate when visiting a web site in the Ziti console's domain.

  8. With empty username and password form fields, click the LOGIN button to finish authenticating with the certificate you presented in the prior step.

Hi, can I ask:

How do you obtain the zt_session token with the authenticate API ? What is the procedure for this ?

Thanks

By far the easiest mechanism is to steal it from your terminal when using ziti edge login.

$ ziti edge login localhost:1280 -u admin -p admin -y
Cert #0 in the chain doesn't match
WARNING: server supplied certificate authority doesn't match cached certs at /home/cd/.config/ziti/certs/localhost
Server certificate chain written to /home/cd/.config/ziti/certs/localhost
Token: 91833c8c-96ce-44c5-956a-a88c1771311c
Saving identity 'default' to /home/cd/.config/ziti/ziti-cli.json

See the Token there? That's the zt_session you could use...

You could log into ZAC and get it from the ZAC using dev tools as well. You could use curl and actually authenticate to the API if you wish. Those are other ways to get it...

Is this the way to correctly do it with curl ? - im running this on the controller after logging in on CLI:

curl -k -X POST https://localhost:1280/authenticate?method=password
-H "Content-Type: application/json"
-d '{
"username": "admin",
"password": "myziti"
}'
~
~ The output I get has a token in it:

./token3.sh | grep -i token
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1503 100 1448 100 55 9675 367 --:--:-- --:--:-- --:--:-- 10020
{"data":{"_links":{"self":{"href":"./api-sessions/cm4hlg9g30ckm0bryzc0m98i3"},"sessions":{"href":"./api-sessions/cm4hlg9g30ckm0bryzc0m98i3/sessions"}},"createdAt":"2024-12-09T22:18:15.123Z","id":"cm4hlg9g30ckm0bryzc0m98i3","tags":{},"updatedAt":"2024-12-09T22:18:15.123Z","authQueries":,"authenticatorId":"8dsIgNDgmy","cachedLastActivityAt":"2024-12-09T22:18:15.132Z","configTypes":,"identity":{"_links":{"auth-policies":{"href":"./auth-policies/default"},"authenticators":{"href":"./identities/8dsIgOzt6/authenticators"},"edge-router-policies":{"href":"./identities/8dsIgOzt6/edge-router-policies"},"edge-routers":{"href":"./identities/8dsIgOzt6/edge-routers"},"enrollments":{"href":"./identities/8dsIgOzt6/enrollments"},"failed-service-requests":{"href":"./identities/8dsIgOzt6/failed-service-requests"},"posture-data":{"href":"./identities/8dsIgOzt6/posture-data"},"self":{"href":"./identities/8dsIgOzt6"},"service-configs":{"href":"./identities/8dsIgOzt6/service-configs"},"service-policies":{"href":"./identities/8dsIgOzt6/service-policies"},"services":{"href":"./identities/8dsIgOzt6/services"}},"entity":"identities","id":"8dsIgOzt6","name":"Default Admin"},"identityId":"8dsIgOzt6","ipAddress":"::1","isCertExtendable":false,"isMfaComplete":false,"isMfaRequired":false,"lastActivityAt":"2024-12-09T22:18:15.119Z","token":"fdc2026a-2282-42af-ba03-018a3f5777a2","expirationSeconds":1800,"expiresAt":"2024-12-09T22:48:15.119Z"},"meta":{}}

Yep that works when using UPDB (not cert) auth. I would do it a tiny bit differently, adding -s to curl as well and using jq, but it's the same mostly :slight_smile: :

$ curl -sk -X POST https://localhost:1280/authenticate?method=password \
        -H "Content-Type: application/json" \
        -d '{"username": "admin","password": "admin"}' \
        | jq -r .data.token