Skip to content

Commit

Permalink
Merge pull request #2740 from hmpf/saml-user
Browse files Browse the repository at this point in the history
Make mod-auth-mellon (SAML!) work for logins
  • Loading branch information
hmpf authored Dec 14, 2023
2 parents b601319 + bd6ebea commit c29ae09
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 5 deletions.
1 change: 1 addition & 0 deletions doc/howto/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Howtos
email
migrate-data
mod_auth_openidc_feide
mod_auth_mellon_feide
setting-up-logging
using_the_api
api_parameters
198 changes: 198 additions & 0 deletions doc/howto/mod_auth_mellon_feide.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
================================================================================================================
Authenticating with the apache plugin `mod_auth_mellon <https://github.com/latchset/mod_auth_mellon>`_ and Feide
================================================================================================================

Highly recommended: turn on debug logging while setting things up!

In ``/etc/nav/logging.conf`` in the section ``[levels]``, set ``nav.web.auth``
to ``DEBUG``. The relevant log to keep an eye on will depend on how apache2 is
running NAV, if it's using ``uwsgi`` the file is probably
``/var/log/uwsgi/nav/nav.log``.

Enabling the plugin on Debian
=============================

First check if the plugin is already installed and enabled::

$ sudo apache2ctl -M | grep mellon
auth_mellon_module (shared)

If it is, go straight to `Configuration`_.

If not:

Install the plugin::

$ sudo apt install libapache2-mod-auth-mellon

This should create the following files::

/etc/apache2/mods-available/auth_mellon.conf
/etc/apache2/mods-available/auth_mellon.load
/etc/apache2/mods-enabled/auth_mellon.conf
/etc/apache2/mods-enabled/auth_mellon.load

Enable with::

$ sudo a2enmod auth_mellon

Disable with::

$ sudo a2dismod auth_mellon

Files needed
============

Make a directory ``/etc/apache2/mellon`` on the NAV host. This will contain the
keys, certificates and metadata.

You need to download the idp metadata.

* `Test metadata <https://idp-test.feide.no/simplesaml/saml2/idp/metadata.php>`_
* `Production metadata <https://idp.feide.no/simplesaml/saml2/idp/metadata.php>`_

Save the file as ``/etc/apache2/mellon/idp-metadata.xml``.

The easiest way to create the sp metadata is with the command
``mellon_create_metadata``. Stand in ``/etc/apache2/mellon`` and run::

mellon_create_metadata https://DOMAINNAME https://DOMAINNAME/mellon

This will output a summary and create three files: ``https_DOMAINNAME.cert``,
``https_DOMAINNAME.key`` and ``https_DOMAINNAME.xml``. You can edit the
xml-file if needed.

Feide Kundeportal configuration
===============================

You will need to ask somebody with the correct access-rights at `Feide
kundeportal <https://kunde.feide.no>`_ for your organization to create
a service and a SAML 2.0 configuration for that service. Configurations are
locked to a specific NAV domain name and user group and cannot be shared. If
the domainname is updated the Feide and Apache2-configurations will need to be
updated as well.

The Feide admin will need:

* A name for the service, we recommend: "NAV: domainname" or "NAV: your organization".
* An url to redirect to after login, this is the domainname followed by
a relative url that is *not served by NAV*. We use ``/mellon`` in this howto.
* A copy of ``https_DOMAINNAME.xml``, to use in the SP metadata field.

Also, the ``userid-feide`` scope needs to be turned on at
*User information > Personal information*.

Apache2 Configuration
=====================

Apache virtual host configuration::

<Location />
.
.

AuthType mellon
Require valid-user

MellonEnable "auth"
MellonSecureCookie On
MellonUser "eduPersonPrincipalName"
MellonMergeEnvVars On
#MellonSessionIdleTimeout 28800 # auto logout after 8 hours
MellonSPMetadataFile /etc/apache2/mellon/https_DOMAINNAME.xml
MellonSPPrivateKeyFile /etc/apache2/mellon/https_DOMAINNAME.key
MellonSPCertFile /etc/apache2/mellon/https_DOMAINNAME.cert
MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
</Location>

<Location /mellon>
SetHandler none
AuthType mellon
Require valid-user

</Location>

<Location /index/logout>
AuthType None
Require all granted
</Location>

<Location /about>
AuthType None
Require all granted
</Location>

<Location /refresh_session>
AuthType None
Require all granted
</Location>

<Location /api>
AuthType None
Require all granted
</Location>

<Location /doc>
AuthType None
Require all granted
</Location>

Note the location block ``<Location />``. The "Require"-line replaces any other
"requires" already there. This locks down the entire site. We haven't found
a way with this plugin to do it any other way.

