I thought I'd do a shorter article on catonmat this time. It goes hand in hand with my upcoming article series on "100% technical guide to anonymity" and it's much easier to write larger articles in smaller pieces. Then I can edit them together and produce the final article.

This article will be interesting for those who didn't know it already -- you can turn any Linux computer into a SOCKS5 (and SOCKS4) proxy in just one command:

ssh -N -D 0.0.0.0:1080 localhost

And it doesn't require root privileges. The ssh command starts up dynamic -D port forwarding on port 1080 and talks to the clients via SOCSK5 or SOCKS4 protocols, just like a regular SOCKS5 proxy would! The -N option makes sure ssh stays idle and doesn't execute any commands on localhost.

If you also wish the command to go into background as a daemon, then add -f option:

ssh -f -N -D 0.0.0.0:1080 localhost

To use it, just make your software use SOCKS5 proxy on your Linux computer's IP, port 1080, and you're done, all your requests now get proxied.

Access control can be implemented via iptables. For example, to allow only people from the ip 1.2.3.4 to use the SOCKS5 proxy, add the following iptables rules:

iptables -A INPUT --src 1.2.3.4 -p tcp --dport 1080 -j ACCEPT
iptables -A INPUT -p tcp --dport 1080 -j REJECT

The first rule says, allow anyone from 1.2.3.4 to connect to port 1080, and the other rule says, deny everyone else from connecting to port 1080.

