Skip to content

Commit

Permalink
Make it possible to use mod-auth-mellon for logins
Browse files Browse the repository at this point in the history
  • Loading branch information
hmpf committed Nov 21, 2023
1 parent 74a9c23 commit 1a5998a
Show file tree
Hide file tree
Showing 6 changed files with 208 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
175 changes: 175 additions & 0 deletions doc/howto/mod_auth_mellon_feide.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
================================================================================================================
Authenticating with the apache plugin `mod_auth_mellon <https://github.com/latchset/mod_auth_mellon>`_ and Feide
================================================================================================================

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"
#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.

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 1a5998a

Please sign in to comment.