External Cookie Validation in Apache HTTP Server - apache

I would like to validate cookies' authenticity using a backend http service, which is hidden behind an Apache HTTP Server - set as reverse proxy. I would also like to avoid scripting (CGI, Perl, Ruby, etc.) and use the Apache configuration only.
This should enable the decoupling of the cookies' validation logic from the reverse proxy itself.
Here are the actors in the flow I would like to implement:
Backend main service [Main]
Backend Cookie validation service [CookieValidation]
Backend reverse proxy [RevProxy]
Frontend web browser [Frontend]
Here is the flow I would like to implement:
[Frontend] requests a resource from [Main]
[RevProxy] intercepts the request
***** Pseudo code *****
response = send cookie to [CookieValidation] for validation
// e.g. *http://CookiValidation.server.com/validate?cookie={...}*
if (response.status == STATUS_OK) {
Proxy original request to [Main]
} else {
return response
}
Will that be possible?

You can't do it with just Apache "configuration".
If you don't want to write your own Apache module, you'd have to at least write a small script and ivnoke it as a RewriteMap prg: script. It would contact the cookie validation server, and you'd use its output as a test in RewriteCond to determine whether to proxy / where to proxy (it wasn't very clear what actions you expected)

Related

ktor redirecting to 0.0.0.0 when doing an https redirect

