Introduction

F5's role based access control (RBAC) mechanism allows a BIG-IP administrator to assign appropriate access privilages to the users [1]. 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 exisiting 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 [2], however, they are not exactly easy to follow. To fill the gap, this article describes a method to configure users and roles in much a finer manner.

Please note that the method is only applicable to iControl REST: It does not apply to Configuration Utility or ssh.

Setup

In this example, a user "foo" and a custom role "testRole" are created. The role allows the 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 [3]: You may want to consider using other methods such as F5 Python SDK [4].

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/usesr -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

From v13.1.0, a newly created user is automatically added to the above resource, 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. Just 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 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

References

[1] Manual Chapter: User Roles, AskF5
[2] BIG-IQ® Systems: REST API Reference version 4.5.0
[3] cURL
[4] F5 Python SDK Documentation