Attempting to use LetsEncrypt to run SSL-wrapped BaseHTTPServer in Python fails - python

I had a working HTTP server using BaseHTTPServer in Python, so I attempted to add an SSL cert to allow for https using LetsEncrypt, and now it won't serve any files or respond. No exceptions or errors thrown, nor will it serve any content.
server_address = ('0.0.0.0', 80)
httpd = HTTPServer(server_address, MyHandler)
# I can comment out the following line and it'll work
httpd.socket = ssl.wrap_socket(httpd.socket, keyfile=ssl_key, certfile=ssl_cert, server_side=True)
httpd.serve_forever()
#ssl_key = '/etc/letsencrypt/live/example.com/privkey.pem'
#ssl_cert = '/etc/letsencrypt/live/example.com/fullchain.pem'
Where MyHandler is the following:
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(204)
self.send_header("Content-Type", "text/html")
self.end_headers()
return
def do_POST(self):
self.send_response(204)
self.send_header("Content-Type", "text/html")
self.end_headers()
return
Attempting to access the site via web browser from https://example.com returns a standard no-response "Server not found".
I followed the following instructions to generate a certificate using LetsEncrypt: https://certbot.eff.org/#ubuntuxenial-other
sudo apt-get install letsencrypt
Followed by:
letsencrypt certonly --standalone -d example.com
Is there any way I can easily figure out what the problem is here? Using Python 3.5. Happy to provide additional info if needed.

server_address = ('0.0.0.0', 80)
Attempting to access the site via web browser from https://example.com returns a standard no-response "Server not found".
https://host without explicit port specification means that the server is accessed on the default port for the https protocol, which is 443. But, you have setup your server to use port 80 in server_address.
There are two ways to fix this: either explicitly specify a non-standard port for https in the URL, i.e. https://host:80 or change the port in server_address from 80 to 443. The last option is probably the better one.

Related

How do I make a Python server public?

Using this code:
from http import server
class Serv(server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
server.HTTPServer(('host', 80), Serv).serve_forever()
I have tried using my public IP, which didn't work, my private IP, which only worked from the same network, and localhost, which is my PC only. How can I change the host so when someone connects to my IP, it connects to my website (from my code)? Do I need my router to redirect to my PC? I know host can be my private IP or localhost, are there any other hosts I can use?
Edit: I saw an answer in another question that used Flask, I'd like to not use any dependencies as of now.
If you want to make it work from anywhere,
your application should listen on IP address 0.0.0.0
which means (in short) any IPv4 address at all :
from http import server
class Serv(server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b'Hello, world!')
server.HTTPServer(('', 80), Serv).serve_forever()
If you are in a recent linux/GNU based OS, you may check listening ports with :
ss -ltpn
Note that after that modification, you will depend of any proxy/firewall between you and your server to get your application response.

Dual HTTP and HTTPS Python server based on BaseHTTPServer not working as expected

I'm trying to implement a Python server supporting both HTTP and HTTPS based in BaseHTTPServer. This is my code:
server_class = BaseHTTPServer.HTTPServer
# Configure servers
httpd = server_class(("0.0.0.0", 1044), MyHandler)
httpsd = server_class(("0.0.0.0", 11044), MyHandler)
httpsd.socket = ssl.wrap_socket(httpsd.socket, keyfile="/tmp/localhost.key", certfile="/tmp/localhost.crt", server_side=True)
# Run the servers
try:
httpd.serve_forever()
httpsd.serve_forever()
except KeyboardInterrupt:
print("Closing the server...")
httpd.server_close()
httpsd.server_close()
So, HTTP runs in port 1044 and HTTPS runs in 11044. The MyHandler class is omitted for the sake of briefness.
Using that code, when I send requests to HTTP port (e.g. curl http://localhost:1044/path) it works. However, when I send requests to the HTTPS port (e.g. curl -k https://localhost:11104/path) the server never responses, i.e. the curl terminal gets hanged.
I have observed that if I comment the line starting the HTTP server (i.e. httpd.server_forever()) then the HTTPS server works,.i.e. curl -k https://localhost:11104/path works. Thus, I guess that I'm doing something wrong which is precluding not being able to set both servers at the same time.
Any help is appreciated!
Following feedback comments, I have refactored the code in a multithread way and now it works as expected.
def init_server(http):
server_class = BaseHTTPServer.HTTPServer
if http:
httpd = server_class(("0.0.0.0", 1044), MyHandler)
else: # https
httpd = server_class(("0.0.0.0", 11044), MyHandler)
httpd.socket = ssl.wrap_socket(httpd.socket, keyfile="/tmp/localhost.key", certfile="/tmp/localhost.crt", server_side=True)
httpd.serve_forever()
httpd.server_close()
VERBOSE = "True"
thread.start_new_thread(init_server, (True, ))
thread.start_new_thread(init_server, (False, ))
while 1:
time.sleep(10)

Add https redirect in Python3 Mockserver

we have this mockserver that is now serving https:// requests, and if we remove the ssl wrapping (ssl.wrap_socket(myServer.socket,keyfile='key.pem',certfile= 'cert.pem', server_side=True), the server only serves http:// requests. Is there any way where we can make this server to support both requests. Our objective is when the server receives an http:// request, it will automatically convert it as https:// and process the request.
Thanks in advance for the support
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl
class Mock(BaseHTTPRequestHandler):
-------------------
-------------------
def main():
global hostname, port
hostname = "127.0.0.1"
port = 8000
myServer = HTTPServer((hostname, port), Mock)
myServer.socket = ssl.wrap_socket(myServer.socket,keyfile='key.pem',certfile= 'cert.pem', server_side=True)
myServer.serve_forever()
if __name__ =="__main__":
main()
If the HTTP and HTTPS servers need different functionality, then it makes sense to make them two different instances. Why not make a second HTTPServer that is only HTTP that simply returns a 302 status with the Location header pointing to the HTTPS mock server (but using the same path).

Accessing python web server from external machine

I am experiencing problems with visibility / accessibility of my python web server running on Ubuntu. Server code is below:
#!/usr/bin/python
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
PORT_NUMBER = 8899
#This class will handles any incoming request from
#the browser
class myHandler(BaseHTTPRequestHandler):
#Handler for the GET requests
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
# Send the html message
self.wfile.write("Hello World !")
return
try:
#Create a web server and define the handler to manage the
#incoming request
server = HTTPServer(('', PORT_NUMBER), myHandler)
print 'Started httpserver on port ' , PORT_NUMBER
#Wait forever for incoming htto requests
server.serve_forever()
except KeyboardInterrupt:
print '^C received, shutting down the web server'
server.socket.close()
Calling it locally using curl with below command works - I receive answer with 'hello world'.
curl {externalIP}:8899
Opening address in the browser (chrome, ie) fails!
http://{externalIP}:8899/
ufw status is inactive
iptables as below
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- anywhere anywhere tcp dpt:8765
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Ubuntu has apache2 server installed and opening html files using web browser, external ip and port 80 is working with no problem from above server...
Any ideas what else could I check?
I think you might be listening on the loopback interface and not the one that is connected to the internet.
Either specify IP or use:
server = HTTPServer(('0.0.0.0', PORT_NUMBER), myHandler)
to specify to listen to all your network interfaces.
Removing apache fixed this case. I do not know why, because it should block port 80, but it works now after this:
apt-get remove apache2*

How to redirect HTTP requests on a Python App on Bluemix to HTTPS only?

I have python app on Bluemix and would like to make it accessible over https only. By default I can connect both via http and https. Want to restrict access via https only. So what is the best way to disable http access, or redirect request to https only?
As ralphearle mentioned in his answer, Bluemix proxy server terminates the SSL, so you can look into the X-Forwarded-Proto header to find out if request comes from http or https.
See below a simple example based on the Python starter code from Bluemix. I added the RedirectHandler class to check for the X-Forwarded-Proto header value and redirects the request to https if it not https.
import os
try:
from SimpleHTTPServer import SimpleHTTPRequestHandler as Handler
from SocketServer import TCPServer as Server
except ImportError:
from http.server import SimpleHTTPRequestHandler as Handler
from http.server import HTTPServer as Server
class RedirectHandler(Handler):
def do_HEAD(self):
if ('X-Forwarded-Proto' in self.headers and
self.headers['X-Forwarded-Proto'] != 'https'):
self.send_response(301)
self.send_header("Location", 'https://' + self.headers['Host'] + self.path)
self.end_headers()
def do_GET(self):
self.do_HEAD()
Handler.do_GET(self)
# Read port selected by the cloud for our application
PORT = int(os.getenv('PORT', 8000))
# Change current directory to avoid exposure of control files
os.chdir('static')
httpd = Server(("", PORT), RedirectHandler)
try:
print("Start serving at port %i" % PORT)
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
The Bluemix proxy server terminates the SSL, so that all traffic looks like HTTP to your app. However, the proxy also adds a special HTTP header named $WSSC with a value that can be either http or https. Check this header and, if the value is set to http, then change it to https.
As Adam pointed out in his comment, the IBM forum has further discussion of this approach: https://developer.ibm.com/answers/questions/16016/how-do-i-enforce-ssl-for-my-bluemix-application.html

Categories