Skip to content

Instantly share code, notes, and snippets.

@nahun
Last active November 5, 2024 09:42
Show Gist options
  • Save nahun/5d4d715ca37a2465aaf59ab152413dc2 to your computer and use it in GitHub Desktop.
Save nahun/5d4d715ca37a2465aaf59ab152413dc2 to your computer and use it in GitHub Desktop.
NetBox with AzureAD OAuth

NetBox with AzureAD SSO using OAuth

This is an example setup with NetBox using AzureAD for authentication. It uses the Python Social Auth library.

Most of this was taken from this gist: https://gist.github.com/leoluk/16d91ec22d833945c7ac7ed2b3b05a27

This is written to support NetBox v2.10 to v3.0. I'll try to note differences for 2.9 or earlier.

NOTE: NetBox v3.1 added basic support for using social_auth so most of this is no longer required and can be placed in the configuration.py file.

AzureAD Setup

You will need an AzureAD App Registration (Service Principal) created for NetBox and create a client secret to use. You'll need the Client ID and Tenant ID as well.

Within the App Registration create app roles that will be used to assign users to specific groups. The app role value must be the same as the NetBox group name. See the Microsoft docs on app roles. This step can be skipped if you remove the references to the set_role method in custom_pipeline.py.

The set_username method in custom_pipeline.py will set the user's UPN attribute as the NetBox username. Set this to any attribute you want.

Requirements

Place the local_requirements.txt file in the NetBox root directory (/opt/netbox by default). See the NetBox docs. Install the Python package in the requirements file.

Rename default settings.py to upstream_settings.py

To overwrite the default settings.py and keep upgrades easier, move/rename the default settings.py to upstream_settings.py.

Modify the settings.py file

Enter your AzureAD information to the settings.py file.

Merge your configuration.py

The configuration.py provided here is just what is needed to setup this authentication. Merge it with your own configuration.py.

Copy Files

Copy each file to the $INSTALL_ROOT/netbox/netbox/ directory

  • custom_middleware.py
  • custom_urls.py
  • custom_pipeline.py
  • settings.py

Restart NetBox

systemctl restart netbox netbox-rq

LOGIN_REQUIRED = True
REMOTE_AUTH_BACKEND = 'social_core.backends.azuread_tenant.AzureADTenantOAuth2'
"""
Custom LOGIN_REQUIRED middleware which allows OAuth URLs.
"""
from netbox.middleware import LoginRequiredMiddleware # v2.10+
# v2.9 or earlier
#from utilities.middleware import LoginRequiredMiddleware
from django.conf import settings
class CustomLoginRequiredMiddleware(LoginRequiredMiddleware):
def __call__(self, request):
if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
if request.path_info.startswith('/oauth/'):
return self.get_response(request)
return super(CustomLoginRequiredMiddleware, self).__call__(request)
from django.contrib.auth.models import Group
class AuthFailed(Exception):
pass
def set_role(response, user, backend, *args, **kwargs):
try:
role = response['roles'][0]
except KeyError:
raise AuthFailed("No role assigned")
try:
group = Group.objects.get(name="{}".format(role))
except Group.DoesNotExist:
raise AuthFailed("Unknown role")
group.user_set.add(user)
def set_username(response, *args, **kwargs):
return { 'username': response['upn'].lower() }
"""
Override upstream urls.py for OAuth login
"""
from netbox.urls import *
urlpatterns = urlpatterns + [
path(r'oauth/', include('social_django.urls', namespace='social')),
]
social-auth-app-django
"""
Override the Netbox settings.py for customizations outside of configuration.py.
"""
import os
from netbox.upstream_settings import *
# OAuth monkey patching
MIDDLEWARE = [
'netbox.custom_middleware.CustomLoginRequiredMiddleware' if
x == 'netbox.middleware.LoginRequiredMiddleware' else x # v2.10+
# v2.9 or earlier
#x == 'utilities.middleware.LoginRequiredMiddleware' else x
for x in MIDDLEWARE
]
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_RESOURCE = '{{ AzureAD App Registration Client ID }}'
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = '{{ AzureAD App Registration Client ID }}'
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = '{{ AzureAD App Registration Client Secret }}'
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID = '{{ Azure Tenant ID }}'
LOGIN_URL = '/oauth/login/azuread-tenant-oauth2/'
ROOT_URLCONF = 'netbox.custom_urls'
SOCIAL_AUTH_POSTGRES_JSONFIELD = True
INSTALLED_APPS.append('social_django')
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'netbox.custom_pipeline.set_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
'netbox.custom_pipeline.set_role'
)
Copy link

