From 1a5998a4469c91eb2ac08b681fd55317e9fda099 Mon Sep 17 00:00:00 2001 From: Hanne Moa Date: Tue, 7 Nov 2023 15:36:34 +0100 Subject: [PATCH] Make it possible to use mod-auth-mellon for logins --- doc/howto/index.rst | 1 + doc/howto/mod_auth_mellon_feide.rst | 175 ++++++++++++++++++++++++++ doc/reference/web_authentication.rst | 12 +- python/nav/etc/webfront/webfront.conf | 7 +- python/nav/web/auth/__init__.py | 8 +- python/nav/web/auth/remote_user.py | 10 ++ 6 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 doc/howto/mod_auth_mellon_feide.rst diff --git a/doc/howto/index.rst b/doc/howto/index.rst index 51b7820eb3..4369b4f994 100644 --- a/doc/howto/index.rst +++ b/doc/howto/index.rst @@ -15,6 +15,7 @@ Howtos email migrate-data mod_auth_openidc_feide + mod_auth_mellon_feide setting-up-logging using_the_api api_parameters diff --git a/doc/howto/mod_auth_mellon_feide.rst b/doc/howto/mod_auth_mellon_feide.rst new file mode 100644 index 0000000000..da6bfeb4f9 --- /dev/null +++ b/doc/howto/mod_auth_mellon_feide.rst @@ -0,0 +1,175 @@ +================================================================================================================ +Authenticating with the apache plugin `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 `_ +* `Production metadata `_ + +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 `_ 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:: + + + . + . + + 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 + + + + SetHandler none + AuthType mellon + Require valid-user + + + + + AuthType None + Require all granted + + + + AuthType None + Require all granted + + + + AuthType None + Require all granted + + + + AuthType None + Require all granted + + + + AuthType None + Require all granted + + +Note the location block ````. 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 (````) 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 (````) 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 +````-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`` diff --git a/doc/reference/web_authentication.rst b/doc/reference/web_authentication.rst index 22a87e4ce0..0969a3a91d 100644 --- a/doc/reference/web_authentication.rst +++ b/doc/reference/web_authentication.rst @@ -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: diff --git a/python/nav/etc/webfront/webfront.conf b/python/nav/etc/webfront/webfront.conf index 9d6f428ecb..302aaa1774 100644 --- a/python/nav/etc/webfront/webfront.conf +++ b/python/nav/etc/webfront/webfront.conf @@ -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=/ diff --git a/python/nav/web/auth/__init__.py b/python/nav/web/auth/__init__.py index 142a0d4e5f..5da0222906 100644 --- a/python/nav/web/auth/__init__.py +++ b/python/nav/web/auth/__init__.py @@ -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) @@ -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) diff --git a/python/nav/web/auth/remote_user.py b/python/nav/web/auth/remote_user.py index fe853503da..83a9591bed 100644 --- a/python/nav/web/auth/remote_user.py +++ b/python/nav/web/auth/remote_user.py @@ -53,6 +53,7 @@ class RemoteUserConfigParser(NAVConfigParser): varname=REMOTE_USER workaround=none autocreate=off +post-logout-redirect-url=/ """ @@ -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