Interacting with the UNIS Security Model for GEMINI ==== Overview ===== Testing instance: https://monitor.incntre.iu.edu:9000 The UNIS service with AA enabled expects SSL connections and client/user certificates. The testing instance is configured to accept certificates signed by a trusted CA. Any GENI certificate signed by a CA in genica.bundle will work. It would be very easy to add additonal trusted CAs for testing purposes if necessary. The ability to verify the client/user certificates is what I will call "authenticated" (AuthN) use of the UNIS service. Without SSL, UNIS reverts back to the open model that we currently have running for GEMINI at http://unis.incntre.iu.edu:8888 The current model for "authorized" (AuthZ) use is handled by ABAC within UNIS. The AuthZ component is modular and can easily be disabled with UNIS settings. New AuthZ mechanisms with different policies could also be written and enabled. With all AuthZ modules disabled, any "authenticated" users have full read/write access to UNIS over SSL. The GEMINI-AuthZ module restricts user access to UNIS resources based on slice UUIDs. To associate a slice with a user, GEMINI-AuthZ adds a /register_slice endpoint to UNIS. This endpoint accepts a GENI slice credential, and after validating the credential against the user certificate, creates the default slice_admin ABAC roles, and stores the user and attribute certifacates. GEMINI-AuthZ then restricts read/write access to resources based on if the user has slice_admin attributes. Network resources added to UNIS are required to have a valid slice_uuid property. GEMINI-AuthZ also adds the /add_credential endpoint to UNIS allowing for new attribute certificates to be added to the ABAC store. Example usage is shown below. The current implementation accepts DER-encoded credentials created with creddy, but the interface could be extended to accept the XML-based delegated user credentials used in GENI (similar to how /register_slice accepts a GENI slice credential). Beyond the two endpoints introduced by GEMINI-AuthZ, the UNIS API remains unchanged. ===== Client Programs ===== A python UNIS client is available here: https://github.com/downloads/GENI-GEMINI/GEMINI/unis_client.py A browser can also be used to query UNIS as long as you import your user certificate. To convert GENI manifests into UNIS format, use unisencoder available here: https://github.com/downloads/GENI-GEMINI/GEMINI/unisencoder.tar.gz $ tar -zxf unisencoder.tar.gz $ cd unisencoder $ sudo python setup.py install creddy is available from http://abac.deterlab.net/ ===== Example Usage ===== The following output shows how one can use UNIS with existing GENI tools. Substitute your own credentials to test on your own. Note that GENI users would not typically interact with UNIS directly, but running GEMINI services and the GEMINI instrumentation process will. ::Create GENI slice:: $ omni.py -f pg createslice ezraslice $ omni.py -f pg renewslice ezraslice 20121207T00:00:00Z $ omni.py -f pg -a pg-utah createsliver ezraslice 2node.rspec Get the manifest: $ omni.py -f pg -a pg-utah listresources ezraslice -o INFO:omni:Writing to 'ezraslice-manifest-rspec-www-emulab-net-protogeniv2.xml' Get the slice credential: $ omni.py -f pg -a pg-utah getslicecred ezraslice -o INFO:omni:Retrieved slice cred for slice urn:publicid:IDN+emulab.net+slice+ezraslice INFO:omni:Wrote slice ezraslice credential to file 'ezraslice-cred.xml' Convert the manifest to UNIS: $ export PYTHONPATH=/opt/gcf/src $ unisencoder -t rspec3 --slice_cred ezraslice-cred.xml --out ezraslice.unis ezraslice-manifest-rspec-www-emulab-net-protogeniv2.xml Now the user "experiment" is created, we have the manifest in the correct format, and we're ready to interact with UNIS. ::Query UNIS without registered slice:: Using the same GENI user keypair as the omni commands above. $ ./unis_client.py https://monitor.incntre.iu.edu:9000/topologies GET ssl/kissel.pem ssl/kissel.key Enter PEM pass phrase: Server Response: 403 Forbidden Response data (55 bytes): ================================================================================ {"message": "'GEMINI: no registered slices for user'"} ================================================================================ Current policy prohibits interacting with UNIS unless the user has registered a slice. This also includes POSTing resources to UNIS. ::Register slice:: We can let UNIS know about a GENI slice given a slice credential. $ ./unis_client.py https://monitor.incntre.iu.edu:9000/register_slice POST ssl/kissel.pem ssl/kissel.key ~/geni/ezraslice-cred.xml Enter PEM pass phrase: Server Response: 200 OK Response data (0 bytes): ================================================================================ ================================================================================ A "200 OK" response indicates that the slice credential is now registered in UNIS and associated with the user identity (kissel). If you try to register a slice credential that doesn't belong to you: $ ./unis_client.py https://monitor.incntre.iu.edu:9000/register_slice POST ssl/encrypted_gpo.pem ssl/unencrypted_gpo.key ~/geni/ezraslice-cred.xml Server Response: 400 Bad Request Response data (62 bytes): ================================================================================ {"message": "'Client cert does not match credential owner!'"} ================================================================================ The request above tried to unsuccessfully register the slice credential using a different user certificate than the slice owner. This prevents another user from hijacking someone else's credential file and treating it as their own. You also can't register an expired slice credential, etc. ::Query with registered slice:: Let's try to look for available topologies: $ ./unis_client.py https://monitor.incntre.iu.edu:9000/topologies GET ssl/kissel.pem ssl/kissel.key Enter PEM pass phrase: Server Response: 200 OK Response data (2 bytes): ================================================================================ [] ================================================================================ The query to UNIS will complete now that the user has a registered slice, but the response is empty because UNIS does not contain any resources for that slice. ::Push slice resources to UNIS:: Let's push the manifest (in UNIS format) to UNIS. $ ./unis_client.py https://monitor.incntre.iu.edu:9000/topologies POST ssl/kissel.pem ssl/kissel.key ~/geni/ezraslice.unis Enter PEM pass phrase: Server Response: 201 Created Response data (1322 bytes): ================================================================================ { "selfRef": "https://monitor.incntre.iu.edu:9000/topologies/emulab.net_slice_ezraslice", "links": [ { "href": "https://monitor.incntre.iu.edu:9000/links/emulab.net_slice_ezraslice_link_lan0", "rel": "full" } ], "urn": "urn:publicid:IDN+emulab.net+slice+ezraslice", "ts": 1354113680393753, "id": "emulab.net_slice_ezraslice", "nodes": [ { "href": "https://monitor.incntre.iu.edu:9000/nodes/VM.ezraslice.emulab-net.emulab.net", "rel": "full" }, { "href": "https://monitor.incntre.iu.edu:9000/nodes/VM-0.ezraslice.emulab-net.emulab.net", "rel": "full" } ], "properties": { "geni": { "generated": "2012-11-26T19:46:11Z", "slice_urn": "urn:publicid:IDN+emulab.net+slice+ezraslice", "expires": "2012-12-03T14:15:44Z", "type": "manifest", "generated_by": "Flack", "slice_uuid": "e6037ca6-3965-11e2-9f72-001143e453fe" } }, "$schema": "http://unis.incntre.iu.edu/schema/20120709/domain#", "ports": [ { "href": "https://monitor.incntre.iu.edu:9000/ports/emulab.net_slice_ezraslice_interface_VM%3Aif0", "rel": "full" }, { "href": "https://monitor.incntre.iu.edu:9000/ports/emulab.net_slice_ezraslice_interface_VM-0%3Aif0", "rel": "full" } ] } ================================================================================ The slice topology is now in UNIS and you can query the resources as usual. For example, if you want the node object named "VM": $ ./unis_client.py https://monitor.incntre.iu.edu:9000/nodes?name=VM GET ssl/kissel.pem ssl/kissel.key ~/geni/ezraslice.unis name=VM Enter PEM pass phrase: Server Response: 200 OK Response data (1299 bytes): ================================================================================ [ { "$schema": "http://unis.incntre.iu.edu/schema/20120709/node#", "name": "VM", "selfRef": "https://monitor.incntre.iu.edu:9000/nodes/VM.ezraslice.emulab-net.emulab.net", "urn": "urn:publicid:IDN+emulab.net+slice+ezraslice+node+VM", "ts": 1354113680391641, "relations": { "over": [ { "href": "urn:publicid:IDN+emulab.net+node+pc509", "rel": "full" } ] }, "id": "VM.ezraslice.emulab-net.emulab.net", "properties": { "geni": { "exclusive": false, "component_id": "urn:publicid:IDN+emulab.net+node+pc509", "slice_urn": "urn:publicid:IDN+emulab.net+slice+ezraslice", "slice_uuid": "e6037ca6-3965-11e2-9f72-001143e453fe", "sliver_id": "urn:publicid:IDN+emulab.net+sliver+101759", "hosts": [ { "hostname": "VM.ezraslice.emulab-net.emulab.net" } ], "client_id": "VM", "sliver_type": { "name": "emulab-openvz" }, "component_manager_id": "urn:publicid:IDN+emulab.net+authority+cm" } }, "ports": [ { "href": "https://monitor.incntre.iu.edu:9000/ports/emulab.net_slice_ezraslice_interface_VM%3Aif0", "rel": "full" } ] } ] ================================================================================ ::Other users:: Another user (my GPO certificate) registers and pushes a slice (exact same 2 node topology as before): $ ./unis_client.py https://monitor.incntre.iu.edu:9000/register_slice POST ssl/encrypted_gpo.pem ssl/unencrypted_gpo.key ~/geni/gposlice-cred.xml $ ./unis_client.py https://monitor.incntre.iu.edu:9000/topologies POST ssl/encrypted_gpo.pem ssl/unencrypted_gpo.key ~/geni/gposlice.unis Now both slices have a node named "VM", but each user can only see their own resources: $ ./unis_client.py "https://monitor.incntre.iu.edu:9000/nodes?name=VM&fields=id" GET ssl/kissel.pem ssl/kissel.key ~/geni/ezraslice.unis name=VM&fields=id Enter PEM pass phrase: Server Response: 200 OK Response data (58 bytes): ================================================================================ [ { "id": "VM.ezraslice.emulab-net.emulab.net" } ] ================================================================================ $ ./unis_client.py "https://monitor.incntre.iu.edu:9000/nodes?name=VM&fields=id" GET ssl/encrypted_gpo.pem ssl/unencrypted_gpo.key name=VM&fields=id Server Response: 200 OK Response data (67 bytes): ================================================================================ [ { "id": "VM.gposlice.pgeni-gpolab-bbn-com.emulab.net" } ] ================================================================================ If a user tries to push someone else's slice resources: $ ./unis_client.py https://monitor.incntre.iu.edu:9000/topologies POST ssl/encrypted_gpo.pem ssl/unencrypted_gpo.key ~/geni/ezraslice.unis Server Response: 400 Bad Request Response data (77 bytes): ================================================================================ {"message": "'GEMINI: one or more network objects is not allowed for user'"} ================================================================================ The check is on the 'properties.geni.slice_uuid' field. The current GEMINI-AuthZ implementation requires that field as a unique slice identifier. ::Adding a credential:: Now we want to let some other identity (e.g. GEMINI service, portal, etc.) have access to the slice resources associated with the user. We will call this identity "alice" and then give it access to some of kissel's resources. We can easily create this identity as a proxy certificate (here valid for 7 days): $ openssl req -new -config csr.conf -out alice.csr -keyour alice.key $ openssl x509 -req -CAcreatesertial -in alice.csr -days 7 -out alice_ID.pem -CA kissel.pem -CAkey kissel.key -extfile csr.conf -extensions v3_proxy This new certificate is now valid at the UNIS service, meaning the CA chain is verified by the SSL implementation: $ ./unis_client.py https://monitor.incntre.iu.edu:9000/nodes GET ssl/alice_ID.pem ssl/alice.key Server Response: 403 Forbidden Response data (55 bytes): ================================================================================ {"message": "'GEMINI: no registered slices for user'"} ================================================================================ ...except that alice is not yet authorized to do anything. Now kissel can create an attribute certificate that says "let alice be a slice admin for my slice" (identified by slice_uuid): $ creddy --attribute --issuer kissel.pem --key kissel.key --role slice_admin_for_e6037ca6396511e29f72001143e453fe --subject-cert alice_ID.pem --out alice_sa_for_ezraslice_attr.der (NOTE: the slice_uuid field is in "text" form with hyphens in the manifest, but internally GEMINI-AuthZ uses the "hex" encoding without hyphens. The ABAC RT0 roles also use the latter form for compactness.) Now add this credential to UNIS: $ ./unis_client.py https://monitor.incntre.iu.edu:9000/add_credential POST ssl/alice_ID.pem ssl/alice.key ssl/alice_sa_for_ezraslice_attr.der Now that the attribute has been added, alice is effectively treated the same as kissel for that slice for 7 days: $ ./unis_client.py "https://monitor.incntre.iu.edu:9000/nodes?name=VM&fields=id" GET ssl/alice_ID.pem ssl/alice.key name=VM&fields=id Server Response: 200 OK Response data (58 bytes): ================================================================================ [ { "id": "VM.ezraslice.emulab-net.emulab.net" } ] ================================================================================ Note that alice will only have permissions to view/modify "ezraslice". If kissel registers more slices later on, alice will still only be able to see "ezraslice" because of the slice_admin role defined above. Additional credentials would have to be added for any other slices.