How to verify HMAC in HAProxy - haproxy

Is it possible to check HMAC validity in HAProxy? Ideally I'd like to set an acl if the HMAC is valid so I can use this in rules.
Our Ubuntu 18.04 build server (running Jenkins) sits behind a firewall with only specific IP ranges white-listed.
We have an HAProxy (1.8) instance receiving all inbound requests and routing to the appropriate backend service.
The problem is SonarCloud have changed their webhooks from a defined set of IP addresses to using HMAC to validate authenticity. This means the webhooks are blocked by the firewall unless we open it to all internet traffic.
https://sonarcloud.io/documentation/project-administration/webhooks/#securing-your-webhooks
If we can congifure HAProxy to validate the HMAC then we can open the server to all traffic & use HAProxy to validate these requests (as well as other existing IP whitelisted ranges).

Thanks to Michael for the pointer to HAProxy/Lua integration. My solution noted here for reference.
Created the following Lua script (hmac_validate.lua):
hmac = require('openssl.hmac')
local function tohex(s)
return (string.gsub(s, ".", function (c)
return string.format("%.2x", string.byte(c))
end))
end -- tohex
function validate_sonar_hmac(txn, hmac_header_key, hmac_secret)
local payload = txn.req:dup() -- take a copy of the request content
local body = string.sub(payload,string.find(payload,"\r\n\r\n")+4) -- strip off the headers
local signature = txn.sf:req_fhdr(hmac_header_key) -- get the HMAC signature sent on the request
-- calculate hmac from body & secret
local sc_hmac = hmac.new(hmac_secret, "sha256")
local calculated_signature = tohex(sc_hmac:final(body))
local signatures_match = calculated_signature == signature
if not signatures_match then
core.Alert("Sonar Cloud HMAC signature mismatch - received '"..signature.."' but calculated '"..calculated_signature.."'")
end
txn:set_var("req.sonar_request_valid", signatures_match)
end;
core.register_action("validate-sonar-hmac", {"http-req"}, validate_sonar_hmac, 2)
HA Proxy config changed to add these lines:
global
lua-load /etc/haproxy/hmac_validate.lua
frontend
acl sonarcloud hdr(X-Sonar-Webhook-HMAC-SHA256) -m found
http-request lua.validate-sonar-hmac X-Sonar-Webhook-HMAC-SHA256 {{ sonarcloud_hmac_secret }} if sonarcloud
http-request deny if sonarcloud !{ var(req.sonar_request_valid) -m bool }

HAProxy doesn't do HMAC natively, but it can be done using HAProxy's Lua integration.
One approach would be to find a Lua library that can do the flavor of HMAC that you need, and then write an HAProxy converter in Lua to take the appropriate inputs and do the calculation of the digest for comparison.
I implemented something similar, once, using HAProxy 1.6 with Lua 5.x where the client sent a URL signed using HMAC-SHA1, and the proxy successfully checked it for validity.
Unfortunately, I no longer have access to that code, but I wrote this HAProxy converter to do utf-8-aware URL-escaping (percent encoding) in Lua... I mention it here because it's a complete, working example of one way to extend HAProxy functionality using Lua, including the Lua code and HAProxy configuration needed to use it, so it might help you work toward a solution.

Related

haproxy require client certificate for specific url?

I want to configure Haproxy so that it only requires client certificate when specific URL accessed? Ex:
www.test.com - it proceeds normally.
www.test.com/secure - haproxy requires the client certificate.
To understand why this isn't directly possible requires an understanding of how TLS (SSL) works. TLS encrypts the connection before the HTTP request is sent (over the now-encrypted connection). By the time the URL is known by HAProxy, the time for requiring a client certificate has already passed.
For practical reasons, an endpoint (HAProxy frontend or listen) needs to either require a certificate for connections, or not... however, using verify optional it might be possible to achieve what you want. Using verify optional means that the proxy will ask for a client cert upon connection, and if either the client offers no cert or if the cert is valid according to the ca-file, the client will be allowed to connect. Invalid certs will result in disconnection.
Then, the ssl_c_used fetch could be used to deny requests for that path for clients who didn't present the "optional" certificate, earlier.
http-request deny if { path_beg /secure } ! { ssl_c_used }
The viability of this solution depends on how gracefully browsers behave when asked for a certificate that they would not have -- and all connecting browsers will be asked for a certificate.
But there is no way of doing exactly what you are asking, either in HAProxy or on any other platform since, by design, the path is unknown until after TLS negotiation is already complete.

WS Federation (single sign on) module - redirect issue when using SSL offloading

We have a site that we are trying to configure as a client in a SSO scenario, using WS Federation and SAML.
Our site sits behind a load balancer that is doing SSL offloading - the connection to the balancer is under https, but decrypted and forwarded (internally) to the actual site under http and port 81.
Somewhere the WS federation module is attempting to redirect us, but is building up the URL based on the port and incoming protocol to the website:
We request:
https://www.contoso.com/application
and are getting redirected to:
http://www.contoso.com:81/Application
Which doesn't work as the load balancer (correctly) won't respond on this port.
And it seems to be related to the casing of the virtual directory. Browsing to
https://www.contoso.com/Application
seems to work without issue.
(Note for completeness, attempting to browse to http://www.contoso.com/Application with no port will correctly redirect us to the SSL secured URL).
I am trying to find out:
a) Where this redirect is happening in the pipeline and
b) How to configure it to use the correct external address.
If anybody is able to point me in the right direction, I would very much appreciate it.
EDIT 14:19: Seems to be either the WsFederationAuthenticationModule or the SessionAuthenticationModule. These do a case sensitive comparison of the incoming url to what it expects and redirects otherwise:
https://brockallen.com/2013/02/08/beware-wif-session-authentication-module-sam-redirects-and-webapi-services-in-the-same-application/
So that seems to be happening, its a matter now of trying to get the site to behave nicely and redirect to the correct external url.
The following seems to be related and ultimately points to the culprit in the default CookieHandler:
Windows Identity Foundation and Port Forwarding
Looking at that code decompiled in VS, it compares HttpContext.Current.Request.Url against the targetUrl and will redirect to the expected 'cased' version otherwise (in our case including the errant port number).
It would seem that explicitly setting the path attribute of the cookie fixes this issue. Either an empty string or the virtual directory name seems to work:
<federationConfiguration>
<cookieHandler requireSsl="true" name="ContosoAuth" path="/Application/"/>
<wsFederation passiveRedirectEnabled="true" issuer="https://adfsSite" realm="https://www.contoso.com/Application/" reply="https://www.contoso.com/Application/Home" requireHttps="true"/>
</federationConfiguration>

