Nick's Website

How to setup a DIY cheap and easy proxy

Sometimes I need to route some web traffic to another location (in my case usually Japan). Many people recommend a VPN for this task, but there is a cheaper and easier way.

Instructions

You can use a VPS service to rent a virtual machine, and they usually have a lot of locations if you want to rout your traffic through a specific country. I use Linode for this because I saw and ad for them one time and have had no problems with their service.

Many VPS' charge by the hour so if you just need a quick proxy you can delete the VPS after you are done to reduce costs. When I did this recently it added $0.01 to my monthly bill.

After you have rented it I usually configure ssh settings a bit as to not allow password login, but this is optional if you are just going to delete it afterwords.

You can start the proxy with ssh -D 50001 USER@IP. Alternatively you can use another piece of proxy software to get something more permanent.

If you are using Firefox you can the go to proxy settings and set up the SOCKS v5 proxy. It should be under "Network Settings" then select "Manual proxy configuration". Under SOCKS host enter "localhost" and 50001.

Screenshot of Firefox with the proxy settings

Now you should be good. After you are done you can delete the VPS to save money.

It seems VPN services cost about 5-10 dollars per month. This costs about one cent per use. So if you use it less than 500 times per month you should be coming out ahead.

Summary

To summarize:

  1. Rent a VPS
  2. Starts a SOCKS5 proxy
  3. Tell your software to use said proxy
  4. Use the internet
  5. Delete the VPS

Automation

This still seems kind of annoying to do manually. I mean I have to go to the Linode website twice! That is way too many buttons to press for me. So I wrote a script to automate it. This works with the Linode api, and you need to get a login and a token from them for it to work. I will vary what software uses the proxy depending on the situation, so I do not set it here, but it should be possible. The arch wiki has a nice sample of that here.

I wrote this script mostly by playing around with the tool for making requests.

#!/usr/bin/python3
import requests
import json
import subprocess
import random
import string
import time

url = "https://api.linode.com/v4/linode/instances"
password = ''.join(random.choices(string.ascii_letters + '1234567890!@#$%^&*()-_=+<,>.?/~``;:|', k=128))
token = 'You need to generate an api token and put it here'


linode_key = 'This is in a .pub file for a ssh key you generate. You could also delete the authorized_keys part and just use a password.'

payload = {
    "booted": True,
    "interfaces": [
        {
            "active": True,
            "primary": True,
            "purpose": "public"
        }
    ],
    "swap_size": 512,
    "authorized_keys": [linode_key],
    "image": "linode/debian11",
    "root_pass": password,
    "backups_enabled": False,
    "region": "jp-osa", # I always use japan but you can do whatever region you want
    "type": "g6-nanode-1"
}
headers = {
    "accept": "application/json",
    "content-type": "application/json",
    "authorization": f"Bearer {token}"
}

response = requests.post(url, json=payload, headers=headers)
content = json.loads(response.content)
ip = content['ipv4'][0]
linode_id = content['id']
connection_string = f'root@{ip}'
status = 'booting'
status_url = f"https://api.linode.com/v4/linode/instances/{linode_id}"
headers = {
    "accept": "application/json",
    "authorization": f"Bearer {token}"
}
while status != 'running':
    status_resp = requests.get(url, headers=headers)
    status_content = json.loads(status_resp.content)
    status = status_content['data'][0]['status']
    print('waiting a bit for everything to boot up')
    time.sleep(5)

# In principle there should be a way to do this in python, but this was easy
subprocess.run(['ssh', '-D', '50001', connection_string, '-i', '~/.ssh/linode'])


url = f'https://api.linode.com/v4/linode/instances/{linode_id}'

delete_response = requests.delete(url, headers=headers)
if delete_response.status_code != 200:
    print('It seems like deleting it failed. You should go into the web interface to delete it or they might charge you money!')

Conclusion

Obviously different services do different things, but if you just need to rout some traffic to a different location this is pretty effective.