iControl REST Fine-Grained Role Based Access Control
Introduction
F5's role based access control (RBAC) mechanism allows a BIG-IP administrator to assign appropriate access privileges to the users (see Manual Chapter: User Roles). For example, with the
operator
role, the user is specifically allowed to enable or disable nodes and pool members. The mechanism is generally the best way to manage users easily and consistently, however, finer access management may be required from time to time: e.g., allow only to view the stats of a predefined set of virtuals. Restricting access is especially important in iControl REST because users can remotely and directly access the system.
The existing roles and their access permissions are defined in
/mgmt/shared/authz/roles
and /mgmt/shared/authz/resource-groups
. You can add custom roles with custom permissions for particular users to these resources (iControl REST endpoints). The resources are described in BIG-IQ Systems REST API References, however, they are not exactly easy to follow. To fill the gap, this article describes a method to configure users and roles in a much finer manner.
Note
This method is only applicable to iControl REST: It does not apply to Configuration Utility or ssh.
The method relies on the resources for local users (i.e.,
/mgmt/tm/auth/user
and /mgmt/shared/authz/users
). It does not work for remote authentication mechanisms such as RADIUS or Active Directory.
Setup
In this example, a local user "foo" and a custom role "testRole" are created. The role allows users to
GET
(list) only the virtual server "vs". The access method and resource are defined in "testResourceGroup", that is referenced from the "testRole". No other operation such as PATCH
(modify) or DELETE is permitted. Also, no other resource such as another virtual is allowed. All the REST calls are done using curl: You may want to consider using other methods such as F5 Python SDK .
1. Create a new user
The following curl command creates the user "foo". The password is "foo", the description is "Foo Bar", and the role is "operator" for all the partitions.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/tm/auth/user -X POST -H "Content-Type: application/json" \ -d '{"name":"foo", "password":"foo", "description":"Foo Bar", "partitionAccess":[ { "name":"all-partitions", "role":"operator"} ] }'
You get the following JSON object:
{ "description": "Foo Bar", "encryptedPassword": "$6$jwL4UUxv$IUrzWGEUsyJaXlOB2oyyTPflHFdvDBXb6f3f/No4KNfQb.V6bpQZBgvxl3KkBDXGtpttej79DDphEGRh8d4iA/", "fullPath": "foo", "generation": 1023, "kind": "tm:auth:user:userstate", "name": "foo", "partitionAccess": [ { "name": "all-partitions", "nameReference": { "link": "https://localhost/mgmt/tm/auth/partition/all-partitions?ver=13.1.1.2" }, "role": "operator" } ], "selfLink": "https://localhost/mgmt/tm/auth/user/satoshi?ver=13.1.1.2" }
The role is important. When the access privileges conflict between the role and the fine grained RBAC, the stricter authorization is chosen. For example, if the RBAC is configured to allow PATCH or POST but the user's role is guest (no alteration allowed), the user won't be able to perform these methods.
You can create a user from tmsh or Configuration Utility if that's your preferred method.
2. (13.1.0 and later) Remove the user from /mgmt/shared/authz/roles/iControl_REST_API_User
/mgmt/shared/authz/roles/iControl_REST_API_User
From v13.1.0, a newly created local user is automatically added to
iControl_REST_API_User
, which grants access to iControl REST without any setup. To avoid assigning multiple permissions, you need to remove the user reference from /mgmt/shared/authz/roles/iControl_REST_API_User
.
First, get the data from
/mgmt/shared/authz/roles/iControl_REST_API_User
and redirect it to a file.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/shared/authz/roles/iControl_REST_API_User \ | python -m json.tool > file
Edit the file. The file contains a line for the user "foo" like this.
"name": "iControl_REST_API_User", "userReferences": [ { "link": "https://localhost/mgmt/shared/authz/users/foo" }, ....
Remove the line including the opening and ending curly brackets plus the comma. Save the file.
Overwrite the current data by putting the file to the endpoint.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/shared/authz/roles/iControl_REST_API_User -X PUT -d@file
3. Create a custom resource-group
A resource-group consists of a set of resources and methods. In this example, the resource-group is named "testResourceGroup", which allows a role to perform
GET
request to the resource /mgmt/tm/ltm/virtual/vs
. "testResourceGroup" is later used in the custom role.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/shared/authz/resource-groups -X POST -H "Content-Type: application/json" \ -d '{"name":"testResourceGroup", "resources":[ {"restMethod":"GET", "resourceMask":"/mgmt/tm/ltm/virtual/vs" } ]}'
You get the following JSON object.
{ "id": "fe7a1ebc-3e61-30aa-8a5d-c7721f7c6ce2", "name": "testResourceGroup", "resources": [ { "resourceMask": "/mgmt/tm/ltm/virtual/vs", "restMethod": "GET" } ], "generation": 1, "lastUpdateMicros": 1521682571723849, "kind": "shared:authz:resource-groups:roleresourcegroupstate", "selfLink": "https://localhost/mgmt/shared/authz/resource-groups/fe7a1ebc-3e61-30aa-8a5d-c7721f7c6ce2" }
Note that a resource-group entry is keyed by "id", not "name". The id is automatically generated.
As you can see, the resources field is a list of multiple objects, each containing the endpoint and the method. To add more permissions to the resource-group, add objects to the list.
4. Create a custom role
Create a role "testRole" with the user "foo" and the resource-groups "testResourceGroup". This makes the user to become a member of the role "testRole", hence allows the users to perform
GET
only to the /mgmt/tm/ltm/virtual/vs
.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/shared/authz/roles -X POST -H "Content-Type: application/json" \ -d '{"name":"testRole", "userReferences":[ {"link":"https://localhost/mgmt/shared/authz/users/foo"} ], "resourceGroupReferences":[{"link":"https://localhost/mgmt/shared/authz/resource-groups/fe7a1ebc-3e61-30aa-8a5d-c7721f7c6ce2"}]}'
Please note that the reference to the user foo in the userReferences field is different from Step #1: The user created was
/mgmt/tm/auth/user/foo
while the reference is /mgmt/shared/authz/users/foo
. The /mgmt/shared/authz/users/foo is automatically created. J
ust use /mgmt/shared/authz/users/
+ user name
.
Again, please observe that the resource-groups reference is not by name but by ID. See the selfLink in the step #3.
You get the following JSON object.
{ "name": "testRole", "userReferences": [ { "link": "https://localhost/mgmt/shared/authz/users/foo" } ], "resourceGroupReferences": [ { "link": "https://localhost/mgmt/shared/authz/resource-groups/fe7a1ebc-3e61-30aa-8a5d-c7721f7c6ce2" } ], "generation": 1, "lastUpdateMicros": 1521682931708662, "kind": "shared:authz:roles:rolesworkerstate", "selfLink": "https://localhost/mgmt/shared/authz/roles/testRole" }
Test
The following REST calls demonstrate that the user "foo" can
GET
the virtual "vs" but nothing else.
curl -D - -sku foo:foo https://192.168.226.155/mgmt/tm/ltm/virtual/vs | grep HTTP HTTP/1.1 200 OK curl -D - -sku foo:foo https://192.168.226.155/mgmt/tm/ltm/virtual | grep HTTP HTTP/1.1 401 F5 Authorization Required curl -D - -sku foo:foo https://192.168.226.155/mgmt/tm/ltm/virtual -X PATCH -d '{"test":"test"}' | grep HTTP HTTP/1.1 401 F5 Authorization Required curl -D - -sku foo:foo https://192.168.226.155/mgmt/tm/sys/version | grep HTTP HTTP/1.1 401 F5 Authorization Required
Cleanup
Removing users that are no longer used is a good admin practice. The cleanup procedure is described below (response JSON blobs are not shown):
1. Delete the resource-group. Use ID.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/shared/authz/resource-groups/fe7a1ebc-3e61-30aa-8a5d-c7721f7c6ce2 -X DELETE
2. Delete the custom role.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/shared/authz/roles/testRole -X DELETE
3. Delete the user.
You can perform this step from tmsh or Configuration Utility.
curl -sku admin:<pass> https://<mgmtIP>/mgmt/tm/auth/user/foo -X DELETE
- SteveGoldNimbostratus
BTY, Yes, the semicolon is added by Devcentral automatically
- SteveGoldNimbostratus
Hi Stephan, Good news, It WORKS! When you wrote : "It´s confusing me, that your role contains both references for localhost and a specific IP host of 192.168.203.11." it got me thinking.. I changed everything to "localhost with no IP address and it worked!!!! Thank you so much again! Steve
Nice! Thanks for the update.
- Olivier_GleizesNimbostratus
Hi ! As I understand this article, I can't give to a api user the permissions to POST or PATCH without give him the admin permissions. I try to give the rights to a user with the operator rôle to modify with iControl the gtm configuration (and only this part).
Indeed, the api user can bypass the fine grained permission of icontrol with the Configuration Utility and perform the actions manualy.
- Piotr_Bratkows3Nimbostratus
Hello,
How is it that you are using HTTP auth for non admin users? From 12.1 all authentication is done via token. Am I missing something?
Regards, Piotr
- Olivier_GleizesNimbostratus
Hello !
 
Maybe it's me misunderstanding something. A token is given to an user(*) and if the user don't have the admin role, he can't do any POST or PATCH API request. How you can create a user with admin role without give it the access to the configuration utility with the same role ?
 
(*) As I can see here : https://clouddocs.f5.com/api/icontrol-soap/Authentication_with_the_F5_REST_API.html
 
- BenJNimbostratus
I am inclined to agree with Olivier here. Unfortunately I don't have a unit to run tests on, but if I have understood the above article correctly, the fine-grained security control put in place here only applies to API requests.
As an example, Satoshi mentions giving access to the stats for a particular virtual server - which happens to be the exact use-case I have. To view stats normally, a user needs a role with administrative level privileges (which I can't make sense of, but that's another story!). Based on the example in the article, I am therefore concluding the role assigned to my user in the first step would need to be an administrative role. I would then create a new custom role as described above, attach the user to the custom role and apply the required restrictions (ie. GET only) via the custom resource-group mapped to the custom role.
I understand this works for API requests. However, my confusion is over how (or if) this new role has any effect on the new users' ability to login to the Configuration Utility? If the custom role has no bearing on login to the config utility, then I've just given a user GUI admin privileges when all I wanted was to let them see stats.
Very happy to be corrected if I've misunderstood something here.
Cheers.
- Piotr_Bratkows3Nimbostratus
@BenJ and Oliver
I managed to get this to work. As a non-admin user you need to have token and RBAC policy for example to modify pool member can be written in sucha a way:
{ "name":"poolModifyGroup", "resources":[ {"restMethod":"PATCH", "resourceMask":"/mgmt/tm/ltm/pool/*" }, {"restMethod":"PATCH", "resourceMask":"/mgmt/tm/ltm/pool/*/*" }, {"restMethod":"PATCH", "resourceMask":"/mgmt/tm/ltm/pool/*/*/*" }, {"restMethod":"GET", "resourceMask":"/mgmt/tm/ltm/pool/*" } ] }
If you only write
then it's not enough.{"restMethod":"PATCH", "resourceMask":"/mgmt/tm/ltm/pool/*" }
Hope that helps.
Regards, Piotr
- jonwest1_ukCirrus
Hi, thanks for the well written article. I've tested on versions 12.1.2 and 13.0.0 and I don't get any 401 responses. Foo is able to successfully make each call with a 200 response. I'm guessing this only works for 13.1.0 and above (like step 2), is that correct? If so is there any other way to restrict API calls in lower versions?
- Satoshi_Toyosa1Ret. Employee
Step 2 is a specific instruction for 13.1.0 and above. Skip it for older versions. iControl REST RBAC should work for 11.6 and above.