Management API authentication

Hey, I’m working on some ansible modules to automate resource (identity, service, etc) creation through the REST API, and I’ve a question about the authentication part.

On the docs (Edge Management API Reference | OpenZiti) it says that we can auth either using user/pw, certificate or external jwt.

How the certificate or external jwt can be supplied to the API ?

What kind of certificate are we talking about in here ? A client cert made using the self signed CA ?

Hey @arslane ! That’s awesome. Do you want to authenticate with the ziti CLI or the Ansible uri module or something else? I’ll send some examples.

I’ll be authenticating through some Python code (probably requests lib). The idea is to grab the ztSession token so it can be used to do some other stuff (creating Identities, Services, etc).

Very cool. Since you’re authenticating with the Ziti mgmt API in Python, would you like to use the Python client library generated from the spec?

Here’s the authentication piece.

I’ll add more context here.

Oh didn’t knew such thing existed, this is gonna save me alot of time !

Glad to hear that! I’m really interested to see how you approach this with Ansible and I look forward to getting more familiar with the Py client myself. Are you thinking a separate task module for each type of Ziti resource, e.g., service, or separate module for each use case, e.g., simple-tunnel-service?

Here’s a generic cURL example for using a client authentication certificate to obtain a session token. You’ll see the Py client too has this cert method param.

curl \
    --request POST \
    --header 'content-type: application/json' \
    --data '{}' \
    --silent --show-error --fail \
    --cert zitiadmin-client.crt \
    --key zitiadmin-client.key \
    --cacert ziti-ca-bundle.pem \
    "https://mgmt.ziti.example.com/edge/management/v1/authenticate?method=cert"

The value of .data.token in the response must be included in subsequent requests as HTTP header zt-session (link to doc). I’ll page in someone that’s used the Py client more than me to offer a little more help.

Yeah I think 1 module for each resource type is a good idea, each module have to be idempotent. This would give much more flexibility

Hey Arslane,

We look forward to some help in this space. Please use the below code as a pseudo-example of how to use one of the generated Python API clients with cert based authentication:

Note, at the time we wrote the above, there existed python-prior OpenAPI generator. Let me bump the management api code to be the code generated by the new generator. They should be close enough that the original code should still work.

Edit: As a pseudo guide for these generated clients…please see: OpenAPI Python Clients

2 Likes

I wonder if generating the modules from the client lib for some of the Ziti resource types is possible. :thinking:

For the service module in particular it might be easier to have separate modules because services can take so many different forms. That way each module can have a simpler set of module args and there’s not so much branching logic in each service module, e.g., ziti_service_point_to_point_tunnel and ziti_service_subnet_tunnel, ziti_service_embedded, etc. Just a thought!

EDIT: I kept thinking and realized another way to avoid a spaghetti service module is to separate the Ziti configs from Ziti services cleanly (service module doesn’t manage Ziti configs). That way, there’s no abstraction like “point to point tunnel” at all, and every Ansible playbook has separate tasks to manage the Ziti configs and Ziti services. Am I expressing this clearly?

So we should have a module that manages configs and another to manage services ? That sounds great !

1 Like

Okay, I’ve bumped the code to use the python generator from the latest docker image. I’ll give it a test in a bit to make sure it will work for you and report back. Let me know if you have any issues.

I’ll say that you’ll need to give some thought as to how to create identities idempotently. Enrolling an identity consumes its JWT, so you might need to see if matching authenticator exists for the identity to determine if you should attempt to enroll, or maybe use the JWT enrollment files as markers of a certain sort.

I think 1 module per API resrouce unless it makes more sense to coalesce the functionality. configs are independent from services (for example, for service config overrides)

1 Like

Alright thanks !

Yeah, I'll have to do that for every resource actually.

Just thought about something, I’m using Hashicorp Vault to store my homelab secrets and to generate short-lived credentials to some APIs/Apps trough Vault custom engines. Has anyone worked on a Openziti Engine for Vault ? That could be huge

I know @TheLumberjack has done some work around Vault… not sure if it extends to an engine…

The identity_file is it a non-enrolled JWT file ?

It’s a JSON identity config file for an enrolled Ziti identity with admin permission. The easiest way to obtain this file is to

  1. create an admin identity to obtain an enrollment JWT
  2. enroll with the token to obtain the JSON identity config file

Here’s a ziti CLI examples to illustrate, in case you want to orchestrate this part of the process outside the Python module.

create admin identity

ziti edge create identity device admin-with-cert-auth \
  --jwt-output-file /tmp/admin-with-cert-auth.jwt \
  --admin

enroll admin identity

$ ziti edge enroll --jwt /tmp/admin-with-cert-auth.jwt 
INFO    generating 4096 bit RSA key                  
INFO    enrolled successfully. identity file written to: /tmp/admin-with-cert-auth.json 

authenticate with JSON identity config (cert authentication)

$ jq -r '.id.key' < /tmp/admin-with-cert-auth.json \
  | sed 's/^pem://' > /tmp/admin-with-cert-auth.key
$ jq -r '.id.cert' < /tmp/admin-with-cert-auth.json \
  | sed 's/^pem://' > /tmp/admin-with-cert-auth.cert
$ jq -r '.id.ca' < /tmp/admin-with-cert-auth.json \
  | sed 's/^pem://' > /tmp/admin-with-cert-auth.cacert

$ ziti edge login mgmt.ziti.example.com:443 \
    --username admin-with-cert-auth \
    --client-cert /tmp/admin-with-cert-auth.cert \
    --client-key /tmp/admin-with-cert-auth.key \
    --ca /tmp/admin-with-cert-auth.cacert 

Token: d34a3a91-f0ea-4f8f-a055-cbd8b45fb4b8
Saving identity 'default' to /home/kbingham/.config/ziti/ziti-cli.json

I was surprised not to find a way to use the enrolled JSON identity config file directly (enhancement request), so I parsed out the cert and key to verify it worked.

EDIT: added cacert to CLI cert auth example

1 Like

Correct, the link to the collection code from above just shows an example of unpacking the identity.json file into three (ca, cert, key) separate tempfile managed temporary files (the way it’s written, these get automatically cleaned up on program termination).

1 Like

Thanks to both of you !

On the identity creation schema Edge Management API Reference | OpenZiti the enrollment type basically describe how that identity will be authenticating to Ziti right ? and
ott → Generates the enrollment with the JWT → Once the JWT is enrolled we can use the identity
updb → same as ott + username/pw authentication, right ?
ottca → What does that do ?

You’re welcome!

You would use ottca instead of ott if you plan to supply the client authentication certificate from a 3rd party CA that you’ve verified with Ziti.

That’s right, ott enrollment means Ziti’s edge CA will issue the client auth certificate when you enroll with the token.

Also correct, updb is “the username/password database authentication mechanism”