Using Flask with subdomain breaks Google Task Queue routing - python

I set SERVER_NAME in my Flask app to start using subdomains so I can have e.g. frontend and backend on two different subdomains:
frontend.domain.com
backend.domain.com
I set Flask like this:
app.config['SERVER_NAME'] = 'domain.com'
app.url_map.default_subdomain = "frontend"
The app is published using Google App Engine, everything works fine, except the default App Engine domain https://PROJECT_ID.REGION_ID.r.appspot.com now returns a 404 because I understand Flask is not recognising any matching route.
I thought it was fine since I never used https://PROJECT_ID.REGION_ID.r.appspot.com, now I know I was wrong...
https://PROJECT_ID.REGION_ID.r.appspot.com is used by Google Task Cloud to route tasks and e.g. myapp.ey.r.appspot.com/my_task_worker, which is called by Cloud Tasks create_task, now hits a Not Found 404 while it worked before I set SERVER_NAME
How do I fix this? Do I have to hardcode myapp.ey.r.appspot.com in my Flask app somehow?
Here's an extract of my app.yaml, adapted:
runtime: python37
handlers:
- url: /.*
secure: always
redirect_http_response_code: 301
script: auto
env_variables:
DEBUG: False
SERVER_NAME: 'domain.com'
DEFAULT_SUBDOMAIN: 'frontend'
GCP_PROJECT: 'myapp'
CLOUD_TASK_LOCATION: 'europe-west3'
CLOUD_TASK_QUEUE: 'default'
GOOGLE_CLOUD_PLATFORM_API_KEY: 'xxxxxxxx'
...

Do I have to hardcode myapp.ey.r.appspot.com in my Flask app somehow?
Yes. The problem here is that you're managing the redirection from your App instead of leaving App engine to do it. Although this isn't a bad practice by its own, it leaves many of the App Engine features out and most important, as you already mentioned, other GCP products like Cloud Tasks expect a specific behaviour in order to work properly.
How do I fix this?
Under your current architecture you would have to add a routing to the default URL, however as far as I know Flask doesn't allow to route more than one domain, so you would have to switch the 'SERVER_NAME' to the default app engine or change into something like Django that supports multiple domains.
My suggestion is to map your subdomains to App Engine services (one for your frontend and one for your backend) and leave the routing to GCP (and remove the 'SERVER_NAME'). You can make use of the dispatch.yaml to do the routing, you can for example create the next routes:
dispatch:
# Default service serves the typical web resources and all static resources.
- url: "myapp.ey.r.appspot.com/*"
service: default
- url: "frontend.domain.com/*"
service: frontend
- url: "backend.domain.com/*"
service: backend

Related

How to change the Redirect URI from HTTP to HTTPS within a FastAPI web app using fastapi_msal?

I am trying to setup Azure AD authentication for a web application using FastAPI. I am using the fastapi_msal python package to do this. The problem I am having is that when I go to the web app, I am able to login, but once i am authenticated, it says the redirect URI that the application is using begins with HTTP. However, Azure requires the redirect uri begin with HTTPS unless running the app locally. Does anyone know how I can change the redirect uri to begin with https instead?
The code for my project pretty much exactly resembles the code from this example project here. However, I have found a similar project using Flask instead of FastAPI. And there is a specific portion of the code that addresses this redirect uri problem:
# This section is needed for url_for("foo", _external=True) to automatically
# generate http scheme when this sample is running on localhost,
# and to generate https scheme when it is deployed behind reversed proxy.
# See also https://flask.palletsprojects.com/en/1.0.x/deploying/wsgi-standalone/#proxy-setups
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
Does anyone know how I can do something like this for a web app using FastAPI instead?
The full source code for the Flask app can be found here

Can't serve swagger on remotely hosted server with different url

I'm trying to serve some simple service using flask and flask_restx (a forked project of flask-restplus, that would be eventually served on AWS.
When it is served, I want to generate swagger page for others to test it easily.
from flask import Flask
from flask_restx import Api
from my_service import service_namespace
app = Flask(__name__)
api = Api(app, version='1.0')
api.add_namespace(service_namespace)
if __name__ == '__main__':
app.run(debug=True)
When I test it locally (e.g. localhost:5000), it works just fine. Problem is, when it is hosted on AWS, because it has a specific domain (gets redirected?) (e.g. my-company.com/chris-service to a container), the document page is unable to find its required files like css and so:
What I've looked and tried
Python (Flask + Swagger) Flasgger throwing 404 error
flask python creating swagger document error
404 error in Flask
Also tried adding Blueprint (albeit without knowing exactly what it does):
app = Flask(__name__)
blueprint = Blueprint("api", __name__,
root_path="/chris-service",
# url_prefix="/chris-service", # doesn't work
)
api = Api(blueprint)
app.register_blueprint(blueprint)
...
And still no luck.
Update
So here's more information as per the comments (pseudo, but technically identical)
Access point for the swagger is my-company.com/chris (with or without http:// or https:// doesn't make difference)
When connecting to the above address, the request URL for the assets are my-company.com/swaggerui/swagger-ui.css
You can access the asset in my-company.com/chris/swaggerui/swagger-ui.css
So I my resolution (which didn't work) was to somehow change the root_path (not even sure if it's the correct wording), as shown in What I've looked and tried.
I've spent about a week to solve this but can't find a way.
Any help will be greatful :) Thanks
Swagger parameters defined at apidoc.py file. Default apidoc object also created in this file. So if you want to customize it you have change it before app and api initialization.
In your case url_prefix should be changed (I recommend to use environment variables to be able set url_prefix flexibly):
$ export URL_PREFIX='/chris'
from os import environ
from flask import Flask
from flask_restx import Api, apidoc
if (url_prefix := environ.get('URL_PREFIX', None)) is not None:
apidoc.apidoc.url_prefix = url_prefix
app = Flask(__name__)
api = Api(app)
...
if __name__ == '__main__':
app.run()
Always very frustrating when stuff is working locally but not when deployed to AWS. Reading this github issue, these 404 errors on swagger assets are probably caused by:
Missing javascript swagger packages
Probably not the case, since flask-restx does this for you. And running it locally should also not work in this case.
Missing gunicorn settings
Make sure that you are also setting gunicorn up correctly as well with
--forwarded-allow-ips if deploying with it (you should be). If you are in a kubernetes cluster you can set this to *
https://docs.gunicorn.org/en/stable/settings.html#forwarded-allow-ips
According to this post, you also have to explicitly set
settings.FLASK_SERVER_NAME to something like http://ec2-10-221-200-56.us-west-2.compute.amazonaws.com:5000
If that does not work, try to deploy a flask-restx example, that should definetely work. This rules out any errors on your end.

(Nginx) + Gunicorn + Flask + Bokeh on Azure with AD

(Apologies: I'm a webapp novice.)
REQUIREMENTS:
Azure Application Services Web App
Need Azure AD to authenticate users
Python Bokeh app to display data webapp once user is authenticated
ATTEMPT:
I tried wrapping a Flask app around Bokeh using this example After many iterations I can't get all traffic to flow to bokeh after authenticating. I've tried flask redirect(), and my latest iteration involves using requests' get() from the example in the link.
.
...
from requests import get
...
#app.route('/')
def bkapp_page():
script = server_document('https://myapp.azurewebsites.net/bkapp')
return render_template("embed.html", script=script, template="Flask")
#app.route('/bkapp', defaults={'path': ''})
#app.route('/bkapp/<path:path>")
def bkapp(path):
return get(f'http://localhost:5006/bkapp/{path}').content
...
PROBLEM:
- Despite many iterations, I can't get the template bokeh renders (which points to javascript the server serves) to point to a path that successfully renders the page. The best I have done is to get the /bkapp endpoint to not give me a 404 error.
- Python Bokeh runs on port 5006 and flask is running on 8000. I need to forward all traffic destined to bokeh (after authentication) to bokeh through flask. (Azure App Services appear to only allow one open port unless you customize a container, which I'd like to avoid.)
ALTERNATIVES?:
Sit nginx in front of gunicorn and use a 'reverse proxy' to send all traffic to bokeh. (Can I do this securely?) But I will have to create a custom container. I'd rather do this with just gunicorn + flask + bokeh, if possible.
Feel free to propose anything else. I'd like to use just gunicorn + flask + bokeh, but if I need a different method, I'll result to that.
Thanks!

Force HTTPS for heroku app on google domains

I am using the Automated Certificate Management through heroku in order to implement SSL for my application. My application will successfully connect securely using HTTPS if https://www.myapp.com is used, but if www.myapp.com or myapp.com is used, it defaults to HTTP.
In Heroku the domains that have been added are respectively as follows:
Domain Name: myapp.com, www.myapp.com
DNS Target: myapp.com.herokudns.com, www.myapp.com.herokudns.com
In google domains I have a subdomain forward record as follows:
myapp.com -> https://www.myapp.com
and under Custom resource records I have:
Name: www
Type: CNAME
Date: www.myapp.com.herokudns.com
Is there a way to force https through google domains or heroku-cli, or is this something I need to do in my Python app?
The easiest way is to use flask-sslify:
https://github.com/kennethreitz/flask-sslify
It turns every http request to your app into a https request
you only have to add one line of code to you app (or app factory):
from flask import Flask
from flask_sslify import SSLify
app = Flask(__name__)
sslify = SSLify(app)
flask-sslify doesn't seem to be maintained anymore. Heroku suggests looking at flask-talisman. But the csp requirements don't look trivial to me.
There really needs to be a simpler solution for this.

appengine python routing - confirming back in google app engine console

I have an app with 2 modules (default and module1). The dispatch.yaml looks below . ( The app is a python application deployed on google app engine).
application: myapp
dispatch:
# Default module serves the typical web resources and all static resources.
- url: "*/favicon.ico"
module: default
# Send all mobile traffic to the mobile frontend.
- url: "*/mobile/*"
module: module1
This seems to work successfully.
But after a live deployment is complete, where can we go and confirm the same in google engine console ( like cron.yaml lists the jobs ) about the current routing in place. This will be very useful to debug live issues, to understand current routing ( dispatch.yaml ) .
If you have dispatch rules active, they should be visible in the admin console under the "Main > Dispatch" left navigation link.
Unfortunately, you cannot see the routes in place declared the app.yaml file or ones created dynamically, for that matter, in the app engine dashboard. It's a limitation of the system.

Categories