Surely, executing iptables requires root privileges. If you don't have root privileges, and you don't want to leave your proxy open (and you really don't want to do that), you'll have to use some kind of a simple TCP proxy wrapper to do access control.

Here, I wrote one in Perl. It's called tcp-proxy.pl and it uses IO::Socket::INET to abstract sockets, and IO::Select to do connection multiplexing.

#!/usr/bin/perl
#

use warnings;
use strict;

use IO::Socket::INET;
use IO::Select;

my @allowed_ips = ('1.2.3.4', '5.6.7.8', '127.0.0.1', '192.168.1.2');
my $ioset = IO::Select->new;
my %socket_map;

my $debug = 1;

sub new_conn {
    my ($host, $port) = @_;
    return IO::Socket::INET->new(
        PeerAddr => $host,
        PeerPort => $port
    ) || die "Unable to connect to $host:$port: $!";
}

sub new_server {
    my ($host, $port) = @_;
    my $server = IO::Socket::INET->new(
        LocalAddr => $host,
        LocalPort => $port,
        ReuseAddr => 1,
        Listen    => 100
    ) || die "Unable to listen on $host:$port: $!";
}

sub new_connection {
    my $server = shift;
    my $client = $server->accept;
    my $client_ip = client_ip($client);

    unless (client_allowed($client)) {
        print "Connection from $client_ip denied.\n" if $debug;
        $client->close;
        return;
    }
    print "Connection from $client_ip accepted.\n" if $debug;

    my $remote = new_conn('localhost', 55555);
    $ioset->add($client);
    $ioset->add($remote);

    $socket_map{$client} = $remote;
    $socket_map{$remote} = $client;
}

sub close_connection {
    my $client = shift;
    my $client_ip = client_ip($client);
    my $remote = $socket_map{$client};
    
    $ioset->remove($client);
    $ioset->remove($remote);

    delete $socket_map{$client};
    delete $socket_map{$remote};

    $client->close;
    $remote->close;

    print "Connection from $client_ip closed.\n" if $debug;
}

sub client_ip {
    my $client = shift;
    return inet_ntoa($client->sockaddr);
}

sub client_allowed {
    my $client = shift;
    my $client_ip = client_ip($client);
    return grep { $_ eq $client_ip } @allowed_ips;
}

print "Starting a server on 0.0.0.0:1080\n";
my $server = new_server('0.0.0.0', 1080);
$ioset->add($server);

while (1) {
    for my $socket ($ioset->can_read) {
        if ($socket == $server) {
            new_connection($server);
        }
        else {
            next unless exists $socket_map{$socket};
            my $remote = $socket_map{$socket};
            my $buffer;
            my $read = $socket->sysread($buffer, 4096);
            if ($read) {
                $remote->syswrite($buffer);
            }
            else {
                close_connection($socket);
            }
        }
    }
}

To use it, you'll have to make a change to the previous configuration. Instead of running ssh SOCKS5 proxy on 0.0.0.0:1080, you'll need to run it on localhost:55555,

ssh -f -N -D 55555 localhost

After that, run the tcp-proxy.pl,

perl tcp-proxy.pl &

The TCP proxy will start listening on 0.0.0.0:1080 and will redirect only the allowed IPs in @allowed_ips list to localhost:55555.

Another possibility is to use another computer instead of your own as exit node. What I mean is you can do the following:

ssh -f -N -D 1080 other_computer.com

This will set up a SOCKS5 proxy on localhost:1080 but when you use it, ssh will automatically tunnel your requests (encrypted) via other_computer.com. This way you can hide what you're doing on the Internet from anyone who might be sniffing your link. They will see that you're doing something but the traffic will be encrypted so they won't be able to tell what you're doing.

That's it. You're now the proxy king!

Download tcp-proxy.pl

Download link: tcp proxy (tcp-proxy.pl)
Download URL: http://www.catonmat.net/download/tcp-proxy.pl
Downloaded: 5167 times

I also pushed the tcp-proxy.pl to GitHub: tcp-proxy.pl on GitHub. This project is also pretty nifty to generalize and make a program that redirects between any number of hosts:ports, not just two.

PS. I will probably also write "A definitive guide to ssh port forwarding" some time in the future because it's an interesting but little understood topic.

Comments

May 06, 2010, 23:59

Thanks for writing this! It broke it down very nicely, and I've been meaning to learn more about this kind of stuff for a while now. I am definitely interested in reading "A definitive guide to ssh port forwarding" when that comes around.

May 07, 2010, 17:51

Thanks for the comment. If you follow my blog you'll know when I start writing it. It will probably be part of 100% technical guide to anonymity as it has much to do with anonymity. I will also split it up in several parts because it's easier to write articles incrementally.

Nicely written, and easy to understand. Well done!
I have been using ssh -f -n -N -D for quite sometimes, and it really helps.

May 07, 2010, 17:51

The -n is an option I hadn't noticed. Thanks for mentioning it.

Anon Permalink
May 07, 2010, 13:38

SOCKS, besides being an abysmally awful protocol, is not supported by the majority of Internet software, so the advice you give is of limited usefulness. When I am confronted with a thoroughly hostile firewall, I depend on Net::Proxy. Accept no lesser substitutes.

May 07, 2010, 18:13

On Linux you can LD_PRELOAD trick to make any software use a socks proxy. Socksify does that, also tsocks does that.

Net::Proxy indeed is a good one. Much better than my primitive tcp-proxy. But I wrote it out of curiosity, hadn't used IO::Socket and IO::Select in ages and wanted to refresh my knoweldge.

May 11, 2010, 21:07

If you want to make some SOCKS proxy available to all your applications, possibly better way than using tsocks is implementing iptables-based solution with the help of redsocks (git repository). See my tip Making SOCKS proxy transparent for example.

Another useful (in this context) ssh option is -g, but it requires turning GatewayPorts on in sshd_config.

Wayne Scott Permalink
May 07, 2010, 14:09

You might want to checkout this command I have been using:

http://github.com/apenwarr/sshuttle

May 07, 2010, 18:04

Wow, that is awesome software. Thanks for linking. I'm going to try it!

May 07, 2010, 14:42

great tips.
if sshd port not 22,use "-p "
ssh -f -p your_sshd_port -n -N -D bindip:1080 localhost

May 07, 2010, 18:04

Good tip. Thanks for writing it!

just curious Permalink
May 07, 2010, 16:30

How much money do you make through ads?

May 07, 2010, 17:10

yes - thats a great feature of the ssh client. I used it quite often when i was too lazy to do vpn configs for some nets behind router - just running sshd with correct restriction on the gateway will save you alot of time (at least saved me, cause i am not admin :). But for some time I am just linking some remote ports on the localhost:
ssh -L 8080:localhost:80 someuser@somehost
Its quite usefull too if you have remote server behind router with access only to ssh port.
About the article - very usefull - thought about "-f" option and how nice its usage is

May 07, 2010, 18:12

Yes, local port forwarding is awesome. I am going to describe it in more detail in my definitive guide to ssh port forwarding.

Paul M. Permalink
May 07, 2010, 17:55

If you're concerned about limiting access to your ssh pseudo-proxy and it's running locally, why not just have ssh bind to localhost, rather than 0.0.0.0?

May 07, 2010, 18:11

For example, you want to give your friend socks5 access but you don't have root on the machine and you don't want everyone to have open access to it. Then as I described, you can start the socks5 proxy on, let's say, port 55555, and provide ip based access control via tcp-proxy.pl which would listen on port 0.0.0.0:1080 and forward requests to localhost:55555.

Paul Permalink
May 08, 2010, 08:19

>Turn any Linux computer into SOCKS5 proxy
Iptables, ok - but Perl and ssh on "any Linux computer"?!

May 08, 2010, 14:24

Yeah, Perl and ssh is almost on any Linux computer.

Sarek Permalink
April 24, 2012, 17:51

Hi. I have a question about a problem I am trying to solve.
Your SOCKS5 post.. I did it. It works with TCP. It doesn't work with UDP. How do I get it to work with UDP? Thank you.
If it's not possible because SSH is

<whatever>

, I understand. I think this is really great and I really like.

Satish Permalink
May 08, 2010, 10:56

I have been trying to proxy my IE instance in Win 7 to go through a similar setup via putty but unfortunately IE -> Options -> Connections -> Settings is disabled. I still haven't figured out how to get this working with IE though :(

May 08, 2010, 14:25

You must be using a limited user account.

Anonymous Permalink
May 08, 2010, 17:18

If you're into anonymity, check http://www.i2p2.de/

beneedy Permalink
May 09, 2010, 21:45

I think I have it partially working, but when I configure my browser with the proxy settings I just get a blank white screen to whatever website I am visiting. The webpage says it is done loading, but just no content... What do you think the problem is?

May 11, 2010, 14:07

Nice , but i use python script not perl :D.

kangu Permalink
May 11, 2010, 18:27

Thanks petter it's well written and easy to follow on with!

mwicat Permalink
May 13, 2010, 08:10

when speaking about source host ACLs, socat utility fits perfectly in this task:
socat tcp-listen:4444,range=192.168.100.1/32,reuseaddr,fork tcp:localhost:3333

...to relay between 0.0.0.0:4444 and localhost:3333 passing only 192.168.100.1, range=192.168.100.0/24 for whole network and finally utilize the power of tcp wrappers with:
socat tcp-listen:4444,allow-table=iplist,reuseaddr,fork tcp:localhost:3333
# iplist contents:
ALL: 192.168.100.1: ALLOW
ALL: ALL: DENY

May 13, 2010, 12:50

A lot of work which could be simplified. U use windows too much ;)

