Strumenti Utente

Strumenti Sito


firewall_blocking_outgoing_connections

The problem

I often connect to Internet from a connection which blocks, by default, outgoing connections except for some given ports.

For instance, port 80 (http) is open, as are 443 (https), 995 (pop3s) and a few others… but port 22 (ssh) is closed. So if I want to be able to connect via ssh to a server of mine, I must configure the server to listen (also) on a different port… non blocked. Configuring the server to listen on multiple ports is trivial, this page is devoted to the problem of understanding which port can be used.

My solution

My rudimentary solution is composed of 4 simple Python scripts:

  1. a script for opening ports, which must be ran on the server
  2. a script to receive “orders” via http about which ports to open
  3. a script acting as a bridge between 1. and 2.
  4. a script for probing ports, which must be ran from the client

Notice that you don't need scripts 2. and 3. if you have a way to connect to the remote host 1).

Click here to download all 4 scripts in this page at once - they are released as GPL v.3+.

The server scripts

The ports opener

The script just runs a dummy service on each port from N (provided as argument) to N+257 (I don't check more than 257 ports at the same time, to not incur in the restrictions imposed on the maximum number of opened files).

As suggested in the comment, you should replace “socket.gethostname()” with the hostname the server is accessible to, i.e.

s.bind( ("example.com", port) )

because socket.gethostname() probably won't work.

#! /usr/bin/python
# opener.py

import socket, time, sys

try:
    start = int( sys.argv[1] )
except (IndexError, ValueError):
    print "usage: %s first_port" % sys.argv[0]
    sys.exit(1)

sockets = []

for port in range(start, start + 257):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        # You should probably edit the following line!
        s.bind( (socket.gethostname(), port) )
        s.listen(1)
    except Exception as exc:
        print port, exc
    sockets.append( s )

time.sleep(3600)

The http control

Put this in some place where Python files will be executed by the webserver (i.e. /usr/lib/cgi-bin/ , but depends on the configuration). Notice Apache doesn't execute Python files out of the box: mod_python must be installed and maybe configured.

#! /usr/bin/python
# http_control.py

import cgi
import cgitb
cgitb.enable()

print "Content-type: text/html\n"


form_c = cgi.FormContentDict()

if 'p' in form_c:
    f = open( 'port', 'w')
    f.write( form_c['p'][0] )
    print form_c
    f.close()

The bridge

The above script creates a file, in the same folder, called “port”. This one looks at that script and runs “ports_opener.py” accordingly. So you must replace '/path/to/port/file' with the full path of the “port” file, and save it in the same folder as “opener.py”.

#! /usr/bin/python
# bridge.py

import time, subprocess

port = None
proc = None

while True:
    newf = open('/path/to/port/file')
    try:
        newport = int( newf.read() )
    except ValueError:
        newport = None
    newf.close()
    if newport != port:
        if proc:
            proc.terminate()
        if newport != None:
             print "starting", ["./opener.py", str( newport )]
             proc = subprocess.Popen(["./opener.py", str( newport )])
        else:
             print "stopped"
    port = newport

    time.sleep( 5 )

The client script

This is a very simple port scanner: it tries to connect to a given range of ports on the given host (again, from N, the argument, to N+257). You must replace “put.here.the.hostname” with the true address of your server (the same you presumably replaced “socket.gethostname()” with above).

#! /usr/bin/python
# prober.py

import socket, time, sys

try:
    start = int( sys.argv[1] )
except (IndexError, ValueError):
    print "usage: %s first_port" % sys.argv[0]
    sys.exit(1)

if len(sys.argv) > 2:
    host = sys.argv[2]
else:
    # Edit the following line!
    host = "put.here.the.hostname"

sockets = []

for port in range(start, start + 257):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    res = s.connect_ex( (host, port) )
    if res == 0:
        print "PORT ", port, " OPEN"
    else:
        print port, res

So, to recap

What to do

If you have an alternate access to the server, just run on it “opener.py”, like in

./opener.py 100

and on the client run “prober.py” with the same argument

./prober.py 100

to test ports 100 - 357 (and try again with some other argument to test other ports ranges).

If you don't have an alternate access to the server, from another connection, some time before

  • save http_control.py to a place where Python scripts are ran by the webserver (and remember to make it executable)
  • save opener.py and bridge.py to the same folder (and remember to make them executable)
  • save prober.py on the client
  • edit the specified lines in prober.py, bridge.py and opener.py
  • run bridge.py (possibly from inside screen), or just as “nohup bridge.py”

Then, once you are behind the firewalled connection, assuming that http_control.py can be accessed at http://www.example.com/http_control.py, just

./prober.py 100

to test ports 100 - 357 (and try again with some other argument to test other ports ranges).

Understanding output

prober.py will print “OPEN” for every port it finds open. You will know those ports are not filtered. Still, notice that some of them will already be in use by existing services of your server: you must find some free one in order to run ssh at it. Just compare the output of prober.py with what you obtain when running ./prober.py against you host with opener.py not in action (if you use http_control.py, you can disable it by visiting “http://www.example.com/http_control.py?p=None”). Ports which result open in the first case but not in the second are what you're searching for.

1)
OK, and then apparently you shouldn't be reading this at all, but maybe you have a temporaneous other connection, or some friend somewhere else willing to help, or a costly connection…
firewall_blocking_outgoing_connections.txt · Ultima modifica: 2011/03/17 09:11 da toobaz