Unable to pass auth info through from proxy server

I’m unable to pass user authentication info via the HTTP headers to RhodeCode. In my case I’m using RC 4.18.2 with an Apache https proxy server authenticating against an LDAP server and then proxying over regular plain HTP to RC… I’ve tried many permutations based on info I’ve found on the web, some of which pertains to quite old versions of RC so I don’t know how relevant it is any more.

On the RhodeCode side, I’ve disabled anonymous usage and enabled the LDAP Authentication plugin, which works fine. I’ve also enabled the Headers Authentication plugin with default settings, which is the header “REMOTE_USER” or the fallback header “HTTP_X_FORWARDED_USER”. This latter value looks suspicious, as if it’s undergone some name mangling, but I’ll let that go for the moment.

Here is the final list of Authentication plugins:

egg:rhodecode-enterprise-ce#token,
egg:rhodecode-enterprise-ce#headers,
egg:rhodecode-enterprise-ce#rhodecode,
egg:rhodecode-enterprise-ce#ldap

Note I’ve also tried putting the headers as the last entry.

On the Apache side I’ve simplified things to match examples I found on the web just using file-based auth. So I ended up with:

<location “/rhodecode”>
ProxyPass http://127.0.0.1:8083/rhodecode timeout=7200 Keepalive=On
ProxyPassReverse http://127.0.0.1:8083/rhodecode

AuthType Basic
AuthName "Credentials Required"
AuthUserFile "/var/www/passwd"
require valid-user
RequestHeader unset X-Forwarded-User
RewriteEngine On
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule .* - [E=RU:%1]
RequestHeader set X-Forwarded-User %{RU}e

Using that config ends up with the RC login screen being displayed - ie the userID is not getting interpreted by RC as a valid user. The same happens when I place a valid user ID directly in the X-Forwarded-User header(!) or use REMOTE_USER / HTTP_X_FORWARDED_USER as the header names.

The only hint I get that something is actually happening is when I accidentally forgot to turn on the rewrite engine. Commenting out that line in the above Apache config results in RC showing it’s main page with the list of repositories but with a username of “(null)”! This only happens when using the X-Forwarded-User header, not the other two mentioned. BUT… I then changed the RC Headers config to specify HTTP_REMOTE_USER as the primary header to check - ie using the same form of name mangling that the fallback header uses. Then testing Apache again, all of the following headers will result in the (null) user being displayed: REMOTE_USER; REMOTE-USER; X_Forwarded_User. So the conclusion there is the name mangling is required, and hyphens and underscores are interchangeable.

In terms of full disclosure I also tried a couple of seemingly old RC settings in rhodecode.ini, setting container_auth_enabled & to true - they made no difference either

FWIW we happen to have another Java application, Sonatype Nexus, behind the same Apache proxy. The only configuration line we have to specify to successfully pass through the userid is “RequestHeader set REMOTE_USER %{REMOTE_USER}s”: Ie we don’t need to use mod-rewrite. Needless to say I tried this with RC, and it resulted in the RC login dialog again - ie it didn’t work. I’m pretty confident with the Apache side of things, but not the RC side.

So the questions / observations I have are:

  • What is the correct way to set this up?!
  • Are there any useful diagnostics I could enable to see what’s happening on the RC side
  • The default Headers config within RC seems self-inconsistent / wrong, and the use of name mangling is confusing to me. The defaults ought be REMOTE_USER and X-Forwarded-User - name mangling should really happen behind the scenes.

Thanks for bearing with me!

Hi,

Have you tried to enable debug logs, and searching for:
Running PRE-AUTH for headers based authentication log line ? That should give you maybe some hints why the header auth is not working

Well I’d tried debug, but didn’t know how to narrow it down after that … so thanks for the additional pointer.

The relevant log message was

User <User('id:2:admin')> is bound to rhodecode auth type. Plugin allows only [u’headers’], skipping | req_id:a6b18efb-7a82-4004-aba1-fd3fe46dbd0b

Which is a little mystifying but it lead me to check the User settings, and from that I realized that Headers had become available in the Authentication Type. So changing that from Ldap to Headers enabled things to start working. So I guess you bind a user to a particular type of authentication - I wasn’t expecting that. I had presumed Headers would be an additional type of authentication, rather than being “either-or”. I’ll have to wrap my head around the ramifications of that.

So I’m good now. But I think my original comment about the the Headers config is still valid (The default Headers config within RC seems self-inconsistent / wrong). To that I would also add that given I’d disallowed anonymous access, I think allowing a null user access would seem to be asking for trouble!

Thanks for the help

I see now why it’s doing that. “(null)” is just being treated as a non-existent user, with the result that the first time it happens RC is adding it as a new user. I no longer know if that’s good or bad so I’ll just mention it and move on!.

It just reads a string as an username, probably that null shouldn’t be there, or if you cannot fix it disable null user so no-one can log into that account.

I’m wondering if if I have something configured incorrectly. I was messing around between using LDAP auth within RC and having the reverse proxy do the LDAP auth and pass the username as a Header.

I would expect that if Header is set above LDAP in the order of authentication handlers, then RC would first try authenticating an existing user using the Header and if it’s not there then it would try authenticating via LDAP. But It looks to me like I have to explicitly set each existing user’s method of authentication to either Header or LDAP, so really making the order of authentication handlers redundant.

If I’m right, then a consequence of this is that if I switch between doing the auth in the reverse proxy versus RC, I have to change all the users, but there is no global method of doing that. Admittedly that should be a very rare thing to have to do once the configuration is final.

Thanks for any confirmation / clarification

Yes this works like that, but once a valid log-in would take place then this user is bound to authentication this account was created from. You can log-in to RhodeCode via auth-header or ldap without an mapped account on RhodeCode side. Once this happens we create internal account stored in the database and bind it to specific authentication type.

If you would like to change few accounts best would be to chaing it under super-admin user edit panel. Or bulk simply edit extern_type database field for users.

RhodeCode uses authentication plugins, they are called plugins since you can basically copy the auth-header plugin (it’s part of open-source CE edition) and modify it to accept other types of accounts(e.g LDAP accounts can also auth against headers, this is how auth-token plugin works) Then you would drop the plugin into the source code so it would be visible in the UI, and you could use this custom logic as you would like

Hope that helps