HOWTO: CMK with OIDC authentication

Rewrite of the original post due to changes/issues at 30-09-2023:
The post has been updated to reflect the current environment, and updated to the latest packages and configuration.

Disclaimer
This How-To is a “Work-in-Progress”, configured with CRE version 2.2.0p10, in a multisite (2 sites) configuration on a RockyLinux 9.2 box.

This is a proof of concept, providing a basic way of federative authentication via OpenID-Connect, and should (for now) not be used on production sites !

Base condition:

  • A working / configured site on the box.
  • wget command available on the box.

Prerequisites:

  • Get mod_auth_oidc rpm (current available version)onto the box (in a directory you prefer) , as root
    wget https://github.com/OpenIDC/mod_auth_openidc/releases/download/v2.4.14.3/mod_auth_openidc-2.4.14.3-1.el9.x86_64.rpm
  • install the rpm (as root)
    rpm -Uvh mod_auth_openidc-2.4.14.3-1.el9.x86_64.rpm

Configuration:

  • Change to / su to your omd site.
    omd su yoursitename

  • Edit etc/apache/conf.d/auth.conf
    – Add/edit the following settings, just below the ServerName directive, the complete auth.conf example is below.

# Set this to the Name of your Checkmk site, e.g.# Define SITE mysite
# Define SITE mysite

Define SITE my-cmk-monitoring-site-name

# ServerName from listen-ports.conf needs to be overwritten here
# and being set to the URL of the real server.

ServerName https://my-monitoring-host.my-domain-name.tld

# Load the OIDC module.
<IfModule !mod_auth_openidc.c>

        LoadModule auth_openidc_module /usr/lib64/httpd/modules/mod_auth_openidc.so

</IfModule>

# (Mandatory)
# URL where OpenID Connect Provider metadata can be found (e.g. https://accounts.google.com/.well-known/openid-configuration)
# The obtained metadata will be cached and refreshed every 24 hours.
# If set, individual entries below will not have to be configured but can be used to add
# extra entries/endpoints to settings obtained from the metadata.
# If OIDCProviderMetadataURL is not set, the entries below it will have to be configured for a single
# static OP configuration or OIDCMetadataDir will have to be set for configuration of multiple OPs.
#OIDCProviderMetadataURL <url>
# if your IDP does not have a configuration-endpoint then you will have to configure them manually as 
# described in the module's configuration-example
# https://github.com/zmartzone/mod_auth_openidc/blob/master/auth_openidc.conf

OIDCProviderMetadataURL https://your.IDP.domain.tld/.well-known/openid-configuration-endpoint

# Your OIDC Client ID - as provided by your IDP-Administrator
OIDCClientID Your_OIDC_Client_ID_pasted_HERE

# Your OIDC Client Secret - as provided by your IDP-Administrator
OIDCClientSecret Your_OIDC_Client_Secret_pasted_HERE

# (Mandatory) The name of the mod_auth_oidc generated cookie - binding it to the site-name - needed for multisite installations.
OIDCCookie "oidc_${SITE}"

# (Mandatory) Define the path in the cookie to the path of the site
OIDCCookiePath "/${SITE}/"

# Specify the names of cookies to strip from the incoming request so they are not passed
# on to the target application(s). This may prevent a large set of chunked session cookies to
# be sent to the backend. In that case you'd set it to (when using the default OIDCCookie setting):
#   mod_auth_openidc_session mod_auth_openidc_session_chunks mod_auth_openidc_session_0 mod_auth_openidc_session_1
# When not defined, no cookies are stripped.
#
# This ensures that the native CMK-cookie is not present when evaluating the Apache logic block for authentication, forcing it to be re-generated.
# Commented out due to issues with the CSFR token when dropping the CMK cookie and re-setting it.
#OIDCStripCookies "auth_${SITE}"

# OIDCRedirectURI is a vanity URL that must point to a path protected by this module but must NOT point to any content
OIDCRedirectURI https://your-cmk-host.domain.tld/${SITE}/secure/redirect_uri