The second location block (``<Location /mellon>``) just needs to be a relative
url that is not in use by anything else, this is used by the plugin as its
endpoint.

The third location block (``<Location /index/logout>``) is the url that must be
visited before the plugin redirects to the IDP for logout.

The remaining location blocks are either public urls (``/doc``, ``/about``),
parts of NAV that has its own authentication system (``/api``), or must not be
under the control of the plugin for the web frontend to correctly function
(``/refresh_session``). If you have added extra pages or apps to the nav-server
that will not use the NAV auth system you need to mark their urls similarly.

Note that ``MellonSessionIdleTimeout`` has been commented out. Not all versions
of mod-auth-mellon support this configuration flag.

Restricting access by affiliation
---------------------------------

A Feide-user has one or more affiliations like "student", "employee" or "staff".
If it is necessary to restrict access by affiliation it is necessary to amend
the apache config file. Just below ``MellonMergeEnvVars`` add::

MellonRequire "eduPersonAffiliation" "staff" "other_affiliation"

Provided debug-logging has been turned on you can see exactly which
affiliations are available. Look for a line containing
"MELLON_eduPersonAffiliation".

There must be one or more quoted strings after "eduPersonAffiliation".

NAV configuration
=================

``webfront.conf``::

[remote-user]
enabled = yes
varname = REMOTE_USER
post-logout-redirect-url = /mellon/logout?returnTo=/

"mellon" in the ``post-logout-redirect-url`` points to the same place as the
``<Location /mellon>``-block in the apache configuration. This is hardcoded in
the SP metadata as well.

Gotchas
=======

When this is in use, local users like "admin" will no longer be available.
Therefore, either:

* *before* enabling the plugin create a user that will use OIDC to login then
set that user as admin
* *after* enabling the plugin set a user as admin via the CLI user script,
``navuser``
12 changes: 9 additions & 3 deletions doc/reference/web_authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,15 @@ originating site upon login/logout completion, the originating NAV URL can be
inserted using the placeholder string ``{}``. Example::

[remote-user]
enabled=yes
login-url: https://sso.example.org/login?nexthop={}
logout-url: https://sso.example.org/logout?nexthop={}
enabled = yes
login-url = https://sso.example.org/login?nexthop={}
logout-url = https://sso.example.org/logout?nexthop={}

``logout-url`` will set the link that the logout-button points to, the default
is "/index/logout".

Some remote user systems need to be visited *after* NAV has logged out the
user locally. The flag for that is ``post-logout-redirect-url``.


Relevant How Tos:
Expand Down
7 changes: 6 additions & 1 deletion python/nav/etc/webfront/webfront.conf
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ enabled = no
# These variables can be used to control which URLs NAV will redirect the user
# to when login or logout is requested. The string `{}` acts as a placeholder,
# where NAV will insert the URL the external idP should return the client to
# when login/logout is done.
# when login/logout is done. Changing *logout-url* changes what the
# Logout-button in the webpage header points to.
#login-url=https://sso.example.org/login/?nexthop={}
#logout-url=https://sso.example.org/logout/?nexthop={}

# Some remote user systems need to be visited *after* NAV has logged the user
# out. The default/unset value is "/"
#post-logout-redirect-url=/magic/logout?nexthop=/
8 changes: 7 additions & 1 deletion python/nav/web/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ def get_login_url(request):
return remote_loginurl if remote_loginurl else default_new_url


def get_post_logout_redirect_url(request):
default = "/"
redirect_url = remote_user.get_post_logout_redirect_url(request)
return redirect_url if redirect_url else default


def get_logout_url(request):
"""Calculate which logout_url to use"""
remote_logouturl = remote_user.get_logouturl(request)
Expand Down Expand Up @@ -152,4 +158,4 @@ def logout(request, sudo=False):
_logger.debug('logout: logout %s', account.login)
LogEntry.add_log_entry(account, 'log-out', '{actor} logged out', before=account)
_logger.debug('logout: redirect to "/" after logout')
return u'/'
return get_post_logout_redirect_url(request)
10 changes: 10 additions & 0 deletions python/nav/web/auth/remote_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class RemoteUserConfigParser(NAVConfigParser):
varname=REMOTE_USER
workaround=none
autocreate=off
post-logout-redirect-url=/
"""


Expand Down Expand Up @@ -145,6 +146,15 @@ def get_logouturl(request):
return get_remote_url(request, 'logout-url')


def get_post_logout_redirect_url(request):
"""Return a url (if set) to log out to/via a remote service
:return: Either a string with an url, or None.
:rtype: str, None
"""
return get_remote_url(request, "post-logout-redirect-url")


def get_remote_url(request, urltype):
"""Return a url (if set) to a remote service for REMOTE_USER purposes
Expand Down

0 comments on commit c29ae09

Please sign in to comment.