Simple HTTP Server SSL record_too_long error - python

I am trying to run a few Python CGI scripts using CGIHTTPServer. This works fine, but now I'd like to run using SSL on the Web listener.
I started with the solution at Activestate code recipes and changed SimpleHTTPRequestHandler to CGIHTTPRequestHandler. This works fine serving static files, including the cgi scripts (cgi-bin is, for now, a link to ".") but for cgi scripts Firefox shows
"SSL received a record that exceeded the maximum permissible length"
Error code: ssl_error_rx_record_too_long.
Web searches indicate this is likely a server issue, not browser, which seems logical to me. Also, I've tried all the suggested browser settings. I've tried a variety of server settings also, but no joy.
Platform is RHEL 2.6.18
Python is 2.4.3 - please don't suggest using "import ssl" as it isn't available
Self signed certificate, accepted in browser (static files work)
code:
import socket, sys
import SocketServer
import BaseHTTPServer
import CGIHTTPServer
from OpenSSL import SSL
class SecureHTTPServer(BaseHTTPServer.HTTPServer):
def __init__(self, server_address, HandlerClass):
self.allow_reuse_address = True
SockeServer.BaseServer.__init__(self, server_address, HandlerClass)
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.use_privatekey_file ("path/key.pem")
ctx.use_certificate_file("path/cert.pem")
self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type))
self.server_bind()
self.server_activate()
class SecureHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
def setup(self):
self.cgi_directories= ['cgi-bin']
self.connection = self.request
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
def test(HandlerClass = SecureHTTPRequestHandler, ServerClass = SecureHTTPServer):
httpd = ServerClass(('0.0.0.0',4443), HandlerClass)
sa = httpd.socket.getsockname()
print "Serving HTTPS on", sa[0], "port", sa[1], "..."
httpd.serve_forever()
if __name__ == '__main__':
test()
How may I get this working for a quick and easy web interface in Python 2.4?

Wound up using Apache httpd to get SSL working with the existing Python CGI scripts. I'd still like to know how to get it working with the CGIHTTPServer class, though

Related

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

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.

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

Python SimpleHTTPServer

Is there a way to make Python SimpleHTTPServer supports mod_rewrite?
I'm trying things with Ember.js with leveraging History API as the location API, and to make it work, I have to :
1) add some vhosts config in WAMP (not simple), or
2) run python -m simpleHTTPServer (very simple)
So when I opened it in the browser, localhost:3000 and clicked around the navigation (about and users for example), it worked well. The URLs are changed by Ember.js to localhost:3000/about and localhost:3000/users respectively.
But when I tried to open localhost:3000/about directly in new tab, the python web server simply returns 404.
I had my .htaccess redirecting everything to index.html, but I suspect python simple web server doesn't really read the htaccess file (am I right on this?)
I've tried downloading PHP 5.4.12 and run the built in web server, the url and htaccess mod_rewrite works well. But I'm still reluctant to upgrade from stable 5.3 to (probably still unstable enough) 5.4.12, so if there's a way to support mod_rewrite in python simple web server, that would be preferrable.
Thanks for the answer.
By modifying pd40's answer, I came up with this which doesn't redirect, it does your traditional "send index.html instead of 404". Not at all optimized, but it works for testing and development which is all I needed.
import SimpleHTTPServer, SocketServer
import urlparse, os
PORT = 3456
class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
# Parse query data to find out what was requested
parsedParams = urlparse.urlparse(self.path)
# See if the file requested exists
if os.access('.' + os.sep + parsedParams.path, os.R_OK):
# File exists, serve it up
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self);
else:
# send index.hmtl
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
with open('index.html', 'r') as fin:
self.copyfile(fin, self.wfile)
Handler = MyHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)
print "serving at port", PORT
httpd.serve_forever()
SimpleHTTPServer does not support apache modules and does not respect .htaccess, because it isn't apache. it won't work with php either.
If you know the cases you need to redirect you can subclass SimpleHTTPRequestHandler and do a redirect. This redirects any missing file requests to /index.html
import SimpleHTTPServer, SocketServer
import urlparse, os
PORT = 3000
class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
# Parse query data to find out what was requested
parsedParams = urlparse.urlparse(self.path)
# See if the file requested exists
if os.access('.' + os.sep + parsedParams.path, os.R_OK):
# File exists, serve it up
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self);
else:
# redirect to index.html
self.send_response(302)
self.send_header('Content-Type', 'text/html')
self.send_header('location', '/index.html')
self.end_headers()
Handler = MyHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)
print "serving at port", PORT
httpd.serve_forever()
No mod_rewrite in Python servers I am afraid, unless you run python scripts behind an Apache server, a resource-costly solution.
Try Cherrypy (http://www.cherrypy.org/), which allows you to manage your page handlers, and very simply makes clean URLs.

Basic SSL Server Using Twisted Not Responding

I am currently trying to pull together a basic SSL server in twisted. I pulled the following example right off their website:
from twisted.internet import ssl, reactor
from twisted.internet.protocol import Factory, Protocol
class Echo(Protocol):
def dataReceived(self, data):
"""As soon as any data is received, write it back."""
print "dataReceived: %s" % data
self.transport.write(data)
if __name__ == '__main__':
factory = Factory()
factory.protocol = Echo
print "running reactor"
reactor.listenSSL(8080, factory,
ssl.DefaultOpenSSLContextFactory(
"./test/privatekey.pem", "./test/cacert.pem"))
reactor.run()
I then tried to hit this server using firefox by setting the url to https://localhost:8080 yet I receive no response. I do, however, see the data arriving at the server. Any ideas why I'm not getting a response?
You're not sending an http header back to the browser, and you're not closing the connection
You've implemented an SSL echo server here, not an HTTPS server. Use the openssl s_client command to test it interactively, not firefox (or any other HTTP client, for that matter).

Python3 Http Web Server: virtual hosts

I am writing an rather simple http web server in python3. The web server needs to be simple - only basic reading from config files, etc. I am using only standard libraries and for now it works rather ok.
There is only one requirement for this project, which I can't implement on my own - virtual hosts. I need to have at least two virtual hosts, defined in config files. The problem is, that I can't find a way how can I implement them in python. Does anyone have any guides, articles, maybe some simple implementation how can this be done?
I would be grateful for any help.
Virtual hosts work by obeying the Host: header in the HTTP request.
Just read the headers of the request, and take action based on the value of the Host: header
For a simple HTTP web server, you can start with the WSGI reference implementation:
wsgiref is a reference implementation of the WSGI specification that can be used to add WSGI support to a web server or framework. It provides utilities for manipulating WSGI environment variables and response headers, base classes for implementing WSGI servers, a demo HTTP server that serves WSGI applications,...
Modifying the example server to check the HTTP_HOST header, here is a simple app that responds, depending on the virtual host, with a different text. (Extending the example to use a configuration file is left as an exercise).
import wsgiref
from wsgiref.simple_server import make_server
def my_app(environ,start_response):
from io import StringIO
stdout = StringIO()
host = environ["HTTP_HOST"].split(":")[0]
if host == "127.0.0.1":
print("This is virtual host 1", file=stdout)
elif host == "localhost":
print("This is virtual host 2", file=stdout)
else:
print("Unknown virtual host", file=stdout)
print("Hello world!", file=stdout)
print(file=stdout)
start_response(b"200 OK", [(b'Content-Type',b'text/plain; charset=utf-8')])
return [stdout.getvalue().encode("utf-8")]
def test1():
httpd = make_server('', 8000, my_app)
print("Serving HTTP on port 8000...")
# Respond to requests until process is killed
httpd.serve_forever()

Categories