You can configure different SSO backends with remoteAuth
.
The implementation is based on Python Social Auth.
Depending on the chosen backend you need to configure different parameters.
You can leverage the extraConfig
value in conjunction with remoteAuth
.
By default the users do not have any permission after logging in.
Using custom auth pipelines you can assign groups based on the roles supplied by the oauth provider.
remoteAuth:
enabled: true
backend: social_core.backends.keycloak.KeycloakOAuth2
autoCreateUser: true
extraConfig:
- secret:
secretName: keycloak-client
- values:
SOCIAL_AUTH_PIPELINE:
[
"social_core.pipeline.social_auth.social_details",
"social_core.pipeline.social_auth.social_uid",
"social_core.pipeline.social_auth.social_user",
"social_core.pipeline.user.get_username",
"social_core.pipeline.social_auth.associate_by_email",
"social_core.pipeline.user.create_user",
"social_core.pipeline.social_auth.associate_user",
"netbox.authentication.user_default_groups_handler",
"social_core.pipeline.social_auth.load_extra_data",
"social_core.pipeline.user.user_details",
"netbox.sso_pipeline_roles.set_role",
]
extraVolumes:
- name: sso-pipeline-roles
configMap:
name: sso-pipeline-roles
extraVolumeMounts:
- name: sso-pipeline-roles
mountPath: /opt/netbox/netbox/netbox/sso_pipeline_roles.py
subPath: sso_pipeline_roles.py
readOnly: true
Additional resources are necessary (please note that the client ID is necessary in the custom pipeline script):
apiVersion: v1
kind: Secret
metadata:
name: keycloak-client
namespace: netbox
type: Opaque
data:
oidc-keycloak.yaml: |
SOCIAL_AUTH_KEYCLOAK_KEY: <OAUTH_CLIENT_ID>
SOCIAL_AUTH_KEYCLOAK_SECRET: <OAUTH_CLIENT_SECRET>
SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY: MIIB...AB
SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL: "https://keycloak.example.com/auth/realms/master/protocol/openid-connect/auth"
SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL: "https://keycloak.example.com/auth/realms/master/protocol/openid-connect/token"
SOCIAL_AUTH_JSONFIELD_ENABLED: true
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sso-pipeline-roles
namespace: netbox
data:
sso_pipeline_roles.py: |
from django.contrib.auth.models import Group
def set_role(response, user, backend, *args, **kwargs):
client_id = '<OAUTH_CLIENT_ID>'
roles = []
try:
roles = response['resource_access'][client_id]['roles']
except KeyError:
pass
user.is_staff = ('admin' in roles)
user.is_superuser = ('superuser' in roles)
user.save()
groups = Group.objects.all()
for group in groups:
try:
if group.name in roles:
group.user_set.add(user)
else:
group.user_set.remove(user)
except Group.DoesNotExist:
continue
remoteAuth:
enabled: true
backend: social_core.backends.gitlab.GitLabOAuth2
autoCreateUser: true
extraConfig:
- secret:
secretName: gitlab-client
- values:
SOCIAL_AUTH_PIPELINE:
[
"social_core.pipeline.social_auth.social_details",
"social_core.pipeline.social_auth.social_uid",
"social_core.pipeline.social_auth.social_user",
"social_core.pipeline.user.get_username",
"social_core.pipeline.social_auth.associate_by_email",
"social_core.pipeline.user.create_user",
"social_core.pipeline.social_auth.associate_user",
"netbox.authentication.user_default_groups_handler",
"social_core.pipeline.social_auth.load_extra_data",
"social_core.pipeline.user.user_details",
"netbox.sso_pipeline_roles.set_role",
]
extraVolumes:
- name: sso-pipeline-roles
configMap:
name: sso-pipeline-roles
extraVolumeMounts:
- name: sso-pipeline-roles
mountPath: /opt/netbox/netbox/netbox/sso_pipeline_roles.py
subPath: sso_pipeline_roles.py
readOnly: true
Additional resources are necessary (please note that the client ID is necessary in the custom pipeline script):
apiVersion: v1
kind: Secret
metadata:
name: gitlab-client
namespace: netbox
type: Opaque
stringData:
oidc-gitlab.yaml: |
SOCIAL_AUTH_GITLAB_API_URL: https://git.example.com
SOCIAL_AUTH_GITLAB_AUTHORIZATION_URL: https://git.example.com/oauth/authorize
SOCIAL_AUTH_GITLAB_ACCESS_TOKEN_URL: https://git.example.com/oauth/token
SOCIAL_AUTH_GITLAB_KEY: <OAUTH_CLIENT_ID>
SOCIAL_AUTH_GITLAB_SECRET: <OAUTH_CLIENT_SECRET>
SOCIAL_AUTH_GITLAB_SCOPE: ['read_user', 'openid']
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sso-pipeline-roles
namespace: netbox
data:
sso_pipeline_roles.py: |
from django.contrib.auth.models import Group
import jwt
from jwt import PyJWKClient
def set_role(response, user, backend, *args, **kwargs):
jwks_client = PyJWKClient("https://git.example.com/oauth/discovery/keys")
signing_key = jwks_client.get_signing_key_from_jwt(response['id_token'])
decoded = jwt.decode(
response['id_token'],
signing_key.key,
algorithms=["RS256"],
audience="<OAUTH_CLIENT_ID>",
)
roles = []
try:
roles = decoded.get('groups_direct')
except KeyError:
pass
user.is_staff = ('network' in roles)
user.is_superuser = ('network' in roles)
user.save()
groups = Group.objects.all()
for group in groups:
try:
if group.name in roles:
group.user_set.add(user)
else:
group.user_set.remove(user)
except Group.DoesNotExist:
continue
In order to enable LDAP authentication, please carry out the following steps:
- Set
image.tag
in your values to an image with LDAP support (e.g.v3.0.11-ldap
) - Configure the
remoteAuth
settings to enable the LDAP backend (see below) - Make sure you set all of the
remoteAuth.ldap
settings shown in thevalues.yaml
file
For example:
remoteAuth:
enabled: true
backend: netbox.authentication.LDAPBackend
ldap:
serverUri: 'ldap://domain.com'
startTls: true
ignoreCertErrors: true
bindDn: ''
bindPassword: ''
# and ALL the other remoteAuth.ldap.* settings from values.yaml
Note: in order to use anonymous LDAP binding set bindDn
and bindPassword
to an empty string as in the example above.