Moving from localhost to real URL in Flask? - python

I'm developing a site with Python and Flask and want to move from localhost in my browser to my team's development server and development site but I'm having problems. I've already looked at the Flask documentation, which recommends using host='0.0.0.0' or specifying SERVER_NAME. Thusfar, neither of these things have helped. Here's a bit of code to show what I'm doing:
app = Flask(__name__)
if __name__ == '__main__':
app.secret_key = 'super secret key'
app.config['SERVER_NAME'] = 'https://example.org/blah'
app.run(host=app.config['SERVER_NAME'], port=4343, debug=True)
As you can see, instead of localhost:500, I want to be able to go into my browser and visit 'https://example.org/blah' and see the same things.
With this piece of code, I get this error:
(py34)user:~/flask$ python app.py
INFO - Connection with server established!
INFO - Server version meets recommendations (v2.9)
Traceback (most recent call last):
File "app.py", line 18, in <module>
app.run(host=app.config['SERVER_NAME'], port=4343, debug=True)
File "/home/me/anaconda3/envs/py34/lib/python3.4/site-packages/flask/app.py", line 772, in run
run_simple(host, port, self, **options)
File "/home/me/anaconda3/envs/py34/lib/python3.4/site-packages/werkzeug/serving.py", line 674, in run_simple
s.bind((hostname, port))
socket.gaierror: [Errno -2] Name or service not known
If instead of using SERVER_NAME I use host='0.0.0.0', I don't get any errors and it will successfully start "Running on http://0.0.0.0:4343/" but I can't follow that url into my browser the same way I can when I connect to my localhost.
What do I need to do to get my flask app running on https://example.org/blah?
Also, if it helps to know, I have to ssh into my server.

If you run with host='0.0.0.0' from the server, you should be able to navigate to example.org:4343 and see your app.
Additionally, SERVER_NAME requires the port, per the documentation:
the name and port number of the server. Required for subdomain support (e.g.: 'myapp.dev:5000')
Finally, if you want to show you app without a port number, you either need to run it at port 80 (or 443) with root permissions, or using Nginx/Apache to route the request from your domain to the app.

Related

Running cherrypy app on AWS: socket could not be created

I have developed a small web app on my Windows PC and tested it locally.
Then I wanted to transfer it to an AWS Ubuntu 18 instance.
For the sake of brevity, the app processes a form from a webpage and redirects a user to the page with the result. The contents of the app do not really matter, since the error occurs before you can interact with it.
I have not encountered any issues running it locally (i.e. the same code as below, w/out cherrypy.config.update).
import cherrypy
import os
filepath = os.getcwd()
class MyApp(object):
#cherrypy.expose
def index(self):
with open('./index.html', 'r') as f:
text = f.read()
return text
#cherrypy.expose
def process_filled_form(self, field1, field2):
out = filed1+field2
return(out)
if __name__ == '__main__':
config = {"/folder": {"tools.staticdir.on": True,
"tools.staticdir.dir": os.path.join(filepath, "folder")}}
cherrypy.config.update({'server.socket_host': 'XXX.XXX.XXX.XXX',
'server.socket_port': 9028,})
cherrypy.quickstart(MyApp(), '/', config)
But when I try to make the app public on an AWS instance, I get the following error:
[06/Mar/2021:06:25:12] ENGINE Listening for SIGTERM.
[06/Mar/2021:06:25:12] ENGINE Listening for SIGHUP.
[06/Mar/2021:06:25:12] ENGINE Listening for SIGUSR1.
[06/Mar/2021:06:25:12] ENGINE Bus STARTING
[06/Mar/2021:06:25:12] ENGINE Started monitor thread 'Autoreloader'.
[06/Mar/2021:06:25:12] ENGINE Error in HTTP server: shutting down
Traceback (most recent call last):
File "/home/user/.conda/envs/user_app/lib/python3.9/site-packages/cherrypy/process/servers.py", line 225, in _start_http_thread
self.httpserver.start()
File "/home/user/.conda/envs/user_app/lib/python3.9/site-packages/cheroot/server.py", line 1836, in start
self.prepare()
File "/home/user/.conda/envs/user_app/lib/python3.9/site-packages/cheroot/server.py", line 1791, in prepare
raise socket.error(msg)
OSError: No socket could be created -- (('XXX.XXX.XXX.XXX', 9028): [Errno 99] Cannot assign requested address)
[06/Mar/2021:06:25:12] ENGINE Bus STOPPING
[06/Mar/2021:06:25:12] ENGINE HTTP Server cherrypy._cpwsgi_server.CPWSGIServer(('XXX.XXX.XXX.XXX', 9028)) already shut down
[06/Mar/2021:06:25:12] ENGINE Stopped thread 'Autoreloader'.
[06/Mar/2021:06:25:12] ENGINE Bus STOPPED
[06/Mar/2021:06:25:12] ENGINE Bus EXITING
[06/Mar/2021:06:25:12] ENGINE Bus EXITED
Changing the socket does not affect anything. The app produces no errors when launched locally on AWS.
I am totally new to anything web-related, so the problem might be smt very obvious. Perhaps, I am missing something in the config, or `quickstart` may not be the right option to launch the app.
Please help
Most likely you are binding to a wrong address.
In you config.update call, modify the socket_host value to 0.0.0.0:
cherrypy.config.update({'server.socket_host': '0.0.0.0',
'server.socket_port': 9028,})
That should allow your app to listen for requests on all the network addresses that are available in your host.

