During one of our NetBox training sessions, one of our students asked:
Can I restrict a user within NetBox to only see devices for a given site?
The short answer is yes. However, getting there involves a good understanding of the NetBox permission feature set, so today, we will look into NetBox permissions and how to configure NetBox to only permit a user to access devices for a single site.
What is NetBox?
NetBox is an open-source IPAM/DCIM, built upon Python Django. NetBox allows you to model and document your infrastructure and acts as a Source of Truth for your infrastructure/network.
NetBox provides a range of network automation features, allowing you to leverage the data via APIs, Ansible and/or Nornir.
NetBox Permissions 101
When granting access to what users (i.e NetBox admins) can access, NetBox provides various authentication settings. As expected, NetBox allows you to create users and groups. Against these users and groups, you can also define:
- Actions - View, add, change, delete.
- Objects - The various objects the actions can be applied to.
Though we gain a huge amount of flexibility with Actions and Objects, we need to leverage Constraints to gain further control and assign more granularity to what users should or should not access.
NetBox Constraints
What are NetBox Constraints?
Let's say we select that we have configured NetBox so that a user can read the object DCIM > Device. This means the user will be able to read all devices within NetBox. Therefore, Constraints provide the ability to further filter these NetBox objects, and in turn further restrict what the user has access to.
For example, going back to our original use case: filter the Device object for a given site to limit the devices the user sees.
Constraint Input
So the next question is:
How do I define these NetBox constraints?
NetBox permission constraints are defined using JSON, where we define the field name and lookup value that we want to use to constrain the given object.
For example. To define a constraint where we, the user, will only see SiteA for the DCIM > Site object, we would perform:
{"name": "SiteA"}
Great. Let's now dive a little deeper.
Under the NetBox hood, what is happening is we are performing a QuerySet filter on the given object, and it is this JSON input that is used as the input to the QuerySet filter.
What's a QuerySet?
In Django, a QuerySet
is used for querying the database using a high-level API instead of raw SQL. It's designed to be lazy, executing its SQL only when necessary. This optimises database interactions.
Therefore, knowing a little about QuerySets is essential as it makes it easier to define your Constraints.
Heres an example of performing a QuerySet filter directly within Python on a NetBox server to filter and return only the objects against the Device model with a name of spine1-nxos.
$ /opt/netbox/venv/bin/python manage.py nbshell
>>> from dcim.models import Device
# Perform a QuerySet on the Device model/object
>>> device = Device.objects.filter(name="spine1-nxos")
# See the result of the QuerySet
>>> device
<ConfigContextModelQuerySet [<Device: spine1-nxos>]>
# See the 1st entry within our QuerySet result
>>> device[0]
<Device: spine1-nxos>
# Explore the result. Using __dict__ to see the objects data attributes
>>> from pprint import pprint as pp
>>> pp(device[0].__dict__)
{'_name': 'spine00000001-nxos',
'_state': <django.db.models.base.ModelState object at 0x7f2dee9c8190>,
'asset_tag': None,
'cluster_id': None,
'comments': ",
'created': datetime.date(2021, 12, 14),
'custom_field_data': {},
'device_role_id': 1,
'device_type_id': 3,
'face': ",
'id': 1,
'last_updated': datetime.datetime(2022, 10, 8, 22, 14, 44, 892220, tzinfo=<UTC>),
'local_context_data': None,
'location_id': 1,
'name': 'spine1-nxos',
'platform_id': 4,
'position': None,
'primary_ip4_id': 3,
'primary_ip6_id': None,
'rack_id': None,
'serial': ",
'site_id': 1,
'status': 'active',
'tenant_id': None,
'vc_position': None,
'vc_priority': None,
'virtual_chassis_id': None}
Therefore, to perform this filter within the NetBox Constraints, we would of:
- Select the Object: DCIM > Device
- Provided a Constraint of
{"name": "spine1-nxos"}
QuerySets Filter Expressions
So far, we have seen that we can provide our QuerySet filter with a field name and the value that the field should be evaluated against. But what else can we do with these QuerySet filters? Here are some examples.
This table provides examples of filtering the Device
model using various query filter lookups based on the Device fields.
Query Filter | NetBox Device Model Example
|
Description |
contains | name__contains='spine1-nxos'
|
Field contains the phrase "spine1-nxos". |
icontains | name__icontains='spine1-nxos'
|
Field contains the phrase "spine1-nxos", case-insensitive. |
endswith | name__endswith='nxos'
|
Field ends with the phrase "nxos". |
iendswith | name__iendswith='NXos'
|
Field ends with the phrase "NXos", case-insensitive. |
This table demonstrates how to filter the Device
model based on fields from related models. For instance, the Device
might be related to Site
or DeviceType
, and we can query the Device
based on fields from these related models.
Query Filter | NetBox Device Model Example with Related Field
|
Description |
related_field | site__name='SiteA'
|
Device is related to, a Site with the name "SiteA". |
related_field__contains | site__name__contains='Site'
|
Device is related to, any Site with a name containing the phrase "Site". |
Now that we understand this, it gives us tremendous flexibility regarding our NetBox permissions. Therefore, let's turn our attention back to our original use case and configure NetBox to allow users to see devices from a single site only.
Configuration Example
Create User
First, we create our user - user1.
Create Group
Next, we create a group - Group-SiteA. And assign our user.
Create Permission Constraints
Next we:
-
Create our permissions and constraints via:
Admin > Authentication > Permissions > +. -
We will create 3 x permission entries, containing the data in the screenshot below.
Note: Pre NetBox 3.6.1, you will need to configure directly within the admin panel (i.e. /admin)
As you can see, we've created 3 different permissions, and here is the caveat with Constraints. Other objects have different fields. Therefore, our filter expression must be adjusted based on the model type. If not, the QuerySet will return an error to say it cannot find the field within the object.
For the eagle-eyed, you will have noticed that based on these permissions, our user will be limited to seeing a single site and only the devices within the site, but he will be able to see the components for all the site devices. However, You can lock this down further if required based on what we have covered.
Closing Comments
I hope this post is helpful and helps you understand the NetBox permission feature set. As you can see, NetBox provides a huge amount of functionality and flexibility when it comes to permitting and restricting user access, and I hope this post will help you configure this within your environment.