Replace certrain string in Authorization with fixed value

Hello there.

As of right now I’m using Haproxy to route requests to a 3rd party proxy provider.

Short explanation:

Haproxy listens on port 8887 and accepts requests from connections that provide a valid proxy-authorization via Header that match my haproxy userlist.
Requests are forwarded to my backend which deletes the proxy-header and sets the actual header needed to be authorized to access the 3rd party provider and sends out the request to them.

What I would like to achieve is being able to replace a certrain string of the username and replace it with a fixed value, while letting the rest of the string that was not matched stay as it is.

As an example.

User sends a request to us at 85.55.55.55:8887 with username admin_2 and password password

We receive a Authorization header corresponding to the base64 encoded value of admin_2:password
which is

Authorization Basic ‘YWRtaW5fMjpwYXNzd29yZA==’

What I would like to do is only replace the username admin with for example customerABC and keep the _2. So I end up with customerABC_2:password

What I tried in backend is using http-request replace-value like so:

http-request replace-value Authorization YWRtaW4= Y3VzdG9tZXJBQkM=

YWRtaW4= being the base64 encoding of admin
Y3VzdG9tZXJBQkM= being the base64 encoding of customerABC

I realize this is most likely not working due to the nature of base64 decoding, is there any workaround so I can get this to work?

So not only you want to replace the username, you want to replace only part of the username?

It does not suffice to replace admin_2 with customerabc_2 statically? Then what is it, exactly, that you want to replace? Everything before an underscore?

Thanks for your reply.

Yes exactly. The issue is the 3rd party provider uses suffixes like

username_country-Germany

or

username_session-ABC

so that the users are able to choose target location or sticky ip sessions.

I know this is kind of a very special request to have but since haproxy offers such huge possiblities I was wondering if its somehow possible to get this working without the need of a custom module.

Kind regards.

Small update:

I’m pretty sure this is able to be done by using the regsub and b64dec options that HAProxy offers.
It should probably be used in a sense of :

req.fhdr(Authorization),regsub(regexp1),b64dec,regsub(regexp2)

Regexp 1 should remove the Basic and whitespace and only return the b64 encoded bit of the authorization header value
Regexp 2 should match and replace username and password while letting the -country-DE and -session-abc bit as it is.

What I need to do now is work to find out how I set up those regular expressions. While I do know the basic stuff these seem a bit advanced and I’m failing at properly setting this up, I guess this not really a HAProxy related issue however.

It’s not a one-liner. It needs a little bit of back and forth with multiple variables. admin_ is replaced with customerABC_ :

# reverse base64 current auth
http-request set-var(req.auth_old) req.fhdr(Authorization),regsub(^Basic\s+,,i),b64dec

# extract old user
http-request set-var(req.auth_old_user) var(req.auth_old),regsub(:.+,)

# acl underscore_found
acl underscore_found var(req.auth_old_user) -m sub _

# prepare new username and password
http-request set-var(req.auth_user) var(req.auth_old_user),regsub(admin_,customerABC_) if underscore_found
http-request set-var(req.auth_pass) var(req.auth_old),regsub(.+:,) if underscore_found

# concat and base64 encode
http-request set-var(req.auth_new) var(req.auth_user),concat(':',req.auth_pass,),base64 if underscore_found

# set Authorization header
http-request set-header Authorization %[str(),concat('Basic ',req.auth_new)] if underscore_found
2 Likes

Wow. What an amazingly indepth answer. I really appreciate it. I was actually on step 3 (acl underscore_found) after 4 hours of working around with the options.

Simply amazing how quick you’ve managed to achieve this, I’m realizing I still have lots of work to do until I am truly familiar with HAProxy.

Thanks a thousand times for your input, if there is any way I can contribute to you, please let me know :heart:

Code above from lukastribus is working perfect!

On another note:

Is the scenario above, I would have to add the different options in my userlist so the request does not get denied, as user admin is authed user admin_country-de or admin_session-123 is not.

Is there a way to set up my authentification in a similar way so only the base username is being matched against my ACL?

Example of my current setup:

In my global section I have setup a userlist:

userlist L1
group AuthedUsers users admin
user admin insecure-password password

In my frontend section I have set up a ACL and a http-request auth rule to only allow users from my userlist:

acl auth_ok http_auth_group(L1) AuthedUsers

http-request auth realm myrealm unless auth_ok

Now when a user tries to access the server with credentials: admin_country-de:password

It will get denied since the http_auth_group looks for the match of those credentials in my list and is unable to find it.

I was thinking of setting it similiarly to the solution to my main question, in which I strip the part after _ before matching the credentials with my list. Is there such a possibility?

Hello,

Still trying to figure out how exactly I can setup authentification so it works with this.

What I was thinking was doing a similiar approach by declaring variables and check them against http_auth_group, however it doesn’t seem to work that way. Here is my try:

# reverse base64 current auth
http-request set-var(req.username_with_addition_and_pass) req.fhdr(proxy-authorization),regsub(^Basic\s+,,i),b64dec

#extract username without additions
http-request set-var(req.username_no_additions) var(req.username_with_addition_and_pass),regsub(-.+,)

#extract password
http-request set-var(req.password_only) var(req.username_with_addition_and_pass),regsub(.+:,)

#add user password to username without additions and compile as base64 encoded string
http-request set-var(req.username_no_addition_with_password) var(req.username_no_additions),concat(':',req.password_only),base64

#acl match with http_auth_group
acl auth_group_match var(req.username_no_addition_with_password) -m found http_auth_group(L1)

Then I declare my auth realm as

http-request auth realm myrealm unless auth_ok or auth_group_match

Sadly this does not work as intended as it just lets through any combinations of user and password that contain underscores.

Would appreciate some more help to see what exactly I’m doing wrong

Are you saying your are authenticating both on haproxy and then again on your backend application, and now you need to rewrite for haproxy itself also?

Hello lukas, yes, since the backend is a 3rd party I have to authenticate on both. I did however figure out how to do it, if anyone has the same problem here is the solution :slightly_smiling_face:

On top of lukastribus edit I had to do my own which removes extra strings, checks them against auth group:

# reverse base64 current auth
http-request set-var(req.username_with_addition_and_pass_b64) req.fhdr(proxy-authorization),regsub(^Basic\s+,,i)
http-request set-var(req.username_with_addition_and_pass) req.fhdr(proxy-authorization),regsub(^Basic\s+,,i),b64dec

#extract username without additions
http-request set-var(req.username_no_additions) var(req.username_with_addition_and_pass),regsub(-.+,)

#extract password
http-request set-var(req.password_only) var(req.username_with_addition_and_pass),regsub(.+:,)

#add user password to username without additions and compile as base64 encoded string
http-request set-var(req.username_no_addition_with_password) var(req.username_no_additions),concat(':',req.password_only),base64

# set Authorization header
http-request set-header proxy-authorization %[str(),concat('Basic ',req.username_no_addition_with_password)]
http-request set-header Authorization %[str(),concat('Basic ',req.username_no_addition_with_password)]

Now if a user tries to auth with username-country-XX for example it will remove those strings. After that you can do the normal

acl auth_ok http_auth_group(L1) AuthedUsers

http-request auth realm myrealm unless auth_ok

Lukas’s part has to be inserted after that http-request auth in order for it to work

1 Like