I've added an http redirect to my Ktor application and it's redirecting to https://0.0.0.0 instead of to the actual domain's https
#ExperimentalTime
fun Application.module() {
if (ENV.env != LOCAL) {
install(ForwardedHeaderSupport)
install(XForwardedHeaderSupport)
install(HttpsRedirect)
}
Intercepting the route and printing out the host
routing {
intercept(ApplicationCallPipeline.Features) {
val host = this.context.request.host()
i seem to be getting 0:0:0:0:0:0:0:0 for the host
Do i need to add any special headers to Google Cloud's Load Balancer for this https redirect to work correctly? Seems like it's not picking up the correct host
As your Ktor server is hidden behind a reverse proxy, it isn't tied to the "external" host of your site. Ktor has specific feature to handle working behind reverse proxy, so it should be as simple as install(XForwardedHeaderSupport) during configuration and referencing request.origin.remoteHost to get actual host.
Let's try to see what's going on.
You create a service under http://example.org. On the port 80 of the host for example.org, there is a load balancer. It handles all the incoming traffic, routing it to servers behind itself.
Your actual application is running on another virtual machine. It has its own IP address, internal to your cloud, and accessible by the load balancer.
Let's see a flow of HTTP request and response for this system.
An external user sends an HTTP request to GET / with Host: example.org on port 80 of example.org.
The load balancer gets the request, checks its rules and finds an internal server to direct the request to.
Load balancer crafts the new HTTP request, mostly copying incoming data, but updating Host header and adding several X-Forwarded-* headers to keep information about the proxied request (see here for info specific to GCP).
The request hits your server. At this point you can analyze X-Forwarded-* headers to see if you are behind a reverse proxy, and get needed details of the actual query sent by the actual user, like original host.
You craft the HTTP response, and your server sends it back to the load balancer.
Load balancer passes this respone to the external user.
Note that although there is RFC 7239 for specifying information on request forwarding, GCP load balancer seems to use de-facto standard X-Forwarded-* headers, so you need XForwardedHeaderSupport, not ForwardedHeaderSupport (note additional X).
So it seems either Google Cloud Load Balancer is sending the wrong headers or Ktor is reading the wrong headers or both.
I've tried
install(ForwardedHeaderSupport)
install(XForwardedHeaderSupport)
install(HttpsRedirect)
or
//install(ForwardedHeaderSupport)
install(XForwardedHeaderSupport)
install(HttpsRedirect)
or
install(ForwardedHeaderSupport)
//install(XForwardedHeaderSupport)
install(HttpsRedirect)
or
//install(ForwardedHeaderSupport)
//install(XForwardedHeaderSupport)
install(HttpsRedirect)
All these combinations are working on another project, but that project is using an older version of Ktor (this being the one that was released with 1.4 rc) and that project is also using an older Google Cloud load balancer setup.
So i've decided to roll my own.
This line will log all the headers coming in with your request,
log.info(context.request.headers.toMap().toString())
then just pick the relevant ones and build an https redirect:
routing {
intercept(ApplicationCallPipeline.Features) {
if (ENV.env != LOCAL) {
log.info(context.request.headers.toMap().toString())
// workaround for call.request.host that contains the wrong host
// and not redirecting properly to the correct https url
val proto = call.request.header("X-Forwarded-Proto")
val host = call.request.header("Host")
val path = call.request.path()
if (host == null || proto == null) {
log.error("Unknown host / port")
} else if (proto == "http") {
val newUrl = "https://$host$path"
log.info("https redirecting to $newUrl")
// redirect browser
this.context.respondRedirect(url = newUrl, permanent = true)
this.finish()
}
}
}

HTTPD Proxy Change Response Address

My setup is as follows:
client -> proxy(dnsname eg. https://test.com) -> Jetty webapp(1.2.3.4)
The webapp sends a redirect response back (to an authentication webapp) to the client. It automatically points to the proxy via dnsname eg. https://proxy/auth and cannot be configured further.
The issue with this is the webapp will pass redirects back to the client and the client cannot resolve https://proxy as I can't make it a dns entry. Is it then possible for the proxy to intercept the traffic from the webapp (https://proxy) and change it to https://test.com? Even better can the proxy autodetect the entry dns name and append it to any responses from the webapp?
I'd envisioned the following:
client request https://test.com/page1-> hits proxy which resolves to webapp -> webapp gives redirect response via https://proxy/auth -> proxy intercepts and changes redirect to https://test.com/auth
I need this so that everything behind the proxy isn't machine nor ip specific. I can shift and deploy to any environment.
I figured this out eventually. You can just modify the redirect headers in the location field.
Header edit Location "(^http[s]?://proxy)" "https://whatevernameyouwant"

Caddy as reverse proxy to rewrite a http redirect url from an upstream response

I am having a backend that is not able when running behind a reverse proxy since I cannot configure a custom base URL.
For the login process the backend makes heavy use of HTTP redirects but due to the fact that is behind a reverse proxy it sends redirection URL that are not reachable by the client.
So I was wondering if there is a way to rewrite the upstream HTTP HEADER Location
If the backend responses
HTTP/1.1 301
Location: http://backend-hostname/auth/login
Caddy should rewrite the Location header to
HTTP/1.1 301
Location: http://www.my-super-site.com/service/a/auth/login
Is something like this possible?
I've that we can remove headers by declaring
header / {
- Location
}
but it possible to replace the header and rewrite the URL?
I was also looking for answer for this question and unfortunately I've found this responses:
https://caddy.community/t/v2-reverse-proxy-but-upstream-server-redirects-to-nonexistent-path/8566
https://caddy.community/t/proxy-url-not-loading-site/5393/7
TLDR:
You need to use sub-domains rather than sub-paths for services that are not design for being after proxy (or at least configure base URL). :(

How to setup authorization for proxy request a-la x-sendfile?

In a setting of apache + mod_wsgi and nginx + uwsgi, what could be the way to setup web-server to proxy big "intranet" files requests?
What I am thinking about is a way a la x-sendfile, but where the wsgi application points to a file URL "intranet" location in its response, web-server downloads and uploads the file to the original requester without revealing it's "intranet" location. Of course, nothing happens if there is no authentication and access rights check on wsgi application side.
It's very hard to find this kind of setup by googling, not even sure what term to use.
By "intranet" I mean files, accessible via HTTPS requests from the proxy server, which may have its own credentials to them, but not from public internet or by local filesystem (like is the use case with x-sendfile)
If using mod_wsgi in daemon mode, you can return an empty HTTP 200 response with Location response header and when that is seen by the Apache process proxying to the mod_wsgi daemon process, it will evaluate that as a sub request. The path in that could be mapped to a new URL handler in Apache configuration which is actually a proxy setup which sends the request to another downstream backend server. The response from that will then be proxied back to the client. If you don't want that secondary URL handler to be visible outside, ie., someone can't request it direct if they work out the URL path, you need to use a mod_rewrite rule to reject any request if it isn't a sub request.
So you might have something like:
RewriteCond %{IS_SUBREQ} false
RewriteRule ^/hidden/stuff/ - [F]
ProxyPass /hidden/stuff/ http://backend.example.com/
The WGSI response would then be empty HTTP 200 response with Location header of:
Location: /hidden/stuff/some-file-name
The sub request request would end up being:
http://backend.example.com/some-file-name
against backend server with response proxied back to client.

Apache redirect rules for back-end server with WebSockets

I'm trying to figure out how to properly setup apache redirect rules for back-end CherryPy server which implements websocket (done via ws4py module). The problem is that if you use rewrite engine or proxypass it strips off Upgrade header in redirected request and therefore CherryPy server complaints about it and fails at handshake step.
The scenario I have is the following. I have CherryPy server with ws4py module which setup WebSockets. It runs on localhost:9000. I want to have apache front-end which just redirect incoming request to back-end server (it does more than that, but for simplicity it should do just that).
The apache rule I have is simple
RewriteRule ^(/websocket(/.*)?)$ http://some_host:9000$1 [P,L]
so for all requests starting with /websocket it redirects them to back-end server running on port 9000. The P flag stands for Proxy, the L stops rewriting process (see http://borkweb.com/story/apache-rewrite-cheatsheet)
If client sends request with HTTP header Upgrade:websocket the apache engine (rewrite module) strips it off, which causes WebSocket handshake fails.
Is there are any way to fix rewrite rule to allow presence of Upgrade header?
Unfortunately, Apache doesn't have the capability to reverse proxy WebSocket connections yet (it absolutely should!). But there is a solution that allows web requests to be handled using Apache and WebSocket connections to be handled by something else. This solution involves using HAProxy as the front end to both apache and your WebSocket server.
Here are a couple of relevant links to get you started:
http://lheurt.blogspot.com/2011/12/reverse-proxy-nodejs-websockets-with.html
HAProxy + WebSocket Disconnection