# (Mandatory)
# Set a password for crypto purposes, this is used for:
# - encryption of the (temporary) state cookie
# - encryption of cache entries, that may include the session cookie, see: OIDCCacheEncrypt and OIDCSessionType
# Note that an encrypted cache mechanism can be shared between servers if they use the same OIDCCryptoPassphrase
# If the value begins with exec: the resulting command will be executed and the
# first line returned to standard output by the program will be used as the password, e.g:
#    OIDCCryptoPassphrase "exec:/bin/bash -c \"head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32\""
# (notice that the above typically only works in non-clustered environments)
# The command may be absolute or relative to the web server root.
#OIDCCryptoPassphrase [ [passphrase] | "exec:/path/to/otherProgram arg1" ]
OIDCCryptoPassphrase "exec:/bin/bash -c \"head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32\"
"
# Define the OpenID Connect scope that is requested from the OP (eg. "openid email profile").
# When not defined, the bare minimal scope "openid" is used.
# NB: multiple scope values must be enclosed in a single pair of double quotes
# NB: this can be overridden on a per-OP basis in the .conf file using the key: scope
#OIDCScope "<scope(s)-separated-by-spaces-and-enclosed-in-double-quotes>"
#
# CMK-specific - as CMK in this configuration is only looking for the username, and this is available thru the profile scope ( mapped from either nickname or preferred_username).
OIDCScope "openid profile"

# Set REMOTE_USER to the IDP-sent attribute nickname (part of the profile scope).
# this is the value that mod_authnz_ldap leverages as the first parameter after basedn.
# in the example below, REMOTE_USER = nickname = username attribute in LDAP
# transmitted in the profile scope as nickname.
# Valid other attribute would be preferred_username, depending on your IDP's config.
OIDCRemoteUserClaim nickname

# Define the X-Forwarded-* or Forwarded headers that will be taken into account as set by a reverse proxy
# in front of mod_auth_openidc. Must be one or more of:
#  X-Forwarded-Host
#  X-Forwarded-Port
#  X-Forwarded-Proto
#  Forwarded
# When not defined, such headers will be ignored.
#OIDCXForwardedHeaders <header>+
#
# CMK-specific, if you forget to add this, then you will end up redirected at port 5000 , which will produce an error, stripping the port off the url you *will* however be logged in and see your dashboard without additional authentication.
OIDCXForwardedHeaders X-Forwarded-Host

<Location /${SITE}>

        # Use OpenID-Connect auth for user-validation, if a session at the IDP exists, then proceed
        # (re-)generating the CMK Cookie previously stripped and whitelist also some other required URLs.

        # Bugfix - if using CRE 2.2.0p10 or lower there is a bug in the handling of sessions
        # To resolve this one needs to forcefully unset a header.
        # This is fixed in CRE 2.2.0p11. (confirmed)
        RequestHeader unset Authorization

    <If "! %{HTTP_COOKIE} =~ /^auth_${SITE}/ && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/register_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/run_cron.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/deploy_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/restapi.py' && \
        ! %{REQUEST_URI} -strmatch '/${SITE}/check_mk/api/*' && \
        ! %{QUERY_STRING} =~ /(_secret=|auth_|register_agent)/ && \
                ! %{REQUEST_URI} =~ m#^/${SITE}/(omd/|check_mk/((images|themes)/.*\.(png|svg)|login\.py|.*\.(css|js)))# ">

                Order allow,deny
                Allow from all

                AuthType openid-connect
                require valid-user

              RequestHeader set X-Remote-User "expr=%{REMOTE_USER}"

        # When OpenID-Connect auth fails, show the login page to the user. This should only happen,
        # if e.g. the mellon cookie is lost/rejected or if the IDP is misconfigured.
        # A failed login at the IDP will not return you here at all.
            ErrorDocument 401 '<html> \
              <head> \
                <meta http-equiv="refresh" content="1; URL=/${SITE}/check_mk/login.py"> \
              </head> \
              <body> \
                OIDC authentication failed, redirecting to login page. \
                <a href="/${SITE}/check_mk/login.py">Click here</a>. \
              </body> \
            </html>'
            </if>
    # This header is also needed after authentication (outside of the If clause)
    RequestHeader set X-Remote-User "expr=%{REMOTE_USER}"

</Location>

CMK web interface config:

  • You now have to activate under Setup → General → Global Settings- > User Interface → Authenticate users by incoming HTTP requests at Current settings the Activate HTTP header authentication option.

Restart your CMK site after making these configuration-changes

  • omd restart

Remember, to make this work the user authentication needs to be an existing Local user in CMK.

3 Likes

Additional to add to the config(if needed):

  • if you need to pull a specific (named) authentication-contract from your IDP add this.
# (Optional)
# Extra parameters that will be sent along with the Authorization Request.
# These must be URL-query-encoded as in: "display=popup&prompt=consent" or
# specific for Google's implementation: "approval_prompt=force".
# This is used against a statically configured (single) OP or serves as the default for discovered OPs.
# As an alternative to this option, one may choose to add the parameters as
# part of the URL set in OIDCProviderAuthorizationEndpoint or "authorization_endpoint"
# in the .provider metadata (though that would not work with Discovery OPs).
#
# Since version 2.3.11rc1 one can pass on query parameters from the request to the authorization
# request by adding e.g. "foo=#" which which will dynamically pull in the query parameter value
# from the request query parameter and add it to the authentication request to the OP.
#
# The default is to not add extra parameters.
# NB: this can be overridden on a per-OP basis in the .conf file using the key: auth_request_params

# For this PoC the configuration-item is used to request a specific authentication-contract.
# In sending a/the acr-property, with the value of a/the URI of a configured authentication-contract
# on the IDP the specifics of this authentication-contract will be honored.
# NB: if this is required the specifics will be provided by your IDP-Administrator.
OIDCAuthRequestParams acr=Your_defined contact-URI or name on the IDP

This will request the specified authentication-contract to be used.
If it does not exist, then the IDP will fall back to the default contract which is specified on the IDP.
(usually this is a username/password contract)

This is the equivalent in OIDC of the SAML parameter to specify the Authentication-context.

  • Glowsome
1 Like

Hi Glowsome,

Seems that i have a working Azure AD OpenID connection to my host/apache.

The problem i am currently facing seems to be with the internal redirects, when i write on the browser:
https://example.com/monitoring/check_mk/ i will be automatically redirected to https://example.com/monitoring/check_mk/login.py?_origtarget=index.py without being asked for the login mask.

In Case i write any location after /check_mk/123example i will be redirected to the login mask and the whole token process finishes correctly and I get the not found error.

currently using the following location directive:

<Location /${SITE}>

        # Use OpenID-Connect auth only in case there is no Checkmk authentication
        # Cookie provided by the user and whitelist also some other required URLs.

    <If "! %{HTTP_COOKIE} =~ /^auth_${SITE}/ && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/register_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/run_cron.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/deploy_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/restapi.py' && \
        ! %{REQUEST_URI} -strmatch '/${SITE}/check_mk/api/*' && \
        ! %{QUERY_STRING} =~ /(_secret=|auth_|register_agent)/ && \
        ! %{REQUEST_URI} =~ m#^/${SITE}/(omd/|check_mk/((images|themes)/.*\.(png|svg)|login\.py|.*\.(css|js)))# ">

                Order allow,deny
                Allow from all

                AuthType openid-connect
                require valid-user
</If>
</Location>

Any idea what needs to be adapted?

Thanks,
Fred

@techtuga

Seems i cut the confoguration-block short in my initial conf … so i posted it completely.

apart from the idp-data it should be a 1:1 usable copy.

Also when trying … always use an incognito/private browser session to avoid strange behaviour with cookies etc.

  • Glowsome

@Glowsome

perfectly working as mentioned. what a journey to get here :smiley:
many many thanks :beer:

Fred

Hi Glowsome,

If it happens that you are logged out you get to the normal login window.
Would be good to have a second login button with the option to login with SSO and maybe disabling the option for the normal login completely.

Br,
Fred

Hi @techtuga

As this whole way of setting/making OIDC happens is like a shell around CMK, and CMK is by itself not aware of the solution it will by default falback to its native login.

Untested:
In the auth.conf there is a redirect when authentication fails, or the login token expires.

 # When OpenID-Connect auth fails, show the login page to the user. This should only happen,
        # if e.g. the cookie is lost/rejected or if the IDP is misconfigured.
        # A failed login at the IDP will not return you here at all.
            ErrorDocument 401 '<html> \
              <head> \
                <meta http-equiv="refresh" content="1; URL=/${SITE}/check_mk/login.py"> \
              </head> \
              <body> \
                OIDC authentication failed, redirecting to login page. \
                <a href="/${SITE}/check_mk/login.py">Click here</a>. \
              </body> \
            </html>'

Maybe by changing that url to point again to the ${SITE}/Check_mk instead of login.py we close the loop.

  • Glowsome

This was hugely helpful, thank you!

I have a similar situation where OIDC login works fine, but coming back after an hour or so and refreshing the (still open) browser window redirects me to the standard CheckMK login screen instead.

I can see the browser performing the standard re-authentication with OIDC upstream, which works, as I see my identity in the apache logs for that request. However, at that point a 302 is issued which redirects me to the CheckMK login page.

I’ve tried the suggestion above without luck.

I’m guessing it’s related to login token expiry and re-authentication. Any suggestions? Thank you!

The redirect as said is done as the OIDC layer on the webserver (Apache) detects that a/the session with the IDP has expired.
This means that re-authentication at the IDP is mandatory to ‘let you back in’.
As to this 302 after authentication to the CMK login page, with my personal setup i was not able to reproduce that.

Do you have like a trace from the whole conversation which could give me insight ?

And with trace - i usually abuse SAML tracer plugin ( as it doesnt only record the SAML bits, but also the HTTP requests/answers), and can be exported in a json format.

  • Glowsome

Additional, i will need to re-setup my OIDC -implementation, as i had to scale down the number of boxes i had running with all types of Proof-of-Concept things, and moved up from RockyLinux 8 to 9.

OK, i have re-Setup my CMK-CE edition (2.2.0p8) on Rocky 9, with the above and am looking at the issue as described by @steveyken .

I am currently monitoring/investigating it to see if i can reproduce it.

  • Glowsome

First observation:

  • the session timeout on the IDP is configured for 60 minutes
  • the session at CMK site is (by default) 90 minutes

=> Session timeout on the application compared to the IDP should be equal or less to avoid having an application running with an internal session without having a session on the IDP.

Note: This goes for both OIDC aswell as SAML implementations !!!

Steps taken :
In CMK changed the idle session time to match your IDP session timeout (in my case: 1 hour) via Setup → Global settings → User interface → User Management → Login session idle timeout set to 1 hour ( same as session-timeout on IDP)
image

This is to align both timeouts, as we dont want to have an application session living longer then the session timout configured on the IDP.

Personal opinion
Potentially you want the session on the application just a bit shorter then the timeout on the IDP.
This is so that you are sure the session at the application was gone, but if you are within the timeout of the IDP-session it gets refreshed.
When both timed out then entforcement of re-authentcation with the IDP takes place.

Result:

Forcing an IDP-logout to make sure there is no (authenticated) session there CMK timed out on a refresh, and dropped me back to the IDP-login screen (forcing me to re-authenticate).
After a successful authentication i again was logged in and presented with the CMK-dashboard of the site - as expected without issues.

  • Glowsome

Thank you for going to so much trouble.

I’ve altered the login session timeout to match my IDP and will see if that helps.

I noticed the following in my apache error logs that may be relevant…

[Fri Sep 01 09:09:49.666318 2023] [auth_openidc:warn] [pid 942431] [client 127.0.0.1:33382] oidc_clean_expired_state_cookies: state cookie could not be retrieved/decoded, deleting: mod_auth_openidc_state_s8HI19gbCNstsMfWg, referer: https:///live/check_mk/index.py?start_url=%2Flive%2Fcheck_mk%2Fdashboard.py

[Fri Sep 01 09:09:49.666549 2023] [auth_openidc:error] [pid 939172] [client 127.0.0.1:33374] oidc_util_jwt_verify: parsing JWT failed: [src/jose.c:753: oidc_jwe_decrypt_impl]: encrypted JWT could not be decrypted with any of the 1 keys: error for last tried key is: crypto error [file: jwe.c, function: _cjose_jwe_decrypt_dat_a256gcm, line: 1269], referer: https:///live/check_mk/index.py?start_url=%2Flive%2Fcheck_mk%2Fdashboard.py

I’ve created a ‘test’ site which exhibits the same symptoms using ‘omd create’.

If I change the CheckMK session timeout to 5 mins, it makes my issue easier to reproduce.

  1. Open CheckMK
  2. Login using OIDC
  3. Simulate idle session timeout by closing browser window for 5 mins
  4. Open CheckMK again
  5. OIDC redirects and re-authenticates successfully as IDP session still exists
  6. CheckMK redirects to login screen

Here’s the key lines from my apache log (I’ve redacted some info). First line is just after successful IDP authentication which contains the code. You can see username@<redacted.com> indicating mod_auth_oidc successfully parses and sets REMOTE_USER. 2nd line redirects to check_mk/index.py which in turn issues a redirect to check_mk/login.py (no REMOTE_USER)

XXX.XXX.XXX.XXX - username@redacted.com [01/Sep/2023:13:33:50 +0800] "GET /test/secure/redirect_uri?code=.............
XXX.XXX.XXX.XXX - username@redacted.com [01/Sep/2023:13:33:51 +0800] "GET /test/check_mk/index.py?start_url=%2Ftest%2Fcheck_mk%2Fdashboard.py HTTP/1.1" 302 383 "https://login.redacted-idp.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188"
XXX.XXX.XXX.XXX - - [01/Sep/2023:13:33:51 +0800] "GET /test/check_mk/login.py?_origtarget=index.py%3Fstart_url%3D%252Ftest%252Fcheck_mk%252Fdashboard.py HTTP/1.1" 200 2158 "https://login.redacted-idp.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188"

I’ve noted that when I delete the “auth_<< site >>” cookie in my browser (in my case ‘auth_test’), and request the page again, then the login flow works properly and it sets a new working auth_test cookie.

I’ve checked my apache configuration, here’s the snippet relating to cookie detection…

    <If "! %{HTTP_COOKIE} =~ /^auth_test/ && \
          ! %{REQUEST_URI} = '/test/check_mk/register_agent.py' && \
          ! %{REQUEST_URI} = '/test/check_mk/restapi.py' && \
          ! %{REQUEST_URI} = '/test/check_mk/run_cron.py' && \
          ! %{REQUEST_URI} = '/test/check_mk/automation.py' && \
          ! %{REQUEST_URI} -strmatch '/test/check_mk/api/*' && \
          ! %{REQUEST_URI} = '/test/check_mk/deploy_agent.py' && \
          ! %{REQUEST_URI} = '/test/check_mk/ajax_graph_images.py' && \
          ! %{QUERY_STRING} =~ /(_secret=|auth_|register_agent)/ && \
          ! %{REQUEST_URI} =~ m#^/test/(omd/|check_mk/((images|themes)/.*\.(png|svg)|login\.py|.*\.(css|js)))# ">

So it looks related to an issue with auth_test cookie but not sure quite what it is.

For reference, I’m on a fresh Ubuntu Server 22.04 LTS installation with OMD and CheckMK v2.2.0p7.cre (RAW)

Thanks in advance for any suggestions you may have.

The issue is that both a CMK-site and mod_auth_oidc use a/their own (session-)cookie.
But neither CMK nor mod_auth_oidc are aware of the other one’s cookie.

And the logic-block in a site’s auth.conf only takes the session-cookie into account generated by CMK.

First changes made:
As we are looking at this from a federative standing the logic should revolve around the cookie set by mod_auth_oidc instead of the cookie generated by CMK itself.

To personalize (as in per-CMK-site for multisite servers) i have added the following directives to the etc/apache/conf.d/auth.conf

# (Mandatory) The name of the mod_auth_oidc generated cookie - binding it to the site-name
OIDCCookie "oidc_${SITE}"

# (Mandatory) Define the path in the cookie to the path of the site
OIDCCookiePath "/${SITE}/"

After this i changed the logic-block for the site-location to look for the mod_auth_oidc cookie (with the name defenied with the above statement OIDCCookie instead of the CMK-generated cookie.

    <If "! %{HTTP_COOKIE} =~ /^oidc_${SITE}/ && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/register_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/run_cron.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/deploy_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/restapi.py' && \
        ! %{REQUEST_URI} -strmatch '/${SITE}/check_mk/api/*' && \
        ! %{QUERY_STRING} =~ /(_secret=|auth_|register_agent)/ && \
        ! %{REQUEST_URI} =~ m#^/${SITE}/(omd/|check_mk/((images|themes)/.*\.(png|svg)|login\.py|.*\.(css|js)))# ">

I also modified the later part of the logic, for when OIDC somehow fails, it should not fallback to local authentication ( as it is in my case unwanted/undesired).

    # When OpenID-Connect auth fails, show the login page to the user. This should only happen,
    # if e.g. the oidc cookie is lost/rejected or if the IDP is misconfigured.
    # A failed login at the IDP will not return you here at all.
        ErrorDocument 401 '<html> \
          <head> \
            <meta http-equiv="refresh" content="1; URL=/${SITE}/check_mk"> \
          </head> \
          <body> \
            OIDC authentication failed, redirecting to login page. \
            <a href="/${SITE}/check_mk">Click here</a>. \
          </body> \
        </html>'
        </if>

After i made these changes i restarted both sites, and cleared all cookies on my browser to start out fresh.

Within the timeframe of a (site-)dashboard refresh i never dropped back/ got redirected to the local authentication page.

Just waiting on a valid timeout ( the 60 minutes configured) to see if in that case it still works out as expected.

Update : the result was not as expected, on a real timeout i faced the same issue with the (native) login popping up unfortunately.

Analysis showed that by doing so the native auth cookie was no longer being set …

So i re-evaluated my logic block to check for both:

 <If "! %{HTTP_COOKIE} =~ /^auth_${SITE}/ || \
        ! %{HTTP_COOKIE} =~ /^oidc_${SITE}/ && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/register_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/run_cron.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/deploy_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/restapi.py' && \
        ! %{REQUEST_URI} -strmatch '/${SITE}/check_mk/api/*' && \
        ! %{QUERY_STRING} =~ /(_secret=|auth_|register_agent)/ && \
        ! %{REQUEST_URI} =~ m#^/${SITE}/(omd/|check_mk/((images|themes)/.*\.(png|svg)|login\.py|.*\.(css|js)))# ">

After this change (and restarting omd site(s) i did manage to get back in, and no longer being dropped to the native login screen.

I will monitor it further and will let you know if this gives the results i/we expect from a session-timeout.

Update 2:

Seems under some conditions the logic-block is ignored, dropping one back to the native login screen ( even tho both cookies are present), but (most likely) the native CMK one has expired.

Taking a new approach in this forcing the cookie to be removed by adding a statement in auth.conf just above the logic-block.

       # force reauthentication by unsetting the auth_* cookie from CMK, and thus forcefully failing the conditions of logic block below.
        RequestHeader unset auth_${SITE}

Again, monitoring the new situation, when i have news to report back (or other readers of this thread respond) i will report back.

Update 3:

Again results are not as expected, so in comparing a 2.1.x (SAML) implementation compared to a 2.2.x site with OIDC the following is seen in the native auth_* cookies:

So somewhere a decision was made to change this, and from my standing i think this influences behaviour.

Still trying to find a solution for this, but this needs alot more research from my end.

  • Glowsome

Attempt 4:

Since above approach did not work, and everything still seems to revolve around the CMK cookie i have dug deeper into the mod_auth_oidc documentation.

To begin with the whole changes i made to the logic block have been reverted back to default.

So the logic block now looks like this:

 <If "! %{HTTP_COOKIE} =~ /^auth_${SITE}/ && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/register_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/run_cron.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/deploy_agent.py' && \
        ! %{REQUEST_URI} = '/${SITE}/check_mk/restapi.py' && \
        ! %{REQUEST_URI} -strmatch '/${SITE}/check_mk/api/*' && \
        ! %{QUERY_STRING} =~ /(_secret=|auth_|register_agent)/ && \
        ! %{REQUEST_URI} =~ m#^/${SITE}/(omd/|check_mk/((images|themes)/.*\.(png|svg)|login\.py|.*\.(css|js)))# ">

                Order allow,deny
                Allow from all

                AuthType openid-connect
                require valid-user

        RequestHeader set X-Remote-User "expr=%{REMOTE_USER}"

    # When OpenID-Connect auth fails, show the login page to the user. This should only happen,
    # if e.g. the oidc cookie is lost/rejected or if the IDP is misconfigured.
    # A failed login at the IDP will not return you here at all.
        ErrorDocument 401 '<html> \
          <head> \
            <meta http-equiv="refresh" content="1; URL=/${SITE}/check_mk"> \
          </head> \
          <body> \
            OIDC authentication failed, redirecting to login page. \
            <a href="/${SITE}/check_mk">Click here</a>. \
          </body> \
        </html>'
        </if>
# This header is also needed after authentication (outside of the If clause)
RequestHeader set X-Remote-User "expr=%{REMOTE_USER}"

</Location>

Then adding to the directives for mod_auth_openidc the following parameter to forcefully remove/strip the CMK auth cookie.
The idea is that every time the logic block is hit it will always trigger.

# Specify the names of cookies to strip from the incoming request so they are not passed
# on to the target application(s). This may prevent a large set of chunked session cookies to
# be sent to the backend. In that case you'd set it to (when using the default OIDCCookie setting):
#   mod_auth_openidc_session mod_auth_openidc_session_chunks mod_auth_openidc_session_0 mod_auth_openidc_session_1
# When not defined, no cookies are stripped.
OIDCStripCookies "auth_${SITE}"

The first results seems promising, but as always i want to see a full cycle and be sure.

So again, will keep you all posted about my findings.

  • Glowsome

Update this seems to catch the issue with the cookies, but it breaks the CSFR token.

Another thing to find a solution for… :frowning: so i have commented out the latest change again.

Update:
Still running into a dead end here in its current config.
So i made a compare on a 2.1.0p33 login.py and 2.2.0p8 … there is a lot of changes.

I have decided to setup a 2nd testing-box with 2.1.0p33 and mod_auth_openidc to investigate if the same behaviour is seen there.

  • Glowsome

I cannot find anything wrong with the CRE 2.1.0p33 version and the written howto for mod_auth_openidc.

Everything works as expected, no fallback to the native login screen or anything.

So the assumption is that CMK broke it themselves in the 2.2.0pX versions (as i have compared the login.py from both versions, and alot of rewriting was done here)

I am not qualified nor have the skills and time to jump into the source of CMK itself and figure out what is/needs to change to fix the cookie again.

One thing i did find out, is that even tho in https://checkmk.com/werk/11492 it is stated that a/the cookie should be SameSite=lax since then.

Observation however is that on a 2.2.0p9 site its set to SameSite=none.

Maybe someone at CMK itself can explain why this was reversed @martin.hirschvogel, @Sara ?

  • Glowsome

Thank you, this is very interesting. I will setup a test CRE 2.1 instance and try it out. I am running 2.2 in production so not sure if downgrading to 2.1 is a viable option. I can certainly try. Will also have a look at the python source changes but can’t promise anything!

Thanks so much for your help narrowing down the error to this point.

I can confirm the documented setup using CRE 2.1 also works fine for me. I am only seeing this issue in 2.2

Hey,

so I don’t have a OIDC setup at hand so I start with guessing :wink: :

We had a bigger session handling refactoring in 2.2 and it seems it broke some details we haven’t yet noticed.
One of those changes seems to be the SameSite cookie flag (mentioned here: HOWTO: CMK with OIDC authentication - #17 by Glowsome)
The other thing I noticed is that apparently we currently do not return the 401 status code if the authentication failed, so the ErrorDocument-Directive does not work as intended. This is just a suspicion. Could somebody of you with a working test environment add a line to a version file and check if that works?

The file: lib/python3/cmk/gui/wsgi/applications/checkmk.py

@@ -204,6 +204,7 @@ def _process_request(  # pylint: disable=too-many-branches
 
     except MKUnauthenticatedException as e:
         resp = _render_exception(e, title=_("Not authenticated"))
+        resp.status_code = http_client.UNAUTHORIZED
 
     except MKAuthException as e:
         resp = _render_exception(e, title=_("Permission denied"))

Thanks for your investigation so far, I will look into the Cookie issue meanwhile :slight_smile:

Regards
Max