$ sudo -i
# iptables -N proxy_allowed
# iptables -A proxy_allowed -s [ALLOWED_IP] -j RETURN
[...]
# iptables -A proxy_allowed -j DROP
# iptables -A INPUT --syn --dport 1080 -j proxy_allowed
# exit
$
$ ssh -g -D 1080 [REMOTE_IP]

no wrapper is required. if u wish to harden the solution use some knocking (or ping with specified packet length and iptables "recent" module) and/or ip-mac association.

socks does not offer much, to redirect dns or udp traffic it is better to use ppp over ssh or vpn (which I consider preferred).. openvpn is free and there is extremally simple windows gui client.

take care!

ps. python > perl ;P

Alphazo Permalink
July 08, 2010, 07:44

100% agree... "-D 1080" is all what you need. I like to add -p 443 to in order to make the SSH traffic less visible ;)

Then to avoid DNS leaks under Firefox, just make sure to set the two following variables to 1 under about:config.

network.proxy.network.proxy.socks_remote_dns = 1
network.proxy.socks_remote_dns = 1

When I Wireshark the above config I can only see HTTPS traffic even when typing non-working URLs.

ctrl_ Permalink
May 18, 2010, 07:57

Is there a log file where I can see the traffic when doing this?

Ashok Permalink
August 19, 2010, 11:50

Is this http://wiki.przemoc.net/tips/linux#making_socks_proxy_transparent are same as SocksCap?

peter Permalink
August 25, 2010, 12:43

Which ip does 0.0.0.0 represents?

August 25, 2010, 18:57

