0

I have a python Flask application that serves a website and does a bunch of other things such as querying a database, displaying the results on the website...and also creating an instance of the Tor process. A website that returns the current IP is visited and this is printed 5 times, where a new request for clean circuits is sent via Signal.NEWNYM on each of the loops.

All this happens in Debian. I am logged as admin, and when I run the APP on the command line (python app.py), inside the python virtual environment, everything goes well: each loop prints the IP, waits for new IP, prints the IP...5 times.

However, I am trying to serve this website with UWSGI and NGINX. When the APP gets loaded using UWSGI, everything else seems to be fine, but when I request a new IP using Signal.NEWNYM the requests hangs. If I have harakiri set on the UWSGI config, it will kill the worker after the set time. If not, the requests will hang forever.

Any clue what the issue could be? Permissions? If so, what files/directories? The sockets? I just don't understand how it works perfectly when it runs on the command line the request hangs using UWSGI.

This is some of the code, for context:

def get_tor_session():
    session = requests.session()
    session.proxies['https'] = 'socks5://localhost:9050'
    session.proxies['http'] = 'socks5://localhost:9050'
    return session

def renew_connection():
    print("Renewing connection")
    torControl.signal(Signal.NEWNYM)
    time.sleep(torControl.get_newnym_wait()) 

def startTor(config):
    GEOIPFILE_PATH = "/usr/share/tor/geoip"
    try:    
        urllib.request.urlretrieve('https://raw.githubusercontent.com/torproject/tor/main/src/config/geoip', GEOIPFILE_PATH)
        print ("GeoIP file updated")
    except Exception as e:
        print ('[INFO] Unable to update geoip file. Using local copy.', e)
    try:
        # start tor
        print("Starting tor")
        torProcess = launch_tor_with_config(
            config=config,  # use our custom config
            tor_cmd='tor',  # start tor normally
            completion_percent=100,  # blocks until tor is 100%
            timeout=90,  # wait 90 sec for tor to start
            take_ownership=True  # subprocess will close with parent
        )
        # connect a controller
        print("Connecting controller")
        torControl = Controller.from_port(
            address="127.0.0.1", port=int(config['ControlPort']))
        # auth controller
        torControl.authenticate(password='somepass')
        print("Connected to tor process")
        return torProcess, torControl

    except Exception as e:
        print(e)```


config = {
    'ClientOnly': '1',
    'ControlPort': '9051',
    'DataDirectory': '/tmp/tor',
    'GeoIPFile': '/usr/share/tor/geoip',
    'EntryNodes': '{UK}',
    'ExitNodes': '{UK}',
    'HashedControlPassword':'16:489A6B09FB6D0E9D601D22CFCF438DB9832E7584450E4AF1F0709FF8FB'}


torProcess, torControl = startTor(config)

Then I call a function where I have a loop that prints the IP address and renews it.

for i in range(5):
        session = get_tor_session()
        print(f"trying: {i}")      
        print(session.get('http://icanhazip.com/', timeout=5).text)
        renew_connection()        

This is the NGINX conf

log_format timedlog '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent $upstream_response_time '
                        '"$http_referer" "$http_user_agent"';

server {
    listen 80;
    server_name somewebsite;
    return 301 somewebsite
}
server {
    listen 443 default ssl;

    ssl_certificate /etc/letsencrypt/live/myweb/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myweb/privkey.pem;
    server_name somewebsite;
    access_log /var/log/nginx/myweb-access.log timedlog;
    error_log /var/log/nginx/myweb-error.log;
    location / {
        include uwsgi_params;
        uwsgi_pass unix:/tmp/myweb.sock;  
    }
}

And the UWSGI ini file

[uwsgi]
plugins = python3
pythonpath = /home/admin/myweb/venv/bin
module = wsgi
socket = /tmp/myweb.sock
chmod-socket = 775
chdir = /home/admin/myweb
master = true
module = app:app
uid = www-data
gid = www-data
processes = 1
threads = 1
harakiri = 60
python-autoreload = 0
touch-reload = /tmp/myweb.touchme

1 Answer 1

1

A very frequent problem: you need a full shell context to execute your app. To do so you need to use a wrapper like screen to launch it in a detached mode with -md command line flags. Use apt install screen to install it if you don't have one. And - if you are about to launch it form a different user - make sure that it has shell set to /bin/bash and for a better efficiency do a su your-user-name first to make sure that all the home directory, files e.t.c. are actually created

4
  • Thank you. I'll try that later on. Can you explain a bit more on the full shell context? Is that related to the way uwsgi executes the apps?
    – ehtio
    Mar 5, 2023 at 18:56
  • 1
    Yes, and to the Uwsgi internals also as well. Basically - it's a result of a shortcuts in writing code just in not using basic system functions with a full parameters set, but to the "defaults" and the env/activate part: it needs a full shell context to put the variables into. You can have a workaround by writing a systemd run file for that. As for me - I do prefer Django for it's stability and no such problems at all
    – Alexey Vesnin
    Mar 5, 2023 at 19:27
  • Do you mean Django with UWSGI? I'm used to Flask but really open to change and use something different. Thanks for the explanation!
    – ehtio
    Mar 5, 2023 at 20:57
  • Try this official docs and use sockets instead of tcp uwsgi-docs.readthedocs.io/en/latest/tutorials/…
    – Alexey Vesnin
    Mar 6, 2023 at 12:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .