Hi, I am interested in understanding the process of user authentication in the FreeIPA WebUI behind the scenes. However, the available information is scattered, and I am struggling to piece it all together. I have attached a diagram illustrating my current understanding, which I know is incorrect. To clarify my confusion, I will break down each point into specific questions:
https://tinyurl.com/freeipa-auth - link to the scheme in draw.io https://imgur.com/mQEvred - scheme image
1. So user is already sees before him FreeIPA login form. He types his login/password and submits the form
2. Request goes to /ipa/login_password. /ipa/login_password location by itself does not offer much:
<Location "/ipa/session/login_password"> Satisfy Any Require all granted </Location>
The interesting part is actually at /ipa location which handled by mod_auth_gssapi:
<Location "/ipa"> AuthType GSSAPI AuthName "Kerberos Login" GssapiUseSessions On Session On SessionCookieName ipa_session path=/ipa;httponly;secure; SessionHeader IPASESSION GssapiSessionKey file:/etc/httpd/alias/ipasession.key
GssapiImpersonate On GssapiDelegCcacheDir /run/ipa/ccaches GssapiDelegCcachePerms mode:0660 GssapiDelegCcacheUnique On GssapiUseS4U2Proxy on GssapiAllowedMech krb5 Require valid-user ErrorDocument 401 /ipa/errors/unauthorized.html Header always append X-Frame-Options DENY Header always append Content-Security-Policy "frame-ancestors 'none'"
Header unset Set-Cookie Header unset ETag FileETag None </Location>
Here is where things become vague for me. We see *GssapiImpersonate On* and *GssapiUseS4U2Proxy on*. ``` GssapiImpersonate This option can be used even if AuthType GSSAPI is not used for given Location or LocationMatch, to obtain service ticket for a user that was already authenticated by different module ``` and ``` GssapiUseS4U2Proxy Enables the use of the s4u2Proxy Kerberos extension also known as constrained delegation This option allows an application running within Apache to operate on behalf of the user against other servers by using the provided ticket (subject to KDC authorization). ``` So why do we need both? Isn't GssapiUseS4U2Proxy enough to issue service tickets on behalf of user and use them? Also, is there is no TGT actually obtained for the client's principal, only service tickets?
3. Here I have basic understanding that Apache need somehow to talk to KDC. It does it with help of mod_auth_gssapi, which implements GSS-API interface for Apache. Apache itself does not aware of any KDC or other auth provider present, cause there is no settings in apache config about it.
So let's say apache makes some gssapi function call to initiate exchange. - Does it start with SPNEGO negotioation, or directly "asks" for kerberos5 mechanism? (cause it is in the settings GssapiAllowedMech krb5).
We have a /etc/gss file with following settings:
[root@ipa-test conf.d]# cat /etc/gss/mech.d/gssproxy.conf # GSS-API mechanism plugins # # Mechanism Name Object Identifier Shared Library Path Other Options gssproxy_v1 2.16.840.1.113730.3.8.15.1 /usr/lib64/gssproxy/proxymech.so <interposer>
I guess by it we can confirm that gssproxy intercept all the gssapi calls on the host? Next look at the gssproxy config for the ipa:
[root@ipa-test ~]# cat /etc/gssproxy/10-ipa.conf #Installed and maintained by ipa update tools, please do not modify [service/ipa-httpd] mechs = krb5 cred_store = keytab:/var/lib/ipa/gssproxy/http.keytab cred_store = client_keytab:/var/lib/ipa/gssproxy/http.keytab allow_protocol_transition = true allow_client_ccache_sync = true cred_usage = both euid = apache
[service/ipa-api] mechs = krb5 cred_store = keytab:/var/lib/ipa/gssproxy/http.keytab allow_constrained_delegation = true allow_client_ccache_sync = true cred_usage = initiate euid = ipaapi
[service/ipa-sweeper] mechs = krb5 cred_store = keytab:/var/lib/ipa/gssproxy/http.keytab socket = /var/lib/gssproxy/ipa_ccache_sweeper.sock euid = ipaapi cred_usage = initiate
Let's review options for ipa-httpd and ipa-api services:
- allow_protocol_transition (This option controls whether s4u2self requests are allowed for the requesting client) Ok, understandable, we need it in conjuction with GssapiUseS4U2Proxy on of the Apache, otherways it won't work. Though it is present for service/ipa-api and not for the service/ipa-httpd. Why so?
- allow_client_ccache_sync This one is kind of vague for me. This option allows the proxy, in certain circumstances, to send back an additional option in the response structure of certain calls when it determines that a new ticket may have been added to the internal ccache. Clients can then replace their (encrypted) copy with the updated ccache.
So gssproxy injects option in response for a client (apache in our case?) to renew service tickets? What are the "certain calls"?
- allow_protocol_transition So, this one is enabled only for ipa-httpd. This option controls whether s4u2self requests are allowed for the requesting client. This is shows that for some reason for ipa-httpd service we only have s4u2self allowed type of requests and for ipa-api service we have s4u2proxy. Mm, why not both?
I guess those interaction between Apache and KDC through gssproxy are the most vague in all of this.
4. Somehow user getting autheticated. But there is no TGT, is it? So how is it done?
5. What next is gssapi calls are getting intercepted by gssproxy, and when service tickets are recieved they are encrypted by it, so httpd process has no direct access to it, it can't decrypt it. This design likely aims to protect credentials in case of a successful attack on the Apache process.
6. After ldap service tickets aquired (stored in the /run/ipa/ccaches), apache access ldap on behalf of the user. Or better I say python code make ldap request to LDAP server, in GSS context that was handled by apache.
7. Getting data from LDAP
8. Perfoming http response with data from LDAP, and setting a coockie ipa_session. What is in the cookie, by the way? I thought it was TGT but now I'm not that sure
So each point highlight my areas of confusion, and I would appreciate any corrections or insights from anyone familiar with the FreeIPA client authentication process.
On 01/07/2023 16.15, dweller dweller via FreeIPA-users wrote: Hi,
I'm impressed! You figured out most of the authentication and authorization workflow yourself. This is quite an achievement! The system is complex and goes through multiple stacks. I'm sure you would have figured out the rest if you would have found the code behind the "/ipa/session/login_password" route.
Let me fill in some gaps...
So user is already sees before him FreeIPA login form. He types his login/password and submits the form
Request goes to /ipa/login_password. /ipa/login_password location by itself does not offer much:
<Location "/ipa/session/login_password"> Satisfy Any Require all granted
</Location>
The login_password route does more work than you give it credit. When a user enters a password, mod_wsgi runs this Python code to authenticate the credentials with kinit. More later in my post. The route has authentication disabled because there is no authenticated session yet.
https://github.com/freeipa/freeipa/blob/master/ipaserver/rpcserver.py#L979
Here is where things become vague for me. We see *GssapiImpersonate On* and *GssapiUseS4U2Proxy on*.
GssapiImpersonate This option can be used even if AuthType GSSAPI is not used for given Location or LocationMatch, to obtain service ticket for a user that was already authenticated by different module
and
GssapiUseS4U2Proxy Enables the use of the s4u2Proxy Kerberos extension also known as constrained delegation This option allows an application running within Apache to operate on behalf of the user against other servers by using the provided ticket (subject to KDC authorization).
So why do we need both? Isn't GssapiUseS4U2Proxy enough to issue service tickets on behalf of user and use them? Also, is there is no TGT actually obtained for the client's principal, only service tickets?
The options enable both s4u2self and s4u2proxy.
A typical web application implements access control in the backend and has a database user to talk to a database like Postgres. FreeIPA works differently.
There is no database account for FreeIPA HTTP backend. Instead it uses constraint delegation between HTTP/$(hostname) and ldap/$(hostname). The HTTP service principal is allowed to request a service ticket on behalf of the user. This allows the HTTP service to talk to LDAP while impersonating the user. LDAP thinks that LDAP operations are made by the user account and enforces access permission based on the user's permissions.
The document https://freeipa.org/page/Troubleshooting/PrivilegeSeparation explains the concepts in greater details.
Let's review options for ipa-httpd and ipa-api services:
- allow_protocol_transition (This option controls whether s4u2self requests are allowed for the requesting client)
Ok, understandable, we need it in conjuction with GssapiUseS4U2Proxy on of the Apache, otherways it won't work. Though it is present for service/ipa-api and not for the service/ipa-httpd. Why so?
- allow_client_ccache_sync
This one is kind of vague for me. This option allows the proxy, in certain circumstances, to send back an additional option in the response structure of certain calls when it determines that a new ticket may have been added to the internal ccache. Clients can then replace their (encrypted) copy with the updated ccache.
So gssproxy injects option in response for a client (apache in our case?) to renew service tickets? What are the "certain calls"?
Correct, in this case Apache HTTP is the client. mod_auth_gssapi caches the impersonated service ticket that the HTTP service principal requested on behalf of the user for access to LDAP. AFAIK "certain calls" are (1) acquire a new service ticket from KDC, (2) renew expired ticket.
- Somehow user getting autheticated. But there is no TGT, is it? So how is it done?
The Python code behind "login_password" uses kinit to acquire an amore ccache, then a TGT with username and password (or password + OTP). The TGT is stored in a temporary ccache. The code then performs an internal request to "/ipa/session/cookie" in order to fetch session cookie from mod_auth_gssapi. Finally the ccache with the TGT is destoyed and the cookie is returned to the user.
There is also a /ipa/session/login_x509 route that uses mod_lookup_identity and SSSD to perform mTLS authentication for smartcards and client certs.
- Perfoming http response with data from LDAP, and setting a coockie ipa_session. What is in the cookie, by the way? I thought it was TGT but now I'm not that sure
It's not the TGT. HTTP negotiate never transmits the user's TGT, only a service ticket. I'm not entire sure but I *think* it's either the user's encrypted service ticket for HTTP service of the host or something similar that let's mod_auth_gssapi map the session cookie to the encrypted ccache on disk.
HTH, Christian
The document https://freeipa.org/page/Troubleshooting/PrivilegeSeparation explains the concepts in greater details.
Well this article is very valuable and answers most of my questions about the reasons for httpd and ipaapi service separation.
Finally the ccache with the TGT is destoyed and the cookie is returned to the user.
So in the server-side process, the Ticket Granting Ticket (TGT) is obtained (your link to class login_password), but it is then discarded after acquiring the LDAP service ticket and setting the cookie. Consequently, if you possess an "ipa_session" cookie that is somehow linked to a particular service ticket for ldap in the "/run/ipa/ccaches" directory, the authorization code (which executes the "kinit" process) is not executed.
Thank you, I think the pieces of the puzzle are falling into place now.
freeipa-users@lists.fedorahosted.org