All IPs.

Hello @pkrumins. I have bookmarked your page and as soon as I am a bit more organized i will peruse your web site and incorporate any information you provide into my spanish language proxy guide.

by the way, would you mind editing your post to include the commands to connect exclusively to localhost as i have my socks proxy set up to only accept connections from localhost. you should be able to see my email. for the rest there are effective antispam features that do not require captcha like akismewhatosever.

love,
andres

October 24, 2010, 07:33

I am very appreciative of this informative piece. Thank you very much I will be sure to use this in my soon to be proxy server at my residence.

ali Permalink
November 17, 2010, 21:55

hi
thank you for this awesome topic ,but i just wanna know that
is there a way that i can use this little command to have
a socks5 server with authentication i mean(username and pass)
rather than IPs
help me please

Dennis Permalink
December 17, 2010, 14:30

Trying to use the basic ssh command on ec2 (ubuntu image) but getting a "Permission denied (publickey)" error.
Any tips on what the problem is?
Actually, i tried it on my own ubuntu machine (linux via virtualbox) and got the same error.
I did not see other comments reporting this type of error so not sure what to do next.

jack Permalink
February 05, 2013, 03:54

you need to initiate the ssh connection on the localhost using the .pem certificate just like you connect to the ec2 server

Davoud Permalink
June 19, 2013, 09:30

ssh -f -N -D 0.0.0.0:1080 -i keyfile.pem localhost

(Make sure its access privilege is 0600)

008 Permalink
December 25, 2010, 22:05

Its useful for me and many ppl, try write script for PAM auth with this mothod, and many many ppl will be HAPPY!

mary xmas!

Ravi Permalink
December 27, 2010, 13:50

I am told to use a socks5 proxy set up on Host ABC, port 1080. How do I get scp to use this socks5 proxy?

nexed Permalink
August 12, 2011, 19:10

Not sure why you would need to run any kind of tcp-proxy script or iptables to protect the connection....

have proxy ssh listen on localhost:1080 and use something like MyEnTunnel to create an ssh tunnel to the server.

once the tunnel is established, configure your software to your localhost / port you configured and done.

Not only does this provide direct credential protection and eliminates the need for fw but also encrypts the connection to the proxy server so your ISP doesn't know what you are doing....

October 07, 2011, 15:07

Hi
worked...thx
but , can i use password protecting for SOCKS proxy?

November 25, 2011, 19:00

When using this script, the CPU usage slowly crawls up until eventually it hits 100%. I don't know if some bot's found my server and there are hundreds using the proxy or what, but I would like some tips on troubleshooting this. For the time being I have a script that kills the proxy and starts it back up if CPU usage is too high.

December 12, 2011, 12:20

I usually don't comment in peoples blog, but I found your article very appreciating so replying to say "Thanks!"

This is exactly what I was looking for and instead of some big tutorial, you made my work done in less then a minute :)

Pawan Permalink
January 08, 2012, 12:16

hi i m newbie in Linux and trying to access internet via Linux (socks & http)
my squid working perfect
and i try your scripts to run socks also on CENTOS i got an error :

ssh -N -D 0.0.0.0:1080 localhost
ssh: localhost: Name or service not known

please help me how i can access internet through socks...

Drift Permalink
February 18, 2012, 13:51

Hello, im having problem with proxing i have 10 ip adresses on my root and when i start proxi server any ip adress i put of that 10 it will be same one,anyone know how to fix that?Thanks

DAV Permalink
February 24, 2012, 10:05

Is there any way to get this to work from outside of the private subnet and add some sort of authentication? I would like to access my server at the office from home via a socks service but don't want it open to the entire world. I have a live IP on the server. My home system runs ADSL with a dynamic IP address so I can't use that as a restriction. I was thinking of a username/password scenario?

DAV Permalink
February 24, 2012, 10:07

In case the above is unclear. I would like to have my home network tunnel through my office server for internet access. :)

Maverick Permalink
May 19, 2012, 04:39

Hi Peteris. Great post. I have been working on something for a couple of days. Maybe you can help. I want to run a SOCKS proxy on a Linux machine which listens on port 1080 and forwards all incoming data to another SOCKS proxy running on 1.2.3.4:22. How can I do that?

