python tornado user authentication and then reverse proxy via apache - python

I am implementing a two-way SSL authentication and then additional authentication via Kerberos after which it redirects the user to an internal server via reverse proxy.
i.e:
SSL auth <--> Apache Server + kerberos auth using login/password <--reverse proxy-->> internal server
This setup currently works:
Now my idea is to use this configuration as I can control the behavior of the user via Tornado
SSL auth <--> Apache server <---> Tornado webserver for kerberos auth <---> reverse proxy <---> internal server
And I have got the SSL authentication and the Kerberos authentication working.
However, how do I tell Tornado to reverse proxy(apache) to the internal server?

Tornado doesn't have any built-in reverse proxy functionality, but in the simple case a reverse proxy is just a RequestHandler that passes through to an HTTP client:
class ReverseProxyHandler(RequestHandler):
#gen.coroutine
def get(self):
resp = AsyncHTTPClient().fetch(self.convert_url(self.request),
headers=self.request.headers)
self.set_status(resp.code)
for k,v in resp.headers.get_all():
self.add_header(k, v)
self.write(resp.body)
It could get a lot more complicated than that depending on what your requirements are. This is only a simple thing to build if you can be sure that your internal server doesn't do anything tricky.

Related

How can username be received by an upstream private service from a OAuth2-proxy?

I set up OAuth2-proxy as a reverse proxy, providing authentication and authorization for users of a custom web application, deployed as an upstream private service. It all works well. Except that I would like to pass the username from the reverse proxy to the upstream application.
The OAuth2-proxy documentation suggests that username can be passed from the reverse proxy as the HTTP header X-Forwarded-User. But how can my upstream application receive that HTTP header? The upstream application is in Python, so I looked at the requests package, but that package seems to only provide headers for an outbound request made by the application, or the response to that outbound request.
What am I missing?
Figured it out, with some help from someone on the Render forum.
The HTTP header X-Forwarded-Email carries the user's email from OAuth2-proxy to the upstream application. The upstream application is in Dash, which runs on top of Flask. The headers are available via flask.request.headers.

How to tell when a request is coming from a local https reverse proxy as opposed to directly to the http port

I have a REST API flask, configured to listed on http at port 7001
I have also setup an apache as a reverse proxy to supply https
My flask is also using the ProxyFix to be able to correctly detect the incoming IP address in order to use flask.limiter
REST_API = Flask(__name__)
REST_API.wsgi_app = ProxyFix(REST_API.wsgi_app, x_for=1)
I want to reject all requests which are not coming from my reverse proxy, but rather directly to my 7001 port. Without the ProxyFix, I could do this method
#REST_API.before_request
def limit_remote_addr():
logger.debug(request.remote_addr)
if request.remote_addr != '127.0.0.1':
error_msg = get_error(ServerErrors.NO_PROXY)
abort(403, error_msg)
But now with the ProxyFix, I am always seeing the original IP.
If there a way to detect if the requestor is coming via my reverse proxy in this setup?

Bottle-WebSocket: How to ensure an HTTP request is from the same session as ws connection?

I built an web application using Python Bottle framework.
I used bottle-websocket plugin for WebSocket communication with clients.
Here is a part of my code.
from bottle import Bottle, request, run
from bottle.ext.websocket import GeventWebSocketServer, websocket
class MyHandler():
...
class MyServer(Bottle):
...
def _serve_websocket(self, ws):
handler = MyHandler()
some_data = request.cookies.get('some_key') # READ SOME DATA FROM HTTP REQUEST
while True:
msg = ws.receive()
handler.do_sth_on(msg, some_data) # USE THE DATA FROM HTTP REQUEST
ws.send(msg)
del(handler)
if __name__ == '__main__':
run(app=MyServer(), server=GeventWebSocketServer, host=HOST, port=PORT)
As the code shows, I need to read some data from the browser (cookies or anything in the HTTP request headers) and use it for WebSocket message processing.
How can I ensure the request is from the same browser session as the one where WebSocket connection comes?
NOTE
As I do not have much knowledge of HTTP and WebSocket, I'd love to here detailed answere as much as possible.
How can I ensure the request is from the same browser session as the one where WebSocket connection comes?
Browser session is a bit abstract since HTTP does not have a concept of sessions. HTTP and RESTful APIs is designed to be stateless, but there is options.
Usually, what you usually want to know is what user the request comes from. This is usually solved by authentication e.g. by using OpenID Connect and let the user send his JWT-token in the Authorization: header, this works for all HTTP requests, including when setting up a Websocket connection.
bottle-oauthlib seem to be a library for authenticating end-users using OAuth2 / OpenID Connect.
Another option is to identify the "browser session" using cookies but this depends on a state somewhere on the server side and is harder to implement on cloud native platforms like e.g. Kubernetes that prefer stateless workloads.

Authenticating connection in PySolr

This is the first time I am using Python and Solr. I have my Solr instance set up within tomcat on GCE. I am trying to connect to it from my Python code using PySolr. However, I am not sure how to send authentication parameters via PySolr.
This is the exception I get:
solr = pysolr.Solr('http://MY INSTANCE IP/solr/News', timeout=10)
Apache Tomcat/7.0.28 - Error report HTTP Status 401 - type Status reportmessage description This request requires HTTP authentication ().Apache Tomcat/7.0.28
Please advise.
solr = pysolr.Solr('http://user:pass#IP:8983/solr/')
That's all you need ...
You can pass Solr authentication as part of the Solr connection parameter.
You don't have proper documentation in pySolr on how to carry out authentication. Since pySolr internally uses requests for authentication you can follow authentication in requests.
Here is a small example on custom authentication as well.
In the case of Basic Authentication, you can use it as
solr = pysolr.Solr('http://IP:8983/solr/collection',auth=('username','password'))
or
from requests.auth import HTTPBasicAuth
solr = pysolr.Solr('http://IP:8983/solr/collection',auth=HTTPBasicAuth('username','password'))
This is the proper way of authentication. Passing username and password as a part of URL is not recommended as it might create issues if # or ' are used in any of those may create issues in the authentication.Refer this GitHub issue

how to add authentication to a wsgiref python web service

I am working on a sample code given in the python documentation, the code is:
from wsgiref.simple_server import make_server, demo_app
httpd = make_server('', 8000, demo_app)
print "Serving HTTP on port 8000..."
# Respond to requests until process is killed
httpd.serve_forever()
# Alternative: serve one request, then exit
httpd.handle_request()
I can access this through the localhost on port 8000, but now if I want to pass username/password with the "localhost:8000 username, password" how do I do this. I have figured out how I would get to know if the authentication was unsuccessful but not how to actually receive the username/password for checking..
Any hints, and tips.....
Cheers,
If you pass username/password in the query string like http://localhost:8000?username=x&password=y, you can retrieve them in your WSGI handler function from the environ dict: environ['QUERY_STRING']. You can use urlparse.parse_qs from the standard library to parse it. If this is code that's going into production, I second Joran, you should use at least HTTP Basic Authentication and some authentication middleware like barrel.

Categories