Secure communication between flask-socketio server and python-socketio

I am working on a project using flask-socketio and python-socketio. In order to secure the communication, the system needs to be upgraded with SSL. I am working with Mac and Python 3.7.
I created a certificate using openssl (rootCA.pem) and added it to the Mac's keychain. After that, I issued server.cert and server.key with the rootCA.pem. I built the web server using flask-socketio, added server.cert and server.key on its side. In the end, the website worked well with ssl.
The problem happened when I wanted to secure the communication between flask-socketio server and python-socketio client. When the client tried to connect with server, the connection was refused and raised unknown ca error:
ssl.SSLError: [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:2488)
I did some researches and realized that this problem may be due to that Python 3.7 uses its own private copy of OpenSSL. Because of that, the SocketIO client was unable to verify my self-signed certificate. And this is the information from Python 3.7's installation:
This variant of Python 3.7 includes its own private copy of OpenSSL
1.1.1. The deprecated Apple-supplied OpenSSL libraries are no longer used. This means that the trust certificates in system and user
keychains managed by the Keychain Access application and the security
command line utility are no longer used as defaults by the Python ssl
module. A sample command script is included in /Applications/Python
3.7 to install a curated bundle of default root certificates from the third-party certifi package (https://pypi.org/project/certifi/). If
you choose to use certifi, you should consider subscribing to the
project's email update service to be notified when the certificate
bundle is updated.
So I went to the folder of /Applications/Python 3.7 and executed the Install Certificates.command.
However, it is not the key to the problem. Because python certifi is a carefully curated collection of Root Certificates. It, of course, does not contain my self-signed certificate. So, to solve the problem, I need to let the Python find the rootCA.pem.
In the folder of python certifi, there is a document named cacert.pem. This contains the Root Certificates. So I added the content of rootCA.pem in the end of cacert.pem.
After that, I tried this code in the command line:
openssl verify -CAfile cacert.pem server.crt
And the output:
server.crt: OK
I think, in this case, the cacert.pem can verify the certificate of the server. I know it is not an elegant solution. Please let me know if you have better way to make python find my self-signed certificate.
After that, I tried to connect to flask-socketio server with the python-socketio. This time, I got another error. On the server side, the log information is:
(57183) accepted ('127.0.0.1', 56322)
0fd838477c534dfda802bfb0d130d358: Sending packet OPEN data {'sid': '0fd838477c534dfda802bfb0d130d358', 'upgrades': ['websocket'], 'pingTimeout': 60000, 'pingInterval': 25000}
0fd838477c534dfda802bfb0d130d358: Sending packet MESSAGE data 0
127.0.0.1 - - [10/Oct/2019 08:50:36] "GET /socket.io/?transport=polling&EIO=3&t=1570706436.665149 HTTP/1.1" 200 349 0.000436
(57183) accepted ('127.0.0.1', 56324)
http://localhost:3000 is not an accepted origin.
127.0.0.1 - - [10/Oct/2019 08:50:36] "GET /socket.io/?transport=websocket&EIO=3&sid=0fd838477c534dfda802bfb0d130d358&t=1570706436.685631 HTTP/1.1" 400 122 0.000182
And on the client side, the error was thrown like this:
Traceback (most recent call last):
File "simple_client.py", line 18, in <module>
sio.connect('https://localhost:3000', namespaces="/channel_A")
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/socketio/client.py", line 262, in connect
engineio_path=socketio_path)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/engineio/client.py", line 170, in connect
url, headers, engineio_path)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/engineio/client.py", line 308, in _connect_polling
if self._connect_websocket(url, headers, engineio_path):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/engineio/client.py", line 346, in _connect_websocket
cookie=cookies)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_core.py", line 514, in create_connection
websock.connect(url, **options)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_core.py", line 226, in connect
self.handshake_response = handshake(self.sock, *addrs, **options)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_handshake.py", line 79, in handshake
status, resp = _get_resp_headers(sock)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_handshake.py", line 160, in _get_resp_headers
raise WebSocketBadStatusException("Handshake status %d %s", status, status_message, resp_headers)
websocket._exceptions.WebSocketBadStatusException: Handshake status 400 BAD REQUEST
I have no idea why does it happen. The communication between flask-socketio server and python-socketio client works well without SSL. So I think there might be nothing wrong with the code, but I still give the code here. And this is for server:
from flask import Flask, render_template
from flask_socketio import SocketIO
from flask_socketio import Namespace
import eventlet
app = Flask(__name__, template_folder="templates")
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, engineio_logger=True, logger=True)
# Create a URL route in our application for "/"
#app.route('/')
def home():
"""
This function loads the homepage
"""
return render_template('index.html')
class MyCustomNamespace(Namespace):
def on_connect(self):
print("Client just connected")
def on_disconnect(self):
print("Client just left")
def on_messages(self, data):
print(f"\nReceived data from client: \n {data}\n")
return data
socketio.on_namespace(MyCustomNamespace('/channel_A'))
if __name__ == "__main__":
eventlet.wsgi.server(
eventlet.wrap_ssl(eventlet.listen(("localhost", 3000)),
certfile='server.crt',
keyfile='server.key',
server_side=True), app)
# socketio.run(app, host="localhost", port=3000, debug=True)
This is for client:
import socketio
sio = socketio.Client()
def message_received(data):
print(f"Message {data} received")
#sio.on('connect', namespace="/channel_A")
def on_connect():
print("Connect...")
#sio.on('disconnect', namespace="/channel_A")
def on_disconnect():
print(f"Disconnected from server")
sio.connect('https://localhost:3000', namespaces="/channel_A")
Please help! I know my question is wordy. But I just want to show the way I tried to solve the problem. If there is anything wrong, please let me know. Thank you!
Recent versions of Flask-SocketIO come configured with the most secure settings with regards to cross-origins setups, which is to only allow the same origin. If your frontend app and your Flask app are running on different servers or ports, then you have to configure cross-origin so that they can work together.
For example, if your frontend is hosted at http://localhost:3000, you can allow that as an origin with:
socketio = SocketIO(app, cors_allowed_origins='http://localhost:3000')

python flask...My computer couldn't display

This is code from flask homepages.
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(host='0.0.0.0')
app.run(debug=True)
when I run the code then enter http://0.0.0.0:5000/
My computer couldn't display...
what is the problem?
1) That isn't the code on the flask homepage.
2) Calling run() twice won't work. If the first run() will finish executing and get to the second run(), wouldn't the second run() finish executing and get to the end of the program and terminate?
app.run() is defined like this:
run(host=None, port=None, debug=None, **options)
Changed in version 0.10: The default port is now picked from the SERVER_NAME variable.
Parameters:
host – the hostname to listen on. Defaults to '127.0.0.1'.
Set this to '0.0.0.0' to have the server available externally as well.
port – the port of the webserver. Defaults to 5000 or the port defined in the SERVER_NAME config variable if present.
debug – if given, enable or disable debug mode. See debug.
options – the options to be forwarded to the underlying Werkzeug server. See werkzeug.serving.run_simple() for more information.
You can set all those parameters in one app.run() call.
3) From the flask docs:
If you run the server [with app.run()] you will notice that the
server is only accessible from your own computer, not from any other
in the network. This is the default because in debugging mode a user
of the application can execute arbitrary Python code on your computer.
If you have debug disabled or trust the users on your network, you can
make the server publicly available simply by changing the call of the
run() method to look like this:
app.run(host='0.0.0.0')
This tells your operating system to listen on all public IPs.
If you try setting the host to 0.0.0.0, then start your server, flask will display the message:
Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
However, that does not mean that 0.0.0.0 is a host name(= an IP address). 0.0.0.0 means that the server is listening for any public host name(= an IP address) on port 5000. The url http://localhost:5000 will also work.
The bottom line: Don't use the host 0.0.0.0 unless you know what you are doing.
I was facing the same problem and i found out that I had to save any code i had written in all my files then after i run then main.py file the web server showed up. So it might be the same situation, just hit cntr-s and save all code in the different .py files then run it

How to get CherryPy to listen only on a specific host

I have a flask app that I want to deploy using CherryPy's built in server. I chose CherryPy so that the app can be deployed without having to reverse proxy (ie. nginx in front).
I'm having trouble getting CherryPy to listen for requests on just a single hostname.
Say I'm serving 2 sites: test1.com and test2.com (and have them set in my hosts file to point back to localhost).
My /etc/hosts file:
127.0.0.1 test1.com test2.com
CherryPy is serving test1.com, test2.com doesn't have anything serving it.
My cherrypy file is as follows:
import cherrypy
from my_test_flask_app import app
if __name__ == '__main__':
cherrypy.tree.graft(app, "/")
cherrypy.server.unsubscribe()
server = cherrypy._cpserver.Server()
server.socket_host = "test1.com"
server.socket_port = 8030
server.thread_pool = 30
server.subscribe()
cherrypy.engine.start()
cherrypy.engine.block()
Set up this way, I go to test1.com:8030 on my browser and it works as expected.
But when I go to test2.com:8030, the same app is served. I expected it not to serve anything, since CherryPy isn't set up to listen for test2.com.
To me, it seems that CherryPy is just listening for everything on the given port (8030), and treating the socket_host part as if its 0.0.0.0
Am I missing something here? I've looked through lots of docs and tutorials, but all things suggest that this code snippet should be working as I expected.
Thanks
Here's how you can setup what you want...
root = Root()
RootApp = cherrypy.Application(root)
Domain2App = cherrypy.Application(root)
SecureApp = cherrypy.Application(Secure())
vhost = cherrypy._cpwsgi.VirtualHost(RootApp,
domains={'www.domain2.example': Domain2App,
'www.domain2.example:443': SecureApp,
})
cherrypy.tree.graft(vhost)
https://cherrypy.readthedocs.org/en/3.3.0/refman/_cpwsgi.html#classes
Hope this helps!
You misunderstand the socket listen address - they are IP addresses only, not on DNS names. Set this way, CherryPy listens to the localhost (127.0.0.1) only - try using your Ethernet/Wlan local address and you should get connection refused.
Also, you can wrap your application with a WSGI middleware that checks the Host header for the proper domain, or use CherryPy virtual host facility to check the host header.

How to very SIMPLY deploy a Twisted Server on OPenshift

I've got the right environment setup, python 2.7.5, Twisted installed and imports work in the Python Shell.
I have a very simple Server instance to display a landing page that Works on local machine fine.
from twisted.web import http
class MyRequestHandler(http.Request):
pages={
'/': '<h1>Geo-Address Server</h1>Twisted Server is Up and Running..',
'/test': '<h1>Test</h1>Test page',
}
def process(self):
print self.path
if self.pages.has_key(self.path):
self.write(self.pages[self.path])
else:
self.setResponseCode(http.NOT_FOUND)
self.write("<h1>Not Found</h1>Sorry, page does not exist")
self.finish()
class MyHttp(http.HTTPChannel):
requestFactory=MyRequestHandler
class MyHttpFactory(http.HTTPFactory):
protocol=MyHttp
if __name__=='__main__':
from twisted.internet import reactor
reactor.listenTCP(8080, MyHttpFactory())
reactor.run()
However, deploying this on the Openshift Server fails to run. If I try to run the script
python script.py &
I get:
reactor.listenTCP(8080, MyHttpFactory()) File
"/var/lib/openshift/5378ea844382ec89da000432/python/virtenv/lib/python2.7/site-packages/twisted/internet/posixbase.py",
line 495, in listenTCP
p.startListening() File "/var/lib/openshift/5378ea844382ec89da000432/python/virtenv/lib/python2.7/site-packages/twisted/internet/tcp.py",
line 979, in startListening
raise CannotListenError(self.interface, self.port, le) twisted.internet.error.CannotListenError: Couldn't listen on any:8080:
[Errno 13] Permission denied.
Reading through SO, most people just say to bind to port 8080(which I have done), but still I get the same error.
As the kb says
Please note: We don't allow arbitrary binding of ports on the
externally accessible IP address.
It is possible to bind to the internal IP with port range: 15000 -
35530. All other ports are reserved for specific processes to avoid conflicts. Since we're binding to the internal IP, you will need to
use port forwarding to access it:
https://openshift.redhat.com/community/blogs/getting-started-with-port-forwarding-on-openshift
Therefor, just find out the $OPENSHIFT_PYTHON_IP
echo $OPENSHIFT_PYTHON_IP
Then add that address to the reactor listener's interface
reactor.listenTCP(15000, MyHttpFactory(), interface='127.X.X.X')
Alternative(and the best) way to do it is by picking the value in the code. That way, if the IP changes dynamically, you still get the current IP
import os
local_hostname =os.getenv("OPENSHIFT_INTERNAL_IP")
..
..
reactor.listenTCP(15000, MyHttpFactory(), interface=local_hostname)
Note: You can only bind to the port range (15000-35530)

Categories