May 22, 2012, 22:46

You can't do that easily. You need to do something called proxy chaining. It will look like this:

[Your computer]->[Linux running socks5 on 1080]->[1.2.3.4 running socks5 on 22]

What you'll need to do is open a connection from [Your computer] to [Linux running socks5 on 1080], and then ask this socks5 proxy to open a connection to [1.2.3.4 running socks5 on 22]. And now you'll use this connection for your purposes.

Ana Gallardo Permalink
June 29, 2012, 12:33

Hello Peteris and thank you for your post!!

I have a captive portal.

I need to intercept a tcp connection, do some actions and fordward the connection.

Now I only intercept http connections, and then I redirect that conection:

sub redirect {
my ( $self, $peer, $url ) = @_;
$self->log( 10, "GATEWAY : redirigimos a $url" );
$peer->socket->print(
"HTTP/1.1 302 Moved\r\n",
"Location: $url\r\n\r\n", qq{

<html><body>

You should be redirected now. If not, click here.

});
$peer->socket->close;
}

So, I would like to do a general redirect, not only a http redirect.

I would appreciate it if you would give me some ideas :)

Thank you very much in advance and sorry for my english.

zak Permalink
August 15, 2012, 18:54

Hello,

Hello Mate,

I have a dedicated server with 20 additional ips

I would ideally like to convert these ips to socks 5 to receive and send data via ports 5001 / 5000 udp / tcp

My questions now are -

1- which Lunix OS would i need.
2- How will i configure these ips.
3- Will anyone here be willing to help someone in need.

Regards,
Zak

John Doe Permalink
November 22, 2012, 03:20

I received the following error:

ssh: connect to host localhost port 22: Connection refused

Justin Permalink
November 22, 2012, 14:40

Me too.

jon Kolman Permalink
March 04, 2013, 00:17

Is it possible to setup a socks5 proxy on an actual server? For some reason I can't connect to my proxy while at school. I have verizon fios and the router isn't port forwarding port 2100.

Davoud Permalink
June 19, 2013, 09:41

Hi Peteris
I've setup the socks proxy server successfully on an ec2 instant. However I can not bypass the filtering for blocked website (I live in Iran). It only works for https website as if the connection is not encrypted.

August 29, 2013, 19:37

email me ill give you a proxy to use

August 29, 2013, 19:36

that is one of the best articles ive seen that is some good stuff right there!

Marek Permalink
October 24, 2013, 11:44

Very nice, it would be nice also write mentioned "A definitive guide to ssh port forwarding" or anonymity articles. Thank you

cdtoad Permalink
November 04, 2013, 23:22

So can you add username/password to secure the proxy if you're going to be out & about?

Elans Permalink
November 13, 2013, 17:32

Thanks for the article, very interesting.

John Permalink
December 07, 2013, 19:41

you can also use "ssh -fND 0.0.0.0:1080 localhost"

Salman Hyder Permalink
February 15, 2014, 20:04

Thank you for your tutorial. Everything is working fine, just when i'm using proxy on Windows XP SP3 and browsing for youtube it returns the localhost of my linux computer.

Charles Permalink
February 15, 2014, 20:20

Hey, I'm new to Linux software as well as hosting proxy servers. This is a very nice tutorial from what I can gather.
However, I'd be grateful if someone could help me in more detail. Okay, so I've done as the tutorial says but when I try to use this as a proxy it simply doesn't work. When I test the port it says that port 1080 is open. Could someone give me a hint as to what they problem may be?

March 21, 2014, 19:25

Thank you for clean and simply tutorial. I've hosted my website http://www.loadezi.com on my Ubuntu which i'm also using for proxy, every thing going well except when i'm entering www.youtube.com from any cleint proxy returns my website whereas is shows youtube.com in address bar but redirects me to my website which is hosted on proxy server. help resolve this issue and help me to run youtube.com

asjdlfka Permalink
April 20, 2014, 03:49

Hi I need it to do that but when i put in ssh -N -D 108.193.99.204:1080:localhost i get error "bad dynamic forwarding specification" please help apparently nobody else in the comments has that

Leave a new comment

(why do I need your e-mail?)

(Your twitter name, if you have one. (I'm @pkrumins, btw.))

Type the first letter of your name: (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.

Advertisements