0

NOTE: I'm not sure if this is the right place to ask this question or if I'm asking the right question. Please bear with me if I'm wrong and let me know how I can correct my question or move it to the correct StackExchange site.

I have a web server for which I only want some clients to connect to (Ubuntu host 1 & 2) and I want these clients to connect to the web server over a SSH tunnel and all other clients should be denied access to the web server.

The setup should look like this where if the Ubuntu hosts (or any other hosts) try to connect to the web server directly, they are denied access (or better yet the server doesn't respond at all) but if they connect to the server via a SSH tunnel, only then they have access to the web server

                    ┌──────────────────────────────────────────────────────────────────────┐
                    │                                                 Access Denied        │
┌───────────────────┴─┐                                                                    │
│                     │                                 ┌───────────────────────────────┐  │
│                     │           SSH Tunnel            │           Web Server          │  │
│     Ubuntu Host 1   ◄─────────────────────────────────┤                               │  │
│                     ┌────────────────┼────────────────┼───────────────────┐           │  │
│                     │                │                │ Access Granted    │           │  │
└─────────────────────┘                │                │                   │           │  │
                                       │                │                   │           │  │
┌─────────────────────┐                │                │                 ┌─▼───────────┤  │
│                     │                │                │ Access Granted  │   Python    │  │
│                     ├────────────────┼────────────────┼─────────────────► http.server │◄─┘
│   Ubuntu Host 2     ◄────────────────┘                │                 │ port=8080   │
│                     │                                 │                 │ host=0.0.0.0│◄─┐
│                     │                                 └─────────────────┴─────────────┘  │
└───────────────────┬─┘                                                                    │
                    │                                                   Access Denied      │
                    └──────────────────────────────────────────────────────────────────────┘

I can achieve this by doing the following:

On web server (web-server.example.com):

  1. Run python3 server.py
  2. SSH to Ubuntu machines with
ssh ubuntu-host-1.example.com -R 8080:web-server.example.com:8080
ssh ubuntu-host-2.example.com -R 8080:web-server.example.com:8080

Code in server.py (from pythonbasics.org):

# Python 3 server example
from http.server import BaseHTTPRequestHandler, HTTPServer
import time, logging, ipaddress

hostName = "0.0.0.0"
serverPort = 8080
allowed_net = ipaddress.ip_network('172.16.0.0/12')
priv_net = list(allowed_net.hosts())

class MyServer(BaseHTTPRequestHandler):
    def do_GET(self):
        client = ipaddress.ip_address(self.client_address[0])
        if client in priv_net:
            self.send_response(200)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write(bytes("<html><head><title>https://pythonbasics.org</title></head>", "utf-8"))
            self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
            self.wfile.write(bytes("<p>IP: %s</p>" % self.client_address[0], "utf-8"))
            self.wfile.write(bytes("<body>", "utf-8"))
            self.wfile.write(bytes("<p>This is an example web server.</p>", "utf-8"))
            self.wfile.write(bytes("</body></html>", "utf-8"))
        else:
            self.send_response(404)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write(bytes("<html><head><title>https://pythonbasics.org</title></head>", "utf-8"))
            self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
            self.wfile.write(bytes("<p>IP: %s</p>" % self.client_address[0], "utf-8"))
            self.wfile.write(bytes("<body>", "utf-8"))
            self.wfile.write(bytes("<p>Access Denied</p>", "utf-8"))
            self.wfile.write(bytes("</body></html>", "utf-8"))



if __name__ == "__main__":        
    webServer = HTTPServer((hostName, serverPort), MyServer)
    print("Server started http://%s:%s" % (hostName, serverPort))

    try:
        webServer.serve_forever()
    except KeyboardInterrupt:
        pass

    webServer.server_close()
    print("Server stopped.")

As you can see, in my web server code, I have to handle the case for when a client connects from outside the SSH tunnel (i.e. not a part of 172.16.0.0/12 network)

Is there a way that I can achieve this without needing the server to listen on all interfaces (0.0.0.0) but still only serve clients connected via the SSH tunnel?

NOTE: I can't open the SSH tunnel from the Ubuntu hosts to the web server. It has to be the other way around

1 Answer 1

1

Based on your diagram I understand that the SSH tunnel and Web server are running on the same machine. In this case, all valid connections to the Python HTTP server would arrive from "localhost".

If that's the case, not binding on all interfaces, only localhost, would be the solution you're looking for, as anything coming from any IP other than 127.0.0.1 wouldn't reach the web server, as it won't be listening to them there.

4
  • I tried that but it didn't seem to work and the clients couldn't connect so I opened it up with the server listening on all interfaces and noticed that when the client over the SSH tunnel connected to the server, the IP address was in the 172.x.x.x range
    – Vinayak
    May 12, 2021 at 8:24
  • 1
    I see. It seems that your SSH tunnel for web-server.example.com resolves as this 172 IP. If you change the tunnel command to: ssh ubuntu-host-1.example.com -R 8080:localhost:8080 That would force them to establish a connection using the localhost interface. Alternatively, all 172.x.y.z IPs you're seeing SHOULD belong to web-server.example.com, is that right? Maybe filtering by that just single IP would suffice?
    – Tango
    May 12, 2021 at 8:35
  • Thank you so much! The 172.x IP was indeed the IP of the server and while I could just filter traffic by that single IP, the ssh command you mentioned is what I really needed. Instead of ssh ubuntu-host-1.example.com -R 8080:web-server.example.com:8080 I should have been using ssh ubuntu-host-1.example.com -R 8080:localhost:8080 which allows me to have the server listen only on localhost and not 0.0.0.0
    – Vinayak
    May 12, 2021 at 10:42
  • Could you add the SSH command to your answer for posterity? Or I could edit it and add it myself if you don't mind.
    – Vinayak
    May 12, 2021 at 10:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.