Trying to connect Flask backend to React frontend using SSL - python

My backend is an AWS EC2 server that runs on flask, I'm trying to port over my code from http to https. I've used letsencrypt and certbot to create valid certificates. My flask backend accesses the certs and then calls the host. I tried adding SSLify after looking through other posts ( and seeing that it can be used with Flask, but it didn't change anything.
from flask_cors import CORS
from flask_sslify import SSLify
from OpenSSL import SSL
app = Flask(__name__)
CORS(app)
sslify = SSLify(app)
sslify
if __name__ == "__main__":
context = ("../../../../etc/letsencrypt/live/app.mydomain.name/fullchain.pem","../../../../etc/letsencrypt/live/app.mydomain.name/p$
app.run(host='127.0.0.1', port=5000, debug=True, ssl_context=context)
On the React frontend side, I'm using axios
axios.post('https://127.0.0.1:5000/run_query', postData, axiosConfig)
.then(function (response) {
console.log("Successful connection");
console.log(response.data);
influencerList = response.data.query_results;
console.log(influencerList);
currentComponent.setState({IL: influencerList});
})
.catch(function (error) {
console.log(error);
});
using app.run options I listed above on the backend returns
xhr.js:178 OPTIONS https://127.0.0.1:5000/run_query net::ERR_CONNECTION_REFUSED in the Google Chrome Browser Console.
I'm runnning nginx on Linux AMI. The certificates are for RehlOS/CentOS generated through letsencrypt and certbot. My Nginx error.log doesn't return anything, running the backend python file doesn't log anything anymore, like it did before going from HTTP. It just displays this:
* Running on https://ec2-34-209-86-220.us-west-2.compute.amazonaws.com:5000/ (Press CTRL+C to quit)
* Restarting with stat
I also tried changing the post URL to https://ec2-34-209-86-220.us-west-2.compute.amazonaws.com:5000/run-query instead of https://127.0.0.1:5000/run_query but got the exact same error.
I hope it's something simple, any help would be great. Thanks!

Related

i am trying to make a simple chat app with flask_socketio and it doesn't work properly

I am trying to create a simple chat app by using flask and flask_socketio and it works but the WebSocket upgrade doesn't happen and it gives me this error properly
The WebSocket transport is not available, you must install a WebSocket server that is compatible with your async mode to enable it. See the documentation for details. (further occurrences of this error will be logged with level INFO)
and I saw many tutorials and ended with installing (gunicorn, gevent, eventlet) and updating the packages and the library but when I run the app it works and the message sent from the client to the server and broadcasting it again to all the client but the connection is not websocket it's pooling
the server-side
from flask import Flask, app, render_template, redirect
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'test'
socketio = SocketIO(app)
#app.route('/')
def main():
return render_template('chat.html')
#socketio.on('message')
def message_func(data):
socketio.send(data)
if __name__ == '__main__':
socketio.run(app)
the client
var socket = io('http://' + document.domain + ':' + location.port);
socket.on('connect', function() {
console.log('connected');
})
socket.on('message', function(data) {
const p = document.createElement('p');
const br = document.createElement('br');
p.innerHTML = data;
document.querySelector('#panel').append(p);
})
document.querySelector('#send').onclick = () => {
socket.send(document.querySelector('#userin').value)
}
In the "Deployment: Gunicorn Web Server" documentation it says
In the documentation of When using gunicorn with the gevent worker
and the WebSocket support provided by gevent-websocket, the command
that starts the server must be changed to select a custom gevent web
server that supports the WebSocket protocol. The modified command is:
gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 module:app
For that to work you additionally need to install gevent-websocket.

Flask, FlaskSocketIO - RuntimeError: Cannot obtain socket from WSGI environment

When I try to use the functionality that uses websockets in my application, I get this error in the console:
File "/Users/user/venv/lib/python3.7/site-packages/simple_websocket/ws.py", line 138, in __init__
raise RuntimeError('Cannot obtain socket from WSGI environment.')
RuntimeError: Cannot obtain socket from WSGI environment.
I also get this error in the browser console:
WebSocket connection to 'ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=40NYzDgGYStMR0CEAAAJ' failed:
I tried using gevent, gevent-websocket, and eventlet, but this created other issues, and I'm not sure that I need to use gevent or eventlet for this use case.
Here's the rest of the relevant code:
__ init __.py
from flask_socketio import SocketIO
...
socketio = SocketIO()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
socketio.init_app(app, cors_allowed_origins='*')
...
return app
app.py
from app import create_app, socketio
app = create_app()
if __name__ == '__main__':
socketio.run(app)
routes.py
This only accepts POST requests because I send data to this route from a Celery task
from app import socketio
...
#main_bp.route('/send_message', methods=['POST'])
def send_message():
...
socketio.emit('test_message', {'msg':'Test'}, namespace='/test')
return 'Results Sent'
index.html
var socket = io.connect('http://localhost:5000/test');
socket.on('connect', function(){
console.log('Connected to socket')
});
socket.on('test_message', function(message){
console.log('Received test message');
}
)
Note that in the browser console I'll see "Connected to socket", but not "Received test message"
You are using the simple-websocket package. This package has a list of supported web servers. The error indicates that you are using a web server that is not in the supported list.
Supported web servers are:
The Flask dev server (for development purposes only, of course)
Gunicorn
Eventlet
Gevent
From this list, it seems your only choice for a production web server is Gunicorn, since you say that eventlet/gevent won't work for your needs.
The use of Gunicorn is covered in the documentation. In that section, simple-websocket is the third option mentioned. Here is the example start up command:
gunicorn -w 1 --threads 100 module:app
Maybe you can turn on the logger and the engine.io logger. that qould give you an idea what is the issue.
don't use eventlet and gevent together. use anyone and uninstall the other.

Why do I get CORS Error Reason: CORS request did not succeed

I am developing a svelte app which uses a FastAPI backend to interact with a PostgreSQL database which is local to my organization, not on my AWS EC2 instance.
I use javascript's fetch api to send requests to my backend to query the database for various things, on my local machine all of these requests are received on both ends, on AWS however the requests from my frontend are not even recognized by the fastAPI backend.
I am fairly inexperienced with setting up servers like this so I assume there is some basic mistake in my setup of these servers that makes communication between them on AWS like this impossible.
The fastapi code looks like this:
import uvicorn
from fastapi import (Depends, FastAPI, HTTPException, Query,
Request)
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(debug=True)
origins = [
'*'
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
#app.get("/")
async def root():
return {"message": "Hello World"}
if __name__ == '__main__':
uvicorn.run(app, host="0.0.0.0", port=8000)
My fetch requests look like this typically:
const endpoint = public_IP:8000 + `/`;
let objs = [];
fetch(endpoint)
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
When I send a fetch request like the above one I get a CORS error with reason: CORS request did not succeed. The FastAPI server, has no feedback, the request never seems to reach it, I am unsure why. As far as I know this usually has to do with issues between requests with HTTP and HTTPS, but these don't seem relevant to my issue as no part of my setup is not using HTTP. When using chrome I get the error: Failed to load resource: net::ERR_CONNECTION_REFUSED as well.
My AWS security groups for my EC2 instance allow TCP trafic from any IP to ports 5000 and 8000. Which is where my two apps are hosted, Svelte and FastAPI respectively.
Most likely, your endpoint in the fastapi app is raising an exception. When this arise, the CORSMiddleware installed via app.add_middleware(...) does not see any HTTP response and has no chance to do its job on the HTTP response produced by FastAPI error handler.
The solution is to wraps the whole FastAPI app with the CORSMiddleware as in :
import uvicorn
from fastapi import (Depends, FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(debug=True)
origins = [
'*'
]
#app.get("/")
async def root():
return {"message": "Hello World"}
app = CORSMiddleware(
app=app,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
if __name__ == '__main__':
uvicorn.run(app, host="0.0.0.0", port=8000)
Summary
CORS errors are can be so confusing sometimes because, the error messages are not clear enough and there is a lot of possible solutions.
First possibility
If you load your page from a given host, in your case 0.0.0.0 and then try to make an request to another host, for example 127.0.0.1, the request gets blocked to prevent XSS attacks
Changing your host from 0.0.0.0 to 127.0.0.1 should fix this.
Second possibility
If you are a Mozillian, there is bug(s)[see, see] in Mozilla, but if this the case your code should be working in other browser
Solution: You must enable the certificate on each port indivually.
Other possibilities
Try changing your endpoint this
const endpoint = public_IP:8000 + `/`;
to this
const endpoint = `/`;

Usage custom namespace Flask Socket io gives an errors

I've used perfect Flask-SocketIO library with Python 3 for couple of months. Everything worked as expected until the last couple of days.
All works fine, if namespace for connection to websocket server stay default /socket.io. But I'm geting an error now if I trying to change namespace for connection to python flask-socketio backend.
My app.py:
from flask import Flask, session, request, render_template, copy_current_request_context
from flask_cors import CORS, cross_origin
import flask_socketio as socketio
import ujson
async_mode = 'threading'
namespace = '/mynamespace'
app = Flask(__name__)
CORS(app)
app.config['SECRET_KEY'] = 'secret!'
sio = socketio.SocketIO(app, async_mode=async_mode)
#sio.on('connect', namespace=namespace)
def connect():
logging.info('Connected')
#sio.on('disconnect', namespace=namespace)
def disconnect():
logging.info('Disconnected')
#app.route("/home/index")
def home():
return render_template('index.html',
async_mode=sio.async_mode)
I'm using ./main.py to run the server, main.py contains:
from app import app, sio
if __name__ == "__main__":
sio.run(app, debug=False)
My template/index.html contains ton of code js, but I think most valuable I loading the socketio from cdn in a head:
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
... and I using connect with custom namespace path, as in docs:
namespace = '/mynamespace/socket.io';
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port,
{path: namespace});
socket.on('connect', function() {
socket.emit('Connected to server');
})
As I understand, By default socketio library trying to connect to backend with emitting connect message to namespace. During loading 'index.html' template on '/home/index' route, logging the errors to console:
Flask server also gives and 404 error:
My best guess: at this moment it looks like something changed in client-side JS library or in chrome browser itself(few days ago I updated Chrome).
Maybe I just understood wrong one small detail. I really appreciate some help with this problem.
Stack versions:
Python 3.7.2,
Flask 1.0.2,
Flask-SocketIO 3.3.1,
socketio.min.js
1.3.5,
Google Chrome 77.0.3865.90 (64 bit)
You are confusing namespace with path, which are completely different things. The path is the endpoint URL where the Socket.IO server is listening. The namespace is a protocol feature of Socket.IO that allows multiplexing of multiple logical connections into a single physical connection.

can you add HTTPS functionality to a python flask web server?

I am trying to build a web interface to Mock up a restful interface on networking device this networking device uses Digest Authentication and HTTPS.
I figured out how to integrate Digest Authentication into the web server but I cannot seem to find out how to get https using FLASK if you can show me how please comment on what i would need to do with the code below to make that happen.
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/')
def index():
return 'Flask is running!'
#app.route('/data')
def names():
data = {"names": ["John", "Jacob", "Julie", "Jennifer"]}
return jsonify(data)
if __name__ == '__main__':
app.run()
Don't use openssl or pyopenssl its now become obselete in python
Refer the Code below
from flask import Flask, jsonify
import os
ASSETS_DIR = os.path.dirname(os.path.abspath(__file__))
app = Flask(__name__)
#app.route('/')
def index():
return 'Flask is running!'
#app.route('/data')
def names():
data = {"names": ["John", "Jacob", "Julie", "Jennifer"]}
return jsonify(data)
if __name__ == '__main__':
context = ('local.crt', 'local.key')#certificate and key files
app.run(debug=True, ssl_context=context)
this also works in a pinch
from flask import Flask, jsonify
from OpenSSL import SSL
context = SSL.Context(SSL.PROTOCOL_TLSv1_2)
context.use_privatekey_file('server.key')
context.use_certificate_file('server.crt')
app = Flask(__name__)
#app.route('/')
def index():
return 'Flask is running!'
#app.route('/data')
def names():
data = {"names": ["John", "Jacob", "Julie", "Jennifer"]}
return jsonify(data)
#if __name__ == '__main__':
# app.run()
if __name__ == '__main__':
app.run(host='127.0.0.1', debug=True, ssl_context=context)
To run HTTPS functionality or SSL authentication in your flask application, first install "pyOpenSSL" python package
pip install pyopenssl
Next step is to create cert.pem and key.pem
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
Copy generated cert.pem and key.pem in your flask application project
Add ssl_context=('cert.pem', 'key.pem') in app.run(), like in the example below.
from flask import Flask, jsonify
app = Flask(__name__)
#app.route("/")
def index():
return "Flask is running!"
#app.route("/data")
def names():
data = {"names": ["John", "Jacob", "Julie", "Jennifer"]}
return jsonify(data)
if __name__ == "__main__":
app.run(ssl_context=("cert.pem", "key.pem"))
The top-scoring answer has the right idea, but the API seems to have evolved so that it no longer works as when it was first written, in 2015.
In place of this:
from OpenSSL import SSL
context = SSL.Context(SSL.PROTOCOL_TLSv1_2)
context.use_privatekey_file('server.key')
context.use_certificate_file('server.crt')
I used this, with Python 3.7.5:
import ssl
context = ssl.SSLContext()
context.load_cert_chain('fullchain.pem', 'privkey.pem')
and then supplied the SSL context in the Flask.run call as it said:
app.run(…, ssl_context=context)
(My server.crt file is called fullchain.pem and my server.key is called privkey.pem. These files were supplied to me by my LetsEncrypt Certbot.)
Deploy Flask on a real web server, rather than with the built-in (development) server.
See the Deployment Options chapter of the Flask documentation. Servers like Nginx and Apache both can handle setting up HTTPS servers rather than HTTP servers for your site.
The standalone WSGI servers listed would typically be deployed behind Nginx and Apache in a proxy-forwarding configuration, where the front-end server handles the SSL encryption for you still.
For a quick n' dirty self-signed cert, you can also use flask run --cert adhoc or set the FLASK_RUN_CERT env var.
$ export FLASK_APP="app.py"
$ export FLASK_ENV=development
$ export FLASK_RUN_CERT=adhoc
$ flask run
* Serving Flask app "app.py" (lazy loading)
* Environment: development
* Debug mode: on
* Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 329-665-000
The adhoc option isn't well documented (for good reason, never do this in production), but it's mentioned in the cli.py source code.
There's a thorough explanation of this by Miguel Grinberg at Running Your Flask Application Over HTTPS.
If this webserver is only for testing and demoing purposes. You can use ngrok, a open source too that tunnels your http traffic.
Bascially ngrok creates a public URL (both http and https) and then tunnels the traffic to whatever port your Flask process is running on.
https://ngrok.com/product
It only takes a couple minutes to set up. You first have to download the software. Then run the command
./ngrok http [port number your python process is running on]
It will then open up a window in terminal giving you both an http and https url to access your web app.
Super simple:
app.py
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
run:
$ openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
$ flask run --cert=cert.pem --key=key.pem
You can use mkcert to generate certificate that is trusted by browsers.
mkcert example.com "*.example.com" example.test localhost 127.0.0.1 ::1
Browser will trust all of the following domains and IPs
- "example.com"
- "*.example.com"
- "example.test"
- "localhost"
- "127.0.0.1"
- "::1"
Python Code:
from flask import Flask, jsonify
app = Flask(__name__)
#app.route("/")
def index():
return "Welcome to the Python Flask's Index!"
if __name__ == "__main__":
app.run(port=443, ssl_context=("localhost+3.pem", "localhost+3-key.pem"))
Screenshot:

Categories