Parse-server SSL IIS

I have tried using serverURL as localhost:1337/parse and publicServerURL as https://mydomaindotcom:1337/parse but I don't know how parse is recognizing the certificate. I don't see a config value to set the cert for parse-server.
Also tried adding cert to IIS 7 to a website on port 443 and using URL Rewrite 2.0 to localhost:1337/parse.
I'm running a self-hosted parse instance using node. parse-server is installed globally. I'm using a config file to start to instance.
Server works fine on http, however I need to utilize https so I can use the rest or js api key without passing headers like so https://myAppID:javascript-key=myJavaScriptKey#mydomaindotcom/classes/GameScore/Ed1nuqPvcm
Any help is much appreciated.
You need to add an https binding that references port 1337 if that is the one that you want to use.

How do SNI clients send server_name in CLIENT HELLO

I hope i am at the right place asking this question, its regarding understanding of SNI
According to https://devcentral.f5.com/articles/ssl-profiles-part-7-server-name-indication#.U5wEnfmSzOz
"With the introduction of SNI, the client can indicate the name of the server to which he is attempting to connect as part of the "Client Hello" message in the handshake process"
My question is how does client like browser or any HTTP client (say java.net) send this server name in CLIENT HELLO?? Does client do by itself or you have to add it Programmatically to https request (e.g how in JAVA.net HttpsURLConnection)
Reading from http://www.ietf.org/rfc/rfc4366.txt
"Currently, the only server names supported are DNS hostnames"
so the hostname is the server_name sent by SNI complient client or any other name can be sent by the client..
I hope i am clear, do improve the question/wording if its unclear or let me know if its not clear
thanks
If you are using an https library, which you can give a URL and the library will fetch the contents of that URL for you, then the clean way to add SNI support is to perform it entirely within the library.
It is the library which parses the URL to find the hostname, the caller will never know which part of the URL is the hostname, so the caller couldn't tell the library which hostname to send in the SNI request. If the caller had to somehow figure out the hostname in order to tell this to the library, then that would be a poorly designed library.
You might look a level deeper in the software stack and find that an https library might be building on top of an SSL library. In such a case even the https library does not need to know about SNI. The https library would simply tell the SSL library, that it want a connection to a particular hostname. The SSL library would resolve the hostname to get IP address to connect to, the SSL library would also be performing the SSL handshake during which the client may send a hostname as part of SNI and the server send a hostname as part of a certificate for the client to verify.
During connection setup, the SSL client library need to use the hostname for three different purposes. It would be trivial to support the usage of three different hostnames for those three purposes. The https library already know the hostname, and passing that hostname three times to the SSL library rather than just one wouldn't be any significant amount of additional work. But it would hardly make sense to support this anyway.
In fact SNI could be entirely transparent to the https library. It would make sense to extend the SSL library with SNI support without changing the API to the https library. There is little reason to turn off SNI support in a client, which supports it. So defaulting to having SNI enabled makes sense.

Need to run a cron job as encrypted

I need to setup a cron job to run a SOAP client. The customer insists that I connect to their web service (on an https address) from an https address. They insist that if I don't their response to me can't be encrypted.
My first question is, is that true? I thought that as long as I'm connecting to their SOAP service over https, the response back would automatically be encrypted.
If that's true, how can I run a cron job to be as https? My site is on a LAMP setup with cPanel access.
Thank you in advance for your help!
Your customers statement seems to be a little bit unclear in what he/she specifically means by "... connecting from an https adress" as there isn't any notion of the term "https adress" in the specs and https URLS only seem to make sense in the context of Request-URI s given in a https request.
Given this unclarity I'm only wild guessing. Nevertheless to me it seems your clients requirements might most probably not be connected to the http protocol but rather to establishing your TLS connection.
If your client is very sensitive in respect to the security of his system - which in fact if he intends to offer RPC requests might be a very good idea - he might not want to the whole world to be able to connect an encrypted connection to his machines and rely on any secondary authentication mechanism once the connection has been established.
As most users of the public internet don't have any certificates signed by a trusted authority this feature it isn't used out in the open wild but besides server authentication the TLS handshake protocol also provides a means of client authentication via client certificates (the relevant part being section 7 in RFC 5246 here. see: https://www.rfc-editor.org/rfc/rfc5246#section-7)
While in the absence of widely used client certificates web services usually rely on establishing an encryted connection to first to authenticate users by some kind of challange response test like querying for username and password your client might want to either additionally secure access to his machines by additionally requiring a valid client certificate or even - probably not the best idea - replace a second authorization like the one already mentioned above.
Nevertheless all this are nothing but some ideas that I came along with given the riddle in your question.
Most probably the best idea might be to just ask your client what he/she meant when saying "... connecting from an https adress"