New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
External authentication #2328
Comments
It sounds like this should be folded into #1989. The idea also needs to be fleshed out a lot more:
|
Yep, sounds like those two could be solved together.
|
Grafana has a very useful way to authenticate users. I'm writing a proxy based in PHP and cURL and it's working very well. I'm using SimpleSAMLphp to authenticate users when they try to connect to Grafana through this proxy. Once authenticated, the proxy passes some user's data to Grafana through the headers. You can see some documentation from Grafana here: http://docs.grafana.org/tutorials/authproxy/. I'd like to see something similar into Netbox. 😃 |
I think the exact names should be configurable.
Additionally to checking the authentication cookie, Netbox would also need to check for the header. |
The user also needs to be assigned permissions and/or groups in order to have any write access.
A valid user must be created in the database. This is a requirement of the framework and schema. |
Maybe if the group which is forwarded matches an existing group in Netbox, we could work with those permissions? Otherwise just create the user with a default group (configurable). |
I'm on similar project, trying to use MS Kerberos Auth with Netbox. Globally it works with the following configuration :
In reverse-proxy configuration we can't deal with REMOTE_USER, because django is looking for a system variable, not an http header. This is the reason why apache rewrite it to KAUTHUSER http header. Forcing the authentication in Netbox ( The problem is when the user try to use the "logout" button. The redirect scheme is : I don't understand what is the problem... |
Not sure if typo on Github or in your config, but this should probably be |
Yes... error on keyboard... it was correct in my conf. |
Just to add on here, I would love if Duo was supported. |
You would only be able to do this using a OIDC, OAuth or SAML reverse proxy, for example the Keycloak Security Proxy. That's as far as the proposed implementation would go. |
I could use Shibboleth on Apache, doing the following changes in the NetBox code: I modified the file MIDDLEWARE = (
[...]
'utilities.middleware.CustomRemoteUser', # I added this line at the end of the list
)
[...]
AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.RemoteUserBackend', ] # I added this line at the end of the file I also added the following lines at the end of from django.contrib.auth.middleware import RemoteUserMiddleware
class CustomRemoteUser(RemoteUserMiddleware):
header = 'HTTP_REMOTE_USER' In the file LOGIN_REQUIRED = False You can also use another external authentication method, changing the header if necessary, and you can use redirections in Apache for These changes are based on @globoudou's comment. |
@rsepulvedacl I tried to reproduce your setup on v2.5.2 but for some reason it is not working at all. Am I missing a step from your description? I changed everything you mentioned and added the following to my nginx config (for now it's static for tests).
The user netbox exists and is able to login if I change everything back to the previous state. |
@awlx, try it again without the
You could be really passing I only tested this with Apache, using Shibboleth. The setting I used on Apache is the following:
You can also try with or without quotes around the user name. Good luck! |
@awlx, I don't know nginx very well, but I found some settings that could be useful here: |
@awlx , what did you end up getting to work? I need to do something similar but instead use OAUTH2. I was going to use the nginx oauth2 proxy and then make the changes to middleware/settings/configuration.py as above. It looks like that plugin pulls the UPN of the auth'd user to $upstream_http_x_auth_request_user which I'd assume we set as the REMOTE_USER header. Waiting on the app to be enabled by our directory team but curious if you ended up getting your use-case working. |
We ended up implementing a custom OAuth provider via social_django by using a custom settings.py file. If you want to try something similar, maybe this helps: https://gist.github.com/leoluk/16d91ec22d833945c7ac7ed2b3b05a27 Hoping for a proper upstream hook. |
very clean set of monkey patches. well done. almost see how this could indeed be generalized into a proper extensible authentication framework. |
@leoluk , curious, followed your gist using the AzureAD social Django backend but how do I actually trigger the backend to fire? I see the custom url as ^oauth/ but the login page doesn't seem to hit it and if I just goto localhost/oauth/ I get a 404 with not matching the pattern
feel like I'm close but just missing something obvious. my backends are set as follows: AUTHENTICATION_BACKENDS = (
'social_core.backends.azuread_tenant.AzureADTenantOAuth2',
'django.contrib.auth.backends.ModelBackend',
) |
Something like |
ah thanks, that was the tip I needed to look at the social Django code and discovered I should have used |
for some reason the login button doesn't seem to pick up the LOGIN_URL changes. No idea why but it just keeps with the regular login page. It works if I go direct to the URL but not if I click the login button. |
Should the modifications in #2328 (comment) work at 2.5.13? I have authentication at Apache working but Netbox does not seem to do anything with those changes - the user is not with admin rights and the "login" feature works as normal. |
@bluikko, the modifications should work. Please consider that this method won't make any changes to login and logout buttons. You should consider to create a redirection and/or use rewrite on Apache. It's been a long time since I implemented this, so I don't remember whether users are created automatically or not, but I remember that users won't be administrators automatically, unless you make some more improvements to this monkey patch or decide to make them administrators by hand. |
@rsepulvedacl I see. Thanks. Unfortunately it is a problem for me if the user permissions would need to be manually managed. I hope the developers would consider this enhancement request and integrate it properly with LDAP. |
I don't think bloating Netbox away from its core functionality is helpful or maintainable or a good use of development time, nor a wise decision as messing up authentication/authorisation is potentially very dangerous. Leave authentication to the web server front-ends which already have plenty of options available, in a mature state of development. For example, why on earth would someone devote the huge amount of effort to implementing SAML authentication in Netbox (#1677) instead of simply using e.g. Apache2 with mod_auth_mellon in front (as I do). Same for #118. The options are countless and, no matter how many are implemented in Netbox, there will always be calls for more, so why even go down this route? But, Netbox does needs to have better support for already-externally-authorised users:
I have implemented #2328 (comment) and it works fine for 1, but it should be a configuration option rather than an edit to core files that will be lost on update. Then the "loose ends" 2-4 should be implemented. |
The nice thing about Django is that is has a large ecosystem of high quality authentication backends which can easily be used with Netbox, like in the snippet above or my openshift-netbox role: https://github.com/leoluk/openshift-netbox/blob/master/openshift/netbox/openshift_auth.py These don't belong in the core, but a well-defined set of hooks for users to configure their own authentication would solve most of these use cases. In my particular case, these are the only modifications made:
I agree with @davidc that HTTP headers are the best approach for "generic" authentication. |
I have a working docker, kubernetes, nginx+oauth2_proxy and netbox related patches working so it is optionally enabled. How would you like these pull requests? One per feature? All in one? My works are based on v2.6.9 but I'm sure I could port them to develop. |
I think only the netbox -part is desired in this repo. A netbox PR including documentation on how to enable/configure it. Maybe a seperate issue in the 'netbox-docker' repo outlining how you did it with your k8s based setup? |
@CrackerJackMack I'd like to have your K8s components please to integrate into bootc/netbox-chart. This is a setup I would very much like to be able to use and a scenario I'd like to have in the chart. |
I've been going over the discussion here and in other issues, trying to determine the scope for this change. I think it boils down to two different approaches:
As the second case should be handled by #3351 (plugin support), this issue should focus on the first case: HTTP header-based authentication. Of course, a complete implementation of this feature entails not only user assignment, but user creation and group assignment (for granting of permissions) as well. There are some good references linked in the chat above, such as Grafana's implementation, but we need to thoroughly define the login/logout workflow before any serious progress can be made. |
It's surprising this hasn't gotten any more feedback in the past month. We really need to work on scoping this out fully and defining a working model for this to be implemented. Would anyone like to volunteer to take the lead? We need at least a few solid use cases for reference I think. |
If no objection here, I handle this to implement a header for proxy authentication. |
For this issue, agree to focus on the first one to authenticate with a proxy like apache or nginx in front of netbox. |
I don't have time for an official PR (sorry). But I'm are using https://github.com/pusher/oauth2_proxy There is also https://github.com/agoragames/nginx-google-oauth as well as https://github.com/cloudflare/nginx-google-oauth which have different headers. For hosted solutions there is https://teams.cloudflare.com/access/index.html and https://cloud.google.com/iap/ I'll see if I can post a gist of the patch this weekend. Maybe a good starting point for a more generic solution to support more auth proxies. edit: mispoke about django-simple-sso, this was a previous attempt |
@CrackerJackMack maybe I am wrong but at least some of your links seem to do authentication with external sources. I believe that this issue is about using the "preauthentication" headers set by the web server? For example I am using GSSAPI where the web server sets REMOTE_USER HTTP header and all NetBox would need to do for basic support is to check for presence of that header. It would be good if NetBox could be told to automatically give administrator rights to the users. Several web apps that I administer can split authentication and authorization so that authentication is done based on the HTTP headers and then authorization is done in the web app, for example by using LDAP to check group memberships of the user in REMOTE_USER. |
@bluikko You are only partially wrong I believe. They specifically exist to auth, and set HTTP headers. All the links I provided act as a trusted reverse proxy which you can trust that authentication and authorization was handled prior to setting and sending the HTTP headers. In the simplest, most crude examples:
Depending on the solution and if it's configurable with that solution, all of them set HTTP headers with we inherently trust. This choice is based entirely on this snippet from https://docs.djangoproject.com/en/3.0/howto/auth-remote-user/
The not-pr-worthy gist I promised. It also includes a more complicated nginx auth_request module setup to defer to oauth2_proxy using a side request. |
Hi @CrackerJackMack , shouldn’t the ‘user normalization’ be part of the frontend proxy’s job? I think it opens up a whole can of possible rules that end up beging maintained in Netbox on how to interpret the header? |
@sdktr Funny enough, the proxies would argue that it's the application's problem to clean up things if desired 😆 This is one of those annoying external authentication issues everyone likes to gloss over in their "Have them login with facebook!" blog posts. The issue isn't a issue with external authentication on a technical level, but more so that you have no idea how the login system works externally or what it will return in the headers and/or cookie data. There are some agreed upon standards to help adoption, but they aren't a hard rule. First, we have to choose an upstream auth service and see what header is does, or can send back to us. The auth service could do oauth2, SAML, openid connect, LDAP, ... We don't really know how users actually login to the service. Depending on the external authentication the headers could be a combination or just a single set of the following headers
Next problem. Are the username and email forwarded to us usable as is? Well that's really dependent on the downstream application (netbox & administrator). If usernames are intended to be emails then you just use X-Forwarded-Email and ignore -User for example. are the username and email prefixed as to help identify the upstream: I had a 3rd point but I can't recall what it is right now. |
I think it makes sense to implement this feature by introducing two new abilities. First, I propose implementing a custom subclass of Django's RemoteUserBackend. We can provide hooks into this class in the form of configuration parameters to control attributes such as:
The configuration parameter for this might look something like:
The remote authentication backend will be enabled only if The second component is to allow the injection of a custom backend class, to be inserted at the beginning of
I believe this strikes a nice balance between built-in support and custom extensibility, and should be fairly low-effort to maintain long-term. |
In the interest of moving the conversation along, I've introduced PR #4299. This largely implements what I described, however I opted to use individual configuration parameters rather than a dictionary for easier validation. The following can be used to enable and configure built-in remote user authentication: REMOTE_AUTH_ENABLED = True
REMOTE_AUTH_HEADER = 'HTTP_REMOTE_USER' # Default
REMOTE_AUTH_AUTO_CREATE_USER = True # Default
REMOTE_AUTH_DEFAULT_GROUPS = ['Network Engineering', 'Operations']
REMOTE_AUTH_DEFAULT_PERMISSIONS = ['extras.run_script'] This is sufficient to:
If further customization is needed, there is another setting which can be used to swap out the built-in backend with a custom one: REMOTE_AUTH_BACKEND = 'path.to.my.custom.backend' There was some discussion above around manipulating the user name. The backend class does provide a hook for that, but I'm not sure what degree of flexibility would be appropriate. I think at most we can allow the configuration of a regular expression that will match the desired portion of the username passed by the reverse proxy. This would allow us to, for example, strip the username portion from an email address, but anything more complicated than that would require a custom authentication backend. |
All of this sounds very good, I was wondering how feasible it would be to also provide a remote auth header name for groups or some sort of group mapping mechanism (similar to REMOTE_AUTH_HEADER, but like REMOTE_AUTH_GROUPS), or would the standard suggestion would be to use a remote_auth_backend for that? |
@chaomodus There are ways to provide list of groups in an HTTP header but in my opinion that would never be as good as separating authorization from authentication: the HTTP header would be authentication and NetBox would do authorization via LDAP for example. Anyhow it might be good to not try to widen the scope too much at this point - a simple external authentication via HTTP headers would be a good start. |
…cation Closes #2328: External user authentication
Environment
Ability to use external authentication via a proxy (eg. Keycloak-proxy, Nginx, Apache) by setting the appropriate headers. It was already described as a possibility here but I find no documentation or anything about this topic.
Proposed Functionality
It should be possible to pass authentication headers to netbox and either add a user automatically to the local database and make it possible to give the correct permissions in netbox or use the permissions passed via a header.
Use Case
This would be very useful because it wouldn't be necessary to implement and maintain dozens of different authentication mechanisms directly to Netbox but just pass the headers.
There is already an example for this in the wild but it requires some code changes and patching Netbox on every update is in my opinion not a good idea. So it would be really cool if something like this could be implemented in the codebase.
Example:
https://groups.google.com/d/msg/netbox-discuss/BTB8q8CzmrA/2BcnbectAQAJ
The text was updated successfully, but these errors were encountered: