Python HTTPServer that only responds to me?

58 Views Asked by At

I have a python http server implemented which only responds to get request to serve data plots to me remotely showing me what my photovoltaic system is doing at home. I have noticed some failed requests from IP addresses that are not any of my own and would like to lock this down a little to reject connections from computers that are not me specifically.

Example: If there were a way to only respond to GET requests that originate from the MAC address of my iPhone and provide no response otherwise so the service is undetectable by others, that would be ideal.

Question 2: If I exit this script using ctrl-c does this code leave something running or does the daemon take care of stopping the server when I exit the script?

Here is the code I am currently using. Any thoughts, comments, suggestions you might have for locking this thing down would be great!

from http.server import HTTPServer, BaseHTTPRequestHandler, ThreadingHTTPServer
import threading

def load_binary(filename):
    with open(filename, 'rb') as file_handle:
        return file_handle.read()
        
class MyRequestHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        if self.path == '/':
            os.system('python3 /home/pi/Plot_Generator_0_2.py')
            mimetype='image/png'
            self.send_response(200, 'OK')
            self.send_header('Content-type',mimetype)
            self.end_headers()
            self.wfile.write(load_binary('plot_image.png'))

if currentOS != "Windows":
    server = ThreadingHTTPServer(('192.168.0.122', 5555), MyRequestHandler)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.daemon = True
    server_thread.start()
2

There are 2 best solutions below

4
tr0yspradling On BEST ANSWER

Question 1

When dealing with application-level protocols like HTTP, sockets generally do not expose lower-level network details like the MAC address. Access to TCP/IP headers is limited, and thus MAC filtering is not directly possible at this layer.

To capture such details, one would need to use specialized software like a packet capture library (e.g., WinPCap, libpcap) to operate at a lower level of the networking stack. This can introduce significant complexity and potential vulnerabilities if not carefully implemented. It also complicates the software stack, making it more challenging to manage and troubleshoot issues.

Options

Bear in mind these examples are not tested, nor production-ready & should not be used as such. Please use an existing framework if you plan on taking this application to production.

  1. IP allow lists (clients will need a static IP)
class MyRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        client_ip, _ = self.client_address
        if client_ip not in ['192.168.0.122', '192.168.0.123', '127.0.0.1']:
            self.send_error(403)
            return
        ...
  1. HTTP Basic Auth
class MyRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.headers.get('Authorization') == 'Basic ' + str(b64encode(('username:password').encode('utf-8')).decode('utf-8')):
            # Your existing code for handling GET requests
        else:
            self.send_response(401)
            self.send_header('WWW-Authenticate', 'Basic realm=\"Authorization required\"')
            self.end_headers()
            self.wfile.write(b'Authorization required.')

If you choose this route, you should enable HTTPS. Otherwise this information will be in plain text & not secure.


Question 2

Exiting with ctrl-c should be sufficient to terminate the server and all of its threads, without leaving anything running.

0
mighty_mike On

You cannot get the MAC address from a HTTP request because this information isn't passed up to the application layer where HTTP operates.

However, if you're looking to identify or differentiate devices, consider using cookies, sessions, or tokens to achieve this.

And for the second question: Yes, exiting the script using ctrl-c will also stop the server, and there won't be anything left running from this script after you exit.