ghost commented Feb 27, 2023

@nahun

Thanks for the reply.
Yes, I do have the REMOTE_AUTH_BACKEND set.

The only thing missing now is the custom pipeline to parse the data.
What I do not know right now is: How did you know the data structure of the response data? so it can be parsed and pass the data to netbox properly?
Up to this point I've only been using postman and the request library to see how the data is structured.
If I know how to check this part with AzureAD, then I can probably create my own pipeline.
Any input that would point me to the right direction would be very helpful.

@nahun
Copy link
Author

nahun commented Feb 27, 2023

If you run your NetBox instance in developer mode, you can see print() statements from the pipeline in your console. So I would just insert a method into the pipeline and print(response). Something like this:

## configuration.py
SOCIAL_AUTH_PIPELINE = (
    "social_core.pipeline.social_auth.social_details",
    "social_core.pipeline.social_auth.social_uid",
    "social_core.pipeline.social_auth.auth_allowed",
    "social_core.pipeline.social_auth.social_user",
    "social_core.pipeline.user.get_username",
    "social_core.pipeline.user.create_user",
    "social_core.pipeline.social_auth.associate_user",
    "social_core.pipeline.social_auth.load_extra_data",
    "social_core.pipeline.user.user_details",
    "netbox.custom_pipeline.map_groups"
)

## custom_pipeline.py
def map_groups(response, user, backend, *args, **kwargs):
    print(response)

Test logging in and it should output the data to your console.

Copy link

ghost commented Mar 13, 2023

Hi!

I am sorry for the late reply as I got too much workload at the moment.
But as a sign of thanks let me post my custom_pipeline here:

def map_groups(response, user, backend, *args, **kwargs):
    # print(response)
    try:
        role = response['roles'][0]
    except KeyError:
        role = None

    if 'netbox.superuser' in role:
        user.is_staff = True
        user.is_superuser = True

    elif 'netbox.read-write' in role:
        user.is_superuser = True

    else:
        pass

    user.save()
    groups = Group.objects.all()
    for group in groups:
        try:
            if group.name in role:
                group.user_set.add(user)
            else:
                group.user_set.remove(user)
        except Group.DoesNotExist:
            continue

Note that netbox.superuser, netbox.read-write and netbox.read-only are the role names/values that I set on Azure.

Thanks for all the help!

@phillf
Copy link

phillf commented Mar 27, 2023

I am getting the following error and I am not sure where to start to look. The SOCIAL_AUTH_AZUREAD_* variables are set.

AADSTS700016: Application with identifier 'None' was not found in the directory '101Schemes.io, LLC'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.

Copy link

ghost commented Sep 12, 2023

Going back here to see if it is possible to improve what we already have above.

@nahun
Did you happen to know how to create groups and permissions based on the roles that are already set on Azure SSO?
I can't seem to find any document that would help me set the user groups and permissions upon deployment, any ideas?

@nahun
Copy link
Author

nahun commented Sep 12, 2023

@rcalma This doc is out-of-date as mentioned before and at the top.

NOTE: NetBox v3.1 added basic support for using social_auth so most of this is no longer required and can be placed in the configuration.py file.

See this comment too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment