mod_auth_gssapi icon indicating copy to clipboard operation
mod_auth_gssapi copied to clipboard

Cannot set an env var (GSSPROXY_SOCKET) that mod_auth_gssapi will use

Open abompard opened this issue 1 year ago • 9 comments

I have multiple web applications running with different keytabs and gssproxy. I use gssproxy's sockets to differentiate between them. For that, I need to pass the GSSPROXY_SOCKET environment variable to mod_auth_gssapi, and I can't find a way to do it. I've tried:

  • setting one in /etc/systemd/system/httpd.service.d/, that works fine but I can't differentiate between the web apps running in the same apache
  • using SetEnv, which is documented not to work, and indeed it doesn't
  • using SetEnvIf, it doesn't work either (I tried SetEnvIf Host app.example.com GSSPROXY_SOCKET=/var/lib/gssproxy/app.sock)
  • using RewriteRule, and it doesn't work either. I tried RewriteCond %{HTTP_HOST} app.example.com \n RewriteRule .* - [E=GSSPROXY_SOCKET:/var/lib/gssproxy/app.sock]. I did check with increased logging that the rule was matching on the request.

Is there a way to pass an environment variable to mod_auth_gssapi, that is dependent on the virtualhost or even the request path? If so, it would be great if it could be added to the docs (here and/or in gssproxy's Apache doc). Thanks!

abompard avatar Jul 17 '23 10:07 abompard

The environment variable for GSSPROXY_SOCKET is observed by the gssproxy mechglue plugin in gssapi and mod_auth_gssapi is completely oblivious to that.

The env var is a process level variable and would require httpd to set this variable and fork before gssproxy client libs are ever used because gssproxy client libs tend to cache the file descriptor used to communicate to the server.

If you can force httpd to execute different virtualhosts as different users that will be easier to manage with a single socket and just configuration I believe.

If that will not work, I am afraid a gssproxy enhancement request need to be raised and some design done on that side.

simo5 avatar Jul 17 '23 12:07 simo5

Interesting, thanks. My app is a wsgi app and I can set it to run as a different user, but I think mod_auth_gssapi works before that, no? I tried with SuexecUserGroup, and adjusted the gssproxy config to distinguish on users and not socket, but it's still not picking up the right gssproxy service, it's picking up the one that is set for the generic apache user.

abompard avatar Jul 17 '23 15:07 abompard

can you check gssproxy to see if new connections are being made or if apahce opened a connection early and then child processes just keep using the same connections they inherit from the parent ?

simo5 avatar Jul 17 '23 16:07 simo5

Uhm actually the client will detect a fork or a change in euid/egid and reopen the socket ... so it seem like apache is running the authentication code before handing the request to a forked process ?

simo5 avatar Jul 17 '23 16:07 simo5

Sure! When I run curl on the second app, I get 3 connections to the "ipa-httpd" service, which is the service for the apache user, not the one for the user the second app is running as (fasjson):

Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [CID 14][2023/07/18 07:54:06]: Connection matched service ipa-httpd
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [CID 14][2023/07/18 07:54:06]: gp_rpc_execute: executing 6 (GSSX_ACQUIRE_CRED) for service "ipa-httpd", euid: 48,socket: (null)
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]:     GSSX_ARG_ACQUIRE_CRED( call_ctx: { "" [  ] } input_cred_handle: { "HTTP/[email protected]" [ { "HTTP/[email protected]" { 1 2 840 113554 1 2 2 } BOTH 85565 85565 } ] [ ......JJ..E.E...... ] 0 } add_cred: 0 desired_name: <Null> time_req: 4294967295 desired_mechs: { { 1 2 840 113554 1 2 2 } } cred_usage: BOTH initiator_time_req: 0 acceptor_time_req: 0 )
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]:     GSSX_RES_ACQUIRE_CRED( status: { 0 { 1 2 840 113554 1 2 2 } 0 "" "" [  ] } output_cred_handle: { "HTTP/[email protected]" [ { "HTTP/[email protected]" { 1 2 840 113554 1 2 2 } BOTH 85565 85565 } ] [ ......JJ..E.E...... ] 0 } )
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Total sent bytes: 158776
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Total received bytes: 151204
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Idle for: 0 seconds
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [CID 14][2023/07/18 07:54:06]: Connection matched service ipa-httpd
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [CID 14][2023/07/18 07:54:06]: gp_rpc_execute: executing 6 (GSSX_ACQUIRE_CRED) for service "ipa-httpd", euid: 48,socket: (null)
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]:     GSSX_ARG_ACQUIRE_CRED( call_ctx: { "" [  ] } input_cred_handle: { "HTTP/[email protected]" [ { "HTTP/[email protected]" { 1 2 840 113554 1 2 2 } BOTH 85565 85565 } ] [ ......JJ..E.E...... ] 0 } add_cred: 0 desired_name: <Null> time_req: 4294967295 desired_mechs: { { 1 2 840 113554 1 2 2 } } cred_usage: BOTH initiator_time_req: 0 acceptor_time_req: 0 )
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]:     GSSX_RES_ACQUIRE_CRED( status: { 0 { 1 2 840 113554 1 2 2 } 0 "" "" [  ] } output_cred_handle: { "HTTP/[email protected]" [ { "HTTP/[email protected]" { 1 2 840 113554 1 2 2 } BOTH 85565 85565 } ] [ ......JJ..E.E...... ] 0 } )
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Total sent bytes: 162276
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Total received bytes: 151208
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Idle for: 0 seconds
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Total received bytes: 156336
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Idle for: 0 seconds
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [CID 14][2023/07/18 07:54:06]: Connection matched service ipa-httpd
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [CID 14][2023/07/18 07:54:06]: gp_rpc_execute: executing 9 (GSSX_ACCEPT_SEC_CONTEXT) for service "ipa-httpd", euid: 48,socket: (null)
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]:     GSSX_ARG_ACCEPT_SEC_CONTEXT( call_ctx: { "" [  ] } context_handle: <Null> cred_handle: { "HTTP/[email protected]" [ { "HTTP/[email protected]" { 1 2 840 113554 1 2 2 } BOTH 85565 85565 } ] [ ......JJ..E.E...... ] 0 } input_token: [ ........H.......... ] input_cb: <Null> ret_deleg_cred: 1 )
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]:     GSSX_RES_ACCEPT_SEC_CONTEXT( status: { 851968 <None> 2529638947 "Unspecified GSS failure.  Minor code may provide more information" "Request ticket server HTTP/[email protected] not found in keytab (ticket kvno 1)" [  ] } context_handle: <Null> output_token: <Null> delegated_cred_handle: <Null> )
Jul 18 07:54:06 auth.tinystage.test gssproxy[40907]: [2023/07/18 07:54:06]: Total sent bytes: 162528

In the Apache error log I get a message because the principal for fasjson is not found in IPA's keytab, of course: GSS ERROR In Negotiate Auth: gss_accept_sec_context() failed: [Unspecified GSS failure. Minor code may provide more information ( Request ticket server HTTP/[email protected] not found in keytab (ticket kvno 1))]

My apache config for FASJSON:

WSGISocketPrefix /run/httpd/wsgi
WSGIDaemonProcess fasjson \
  user=fasjson group=fasjson \
  processes=4 threads=1 maximum-requests=500 \
  display-name=%{GROUP} socket-timeout=2147483647 \
  lang=C.UTF-8 locale=C.UTF-8
WSGIImportScript /srv/fasjson.wsgi \
    process-group=fasjson application-group=fasjson
WSGIScriptAlias /fasjson /srv/fasjson.wsgi
WSGIScriptReloading Off

<VirtualHost _default_:443>
ServerName fasjson.tinystage.test:443

ErrorLog logs/error_log
TransferLog logs/access_log
LogLevel info

[SSL stuff]

<LocationMatch "/fasjson/v[0-9]+/">
  AuthType GSSAPI
  AuthName "Kerberos Login"
  GssapiUseSessions On
  Session On
  SessionCookieName fasjson_session path=/fasjson;httponly;secure;
  SessionHeader FASJSONSESSION
  GssapiSessionKey file:/run/fasjson/session.key
  WSGIProcessGroup fasjson
  WSGIApplicationGroup fasjson

  GssapiImpersonate On
  GssapiDelegCcacheDir /run/fasjson/ccaches
  GssapiDelegCcachePerms mode:0660
  GssapiUseS4U2Proxy on
  GssapiAllowedMech krb5

  Require valid-user
</LocationMatch>
</VirtualHost>

And the gssproxy config:

[service/fasjson-httpd]
  mechs = krb5
  cred_store = keytab:/var/lib/gssproxy/fasjson-http.keytab
  cred_store = client_keytab:/var/lib/gssproxy/fasjson-http.keytab
  allow_constrained_delegation = true
  allow_client_ccache_sync = true
  cred_usage = both
  euid = fasjson

The HTTP and gssproxy configs for IPA are the default ones, I didn't change anything there.

abompard avatar Jul 18 '23 08:07 abompard

Ah I see, you are running the WSGI app as a user, but auth happens always in apache way before stuff is send down to the pipe talking to the python app.

Should probbably look into apahce-mtm-itk which can run the whole apache's virtualhost handling as a different user.

simo5 avatar Jul 18 '23 12:07 simo5

Cool, thanks for the tip! I tried with mod_suexec but that didn't work either. I'll try with mtm_itk and let you know.

abompard avatar Jul 18 '23 13:07 abompard

Alright, it does work with mpm-itk, but it requires adjustments it a few places and I'd rather not change IPA's environment too much, so I'll stop here and put IPA in its own VM. Thanks for the help though! It would be great if mod_auth_gssapi somehow had a way to select the gssproxy socket that did not involve an environment variable... :-)

abompard avatar Jul 18 '23 14:07 abompard

mod_auth_gssapi can't on its own, it would be a toggle somehow exposed by the gssproxy mechanism. But that mechanism is designed to be transparent to GSSAPI applications ... so ... not so easy.

This is how the thing works:

Httpd -> mod_auth_gssapi -> libgssapi -> [gssproxy-mechanism -> gssproxy-client] ===> gssproxy-daemon -> libgssapi -> krb5-mechanism

(Where -> means dynamic linking, and ===> means socket communication, and [] is the component that deals with the env var)

The thing that selects the socket is the gssproxy-mechanism/client part, which is a layered module that intercepts gssapi operations and shoves them into the gssproxy daemon. As you can see it is pretty deep into the chain, and wholly separated from what mod_auth_gssapi knows about.

There are ways though, in theory, feel free to open a RFE against gssproxy.

Thanks for the discussion it will be useful to others and for future plans.

simo5 avatar Jul 18 '23 14:07 simo5