Tunneler Identity gets its Authenticators Section deleted

v0.9.0 Tunneler
When I enroll a tunneler, its Identity configuration in DB looks this:

{
‘id’: ‘38d540bd-3bd6-414a-a485-46c7ad24e2f8’,
‘createdAt’: ‘2020-03-11T01:54:17.087617195Z’,
‘updatedAt’: ‘2020-03-11T01:54:17.087617195Z’,
‘_links’: {
‘edge-router-policies’: {
‘href’: ‘./identities/38d540bd-3bd6-414a-a485-46c7ad24e2f8/edge-routers’
},
‘self’: {
‘href’: ‘./identities/38d540bd-3bd6-414a-a485-46c7ad24e2f8’
},
‘service-policies’: {
‘href’: ‘./identities/38d540bd-3bd6-414a-a485-46c7ad24e2f8/identities’
}
},
‘tags’: {},
‘name’: ‘dariusz’,
‘type’: {
‘entity’: ‘identity-types’,
‘id’: ‘5b53fb49-51b1-4a87-a4e4-edda9716a970’,
‘name’: ‘Device’,
‘_links’: {
‘self’: {
‘href’: ‘./identity-types/5b53fb49-51b1-4a87-a4e4-edda9716a970’
}
}
},
‘isDefaultAdmin’: False,
‘isAdmin’: False,
‘authenticators’: {
‘cert’: {
‘cert’: ‘-----BEGIN CERTIFICATE-----\nMIIDzTCCAbWgAwIBAgIDAjD3MA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJV\nUzESMBAGA1UEBxMJQ2hhcmxvdHRlMRMwEQYDVQQKEwpOZXRGb3VuZHJ5MR8wHQYD\nVQQLExZaaXRpLURldmVsb3Blci1OZXR3b3JrMSkwJwYDVQQDEyBOZXRGb3VuZHJ5\nLCBJbmMuIEludGVybWVkaWF0ZSBDQTAeFw0yMDAzMTEwMTUzNTNaFw0yMTAzMTEw\nMTU0NTNaMDMxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpOZXRmb3VuZHJ5MQ8wDQYD\nVQQDEwZwb3Atb3MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQtLIYN65rnJtyzuCwz\n+1rDhZwTSzWdP1wXtzIDei33DNy6k0aqk2f3PkAgi+7enQfP/L16fy4oNG2Fb4Eo\nRbT4fhhtNScR0Knd6VtwW929V0iiOClhmUxOc/zYZZ7Ozm+jSDBGMA4GA1UdDwEB\n/wQEAwIEsDATBgNVHSUEDDAKBggrBgEFBQcDAjAfBgNVHSMEGDAWgBRX0qqTqCec\nBgb6Z0ANnBjKXxDJgDANBgkqhkiG9w0BAQsFAAOCAgEAf2Wk7Powbf+vG2SFqAqp\nIxpyVtF84ci9y/F0knEiVtNNU6tRUTVnJF0Xc2QET75O8DCTiOnQTlBv53pXKGSI\nsHnFRMnhcV4iRFXeB3Nptx7jwIYSTytNsiC6CDbfUH3l/bwsgP3VdKmlFO4UvHY2\nPWj+ZgnDItr9pDKc0XFyDG9FOSNS9uGJ+py8ejg8Sn4tRdidOTVmbvVBcbT/QIsW\ns5uBL35IyJPOi/Mtt6PHDJZmTWqWDOnXTy3hmZsjkHLUGVCH4kf+IF0yxEXz5qcI\nUc4sj/X89s2B+00wxf9WFKRsZVKyqywUf1V6kfBuUgp+83nTuTLgpZodNV2h8Bst\npsTM68Bl76tjox0Cg55LQE0L/eVziNliCKvxu1vsl8Lvf6bwSbdeyINlzm2QM2zV\n+hMUEok5Ly0lVZZiYgP4NUCnybVUBMeo9Y6jZl3Ip5oadKMPEK+eOYAm46PkTGbu\nzrZaeeZmc+mfLNYzkEZrXsB/e6nK2ep7S5lE08LOJY8uXw/2aDOsLhM/XcPVY2Sm\n7hiSWB12hbmWge1NeEi+q6hKAT6O0Z47VnM5JcfTwf4M3BI+i//ULpQEnjy/blXT\n21KSpE7a2OZxmSRwKTsOauy+F9U3Zll44BxkR8tJL+nixl9iM0L2TfPvuRpYkGmQ\nE/Kz+z+GC3RV57tzZAobPHM=\n-----END CERTIFICATE-----\n’,
‘fingerprint’: ‘1B:74:E2:E5:18:58:63:51:DE:B2:06:5C:9F:2F:83:97:17:E3:4F:0A’
}
},
‘enrollment’: {},
‘roleAttributes’: [‘dariusz’]
}

After I update/change its roleAtributes from dariusz to test, its cert/finger print gets deleted with the update.
{
‘id’: ‘38d540bd-3bd6-414a-a485-46c7ad24e2f8’,
‘createdAt’: ‘2020-03-11T01:54:17.087617195Z’,
‘updatedAt’: ‘2020-03-11T13:36:13.417086068Z’,
‘_links’: {
‘edge-router-policies’: {
‘href’: ‘./identities/38d540bd-3bd6-414a-a485-46c7ad24e2f8/edge-routers’
},
‘self’: {
‘href’: ‘./identities/38d540bd-3bd6-414a-a485-46c7ad24e2f8’
},
‘service-policies’: {
‘href’: ‘./identities/38d540bd-3bd6-414a-a485-46c7ad24e2f8/identities’
}
},
‘tags’: {},
‘name’: ‘dariusz’,
‘type’: {
‘entity’: ‘identity-types’,
‘id’: ‘5b53fb49-51b1-4a87-a4e4-edda9716a970’,
‘name’: ‘Device’,
‘_links’: {
‘self’: {
‘href’: ‘./identity-types/5b53fb49-51b1-4a87-a4e4-edda9716a970’
}
}
},
‘isDefaultAdmin’: False,
‘isAdmin’: False,
‘authenticators’: {},
‘enrollment’: {},
‘roleAttributes’: [‘test’]
}

After this, my session through the ingress router gets reset due to missing fingerprint.

Could you post back the steps you use to produce this issue? Which tools and mechanisms? You enroll with the enroller then invoke an endpoint? Can you post the jsons and endpoints hit?

I found the issue. I was using PUT method instead PATCH to update the role. WIth PATCH method, the fingerprint is not deleted. I can fix that with my requests calls, but I think ZAC is using PUT method to make the same updates. It may need to be updated if it is not already done

Mar 11 15:11:46 ip-172-31-9-127 ziti-console: Saving: {"name":"ziti-tunnel","type":"Device","isAdmin":false,"tags":{},"roleAttributes":["dariusz"],"enrollment":{"ott":true}}
Mar 11 15:11:46 ip-172-31-9-127 ziti-console: removing
Mar 11 15:11:46 ip-172-31-9-127 ziti-console: https://127.0.0.1:1280/identities/85363600-0269-4466-b36a-f574569fc678
Mar 11 15:11:46 ip-172-31-9-127 ziti-console: Saving As: PUT {"name":"ziti-tunnel","type":"Device","isAdmin":false,"tags":{},"roleAttributes":["dariusz"],"enrollment":{"ott":true},"permissions":[]}
Mar 11 15:11:46 ip-172-31-9-127 ziti-console: {"meta":{},"data":{}}
Mar 11 15:11:46 ip-172-31-9-127 ziti-console: https://127.0.0.1:1280/identities

Quick Q, can I just use PATCH instead of POST/PUT when creating or updating a resource? I have seen this in the past working with vendors to simply the developer experience.

PATCH cannot be used to create resources, so for create you must use POST.

PATCH is a more flexible PUT. PUT requires you to explicitly provide all update-able values. PATCH lets you target only the values you wish. The difference is not inherently useful for a human, but useful for UI developers who do not have UIs that are smart enough to know which fields have changed/not changed during user input and wish to ensure all the values that might need to be set are submitted or results in a trackable error.

POST for create is necessary as there are some fields that can only be set during a create and not changed via PUT/PATCH. In addition REST API patterns dictate this general HTTP verb mapping to CRUD operations:

  • POST = create
  • PATCH = partial update
  • PUT = full update
  • DELETE = delete
  • GET = read

There are further reasons, but I will only go into details if it is desired.

If you don’t mind, can you elaborate why in Ziti instance , you chose to use POST instead of PUT to create resources apart from being a best practice? Also, is the UUID the field you are referring to can only be created with POST?

can you elaborate why in Ziti instance , you chose to use POST instead of PUT to create resources apart from being a best practice?

It isn’t a POST over PUT situation for creating resources. It is clearer to say “the Edge API doesn’t support PUT for create”.

It is possible to support creation via PUT and POST at the same time for the Edge API or any other REST API. The difference between the two is idempotence and that for PUT to create, the client must supply a valid location (i.e. a UUID not in use) to store the new resource in addition to the fields necessary to define the object. See https://restfulapi.net/rest-put-vs-post/ for relatively clear and detailed explanation.

Most REST tooling assumes the conventions of POST for creating resources, especially when “where to store” (the UUID) the resource does not matter. I have seen some tools support PUT for creates, but most haven’t in my experience.

Another concern for PUTs to create is that depending on the domain, it can create friction between opinionated tools on “how a REST API should act.” Take, for instance, a resource that has a “type” field on it. The domain dictates that the “type” field is specified when created but cannot be updated/altered as its “type” dictates other references/logic. With this type of resource, you would need to define two models for PUT-to-create and PUT-to-update. Swagger 2.0 specifically does not allow this type of definition. Swagger 2.0 decided that REST APIs should not have multiple types of inputs to the same resource path with the same HTTP method. There are methods to work around this: different resource paths, different behaviors for various objects, ignoring values during an update, etc., but they all introduce inconsistencies and make the API less guessable.

As for supporting PUT to create resources - we can add that functionality if desired. However, the desire would have to outweigh the amount of ripple and rework it would create in solving some objects create vs. update domain requirements either by reworking them completely or cleverly working around it.

Also, is the UUID the field you are referring to can only be created with POST?

In the Edge API as it stands today, the client cannot define the UUID that is the id for the resource created. It also cannot be updated or altered. The API’s implementation provides the value, and the API’s logic would ignore any id value provided. This was an arbitrary decision as it avoid having to handle cases where a UUID is already in use, but not a major item to change.