Net::P...torial(3pm)

๐Ÿ“ฆ
version 0.13 ยท 3 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

๐Ÿฆ‹ Net::Proxy::Tutorial ๐Ÿฆ‹

okay so bestie, welcome to Net::Proxy::Tutorial(3pm) โ€” it lives in the User Contributed Perl Documentation corner of your system, and its whole vibe is network proxies for fun and profit. โœจ


๐Ÿ’– NAME ๐Ÿ’–

Net::Proxy::Tutorial โ€” network proxies for fun and profit. no cap, that's literally the tagline. ๐Ÿซถ


๐ŸŒธ SYNOPSIS ๐ŸŒธ

This document walks you through, in painful but loving detail, how to actually use Net::Proxy in several real-life situations. not vibes, not theory โ€” real setups. ๐Ÿ’…


๐ŸŽ€ DEFINITIONS ๐ŸŽ€

what even is a proxy? ๐Ÿ‘€

You need a proxy every single time you gotta cross network boundaries to reach a service that isn't directly accessible. it's giving "there's a wall and you need a friend with a key".

The typical example is the corporate web proxy in a company. The corporate firewall is a boundary โ€” usually very tightly closed โ€” between the corporate network and the outside world (the wild wild Internet ๐ŸŒˆ).

To let the employees access all the nice web sites outside, the company sets up a web proxy, which is authorised to cross the boundary (the firewall) on your behalf. Your web browser asks the proxy for whatever it needs, and the proxy goes and fetches the requested stuff on the web. it's the group-chat friend who screenshots things for you. ๐Ÿ’–

Since the proxy sees the client requests, it can check if they fit the corporate browsing policy and decide if it will fetch the document for the requestor. It can also request authentication, and log the username with the request. so it's also the friend who keeps receipts. ๐Ÿ˜ญ

Transparent proxies mimic the actual service you asked for, and reply as if they were the actual service provider. Except that the client doesn't notice there is a proxy in between. sneaky! ๐Ÿ™ˆ Most transparent web proxies grab outgoing traffic on port 80. Some ISP do this to cache responses and spare their bandwidth.

why do I even need a proxy?? ๐Ÿ’€

Sometimes, the traffic you want to send or receive doesn't quite fit the model that the network designers had in mind. the designers said "this is how it works" and your traffic said "no โค๏ธ".

For example, if you need to modify network traffic, almost transparently, at a high level, you probably need Net::Proxy. periodt.


โœจ DESCRIPTION โœจ

In this section, we will see actual examples of use of Net::Proxy. real ones.

a basic Net::Proxy script ๐Ÿงš

Most Net::Proxy based scripts look like the following:

  • The usual boilerplate:
#!perl
use strict;
use warnings;
use Net::Proxy;
  • One or more proxies are created by calling new() with the appropriate parameters:
my $proxy = Net::Proxy->new( ... );
  • The individual proxies are registered with the Net::Proxy framework:
$proxy->register();
  • Some framework options are defined:
Net::Proxy->set_verbosity(1);
Note: The set_verbosity() method is available only since Net::Proxy version 0.04. ๐Ÿ‘‘
  • The framework is started, sets up the listening sockets, and waits for connections to proxy around:
Net::Proxy->mainloop();

the concepts behind Net::Proxy ๐Ÿฆ‹

Any time a proxy handles a network connection, it actually manages two connections: a connection from the client to the proxy, and a connection from the proxy to the server. During normal processing, each chunk of data received on one connection is copied to the other connection, and vice-versa. it's a two-way mirror situation. โœจ

Net::Proxy introduces the concept of "connectors". Connectors are used to represent the ends of the two connections that the proxy handles to create a single client-server connection.

                             +-------+
                             | proxy |
                             |       |
           "client" --->(xx)[in]  [out]---> "server"
                             +-------+

In the above ASCII diagram, (xx) represents the listening port number, and [in] (left) and [out] (right) the Net::Proxy connectors.

The in connector accepts incoming connections on a listening port. Once a connection with the client is established, the proxy uses the out connector to connect to the destination server.

The simplest connector is named Net::Proxy::Connector::tcp (we'll use tcp for short). When placed on the in side, it simply listen()s for incoming connections and them accept()s them. Then the out connector connect()s to the server.

Each connector accepts different parameters, which we'll see in the following examples. ๐Ÿ‘‘

Since the proxy must handle every item of data going through, it can look at it, and modify it. This is what other connectors do: they can insert or transform data on the fly, which provides us with an incredible amount of power on our network connections, which we will leverage throughout this document. no pressure, but that's genuinely iconic. ๐Ÿ”ฅ


๐Ÿ’œ REAL-LIFE EXAMPLES ๐Ÿ’œ

contacting a SSH server through the corporate web proxy ๐ŸŽ€

(This example requires at least Net::Proxy version 0.02 to work.)

In many companies, the corporate firewall doesn't let you connect outside with SSH. The only allowed access to the outside is via the web proxy. rude, honestly.

Luckily, web proxies are designed to let certain types of TCP connection go through them without modifications: encrypted SSL connections, used in HTTPS. These connections are handled in the following way: the client sends a CONNECT connect to the proxy which (according to a policy based on the hostname, port and the user's credentials) actually connects to the remote host and transfers the data between the client and server, without looking at the encrypted data. The proxy doesn't even check that the traffic is actual SSL traffic. it just trusts you. ๐Ÿ’€

So your SSH client could connect to a local proxy, which would send the CONNECT request to the web proxy, asking for a connection to your home SSH server. Thereafter, the local proxy would behave like a standard TCP proxy and simply pass the data around. chef's kiss. ๐Ÿ’…

Here is a network diagram showing the network configuration in ASCII-art:

                                                    '
                            (internal network)      '     (Internet)
                                                    '
                         +-------+     +-------+    '     +-------+
                         | local |     |  web  |    '     |  ssh  |
          ssh            | proxy |     | proxy |    '     | server|
         client --->(22)[tcp]    |     |       |    '     |       |
                         |[connect]-->(8080)   |----'--->(22)     |
                         +-------+     +-------+    '     +-------+
                                                    '
                                                    '

Here's how to set up the local Net::Proxy instance:

Net::Proxy->new(
    {   in => {
            type => 'tcp',
            host => 'localhost',
            port => 22,
        },
        out => {
            type => 'connect',
            host => 'home.example.com',
            port => 22,

            # proxy details
            proxy_host => 'proxy.mycompany.com',
            proxy_port => 8080,

            # proxy credentials
            proxy_user => 'me',
            proxy_pass => 's3kr3t',
        },
    }
)->register();

Most of the time, corporate web proxies do not allow connections on other ports than 443, the standard HTTPS port. You just need to reconfigure your SSH server so that it also listens on port 443:

# sshd configuration file
Port 22
Port 443

In the exemple above, you need to change the out/port from 22 to 443.

Many SSH clients (like PuTTY) already include configuration options to get through web proxies, so Net::Proxy probably isn't necessary any longer to handle this kind of traffic. growth! ๐ŸŒฑ

running two services on the same TCP port ๐Ÿ˜ญ

(This example requires at least Net::Proxy version 0.03 to work.)

So you managed to get out of your corporate prison^Wnetwork by setting up your SSH server to listen on port 443. The problem is that you also run a HTTPS server; and if you want it to be accessible to anyone, it must run on port 443 (otherwise the corporate proxy won't let you pass through, and noone will find it anyway). two queens, one port, one problem. ๐Ÿซ 

Therefore, the only option is to run both the SSL web server and the SSH server on the same port. How is that even possible? TCP clearly doesn't allow this (or we wouldn't need those long services files in our /etc directories). ๐Ÿ’€

What you need is a proxy that can guess what the client wants, but without contacting the server. If it manages to find out which server the client wants to connect to, it can then contact the expected server and do its usual proxy job. mind-reading, but make it networking. ๐Ÿ”ฎ

Luckily, there is a fundamental difference of behaviour between a http/s client and a SSH client:

  • during a HTTP(S) connection, the client "speaks" first
  • during a SSH connection, the server sends a banner first
                        '
         (Internet)     '        (internal network)
                        '
                        '           +-------+
                        '           |reverse|
                        '           | proxy |
          SSL client ---'--->(      |    [tcp]---> SSL server
                        '    ((443)[dual]   |
          SSH client ---'--->(      |    [tcp]---> SSH server
                        '           +-------+
                        '

Net::Proxy's dual connector is able to detect between two such clients with the help of a timeout.

Net::Proxy->new(
    {   in => {
            type         => 'dual',
            host         => '0.0.0.0',
            port         => 443,
            client_first => {
                type => 'tcp',
                port => 444,     # move the https server to another port
            },
            server_first => {
                type => 'tcp',
                port => 22,      # good old SSH
            },

            # wait during a 2 second timeout
            timeout => 2,
        },
        out => { type => 'dummy' },
    }
)->register();

hiding SSH connections going through the corporate proxy from IDS ๐Ÿ™ˆ

(This example requires at least Net::Proxy version 0.06 to work.)

The first technique we presented (using a CONNECT request to get out of the corporate network) is so well-known that many Intrusion Detection Systems (IDS) check the first packets of a connection to try and find hidden SSH connections crossing the corporate boundaries outwards. they're nosy like that. ๐Ÿ‘€

The server banner looks like this:

SSH-2.0-OpenSSH_3.9p1

while the client banner may look like this:

SSH-2.0-OpenSSH_4.2p1 Debian-5

You want to deceive Intrusion Detection Systems (IDS) by modifying the cleartext part of your SSH connection. Since the detection code simply looks for the SSH- string, an "encryption" scheme as simple as ROT-13 is enough. iconic, lowkey, and completely unbothered. ๐ŸŽ€

okay so picture this, bestie โ€” here's the whole scheme laid out like a lil map ๐Ÿ—บ๏ธโœจ

                                                     '
                            (internal network)       '          (Internet)
                                                     '
                         +-------+      +-------+    '          +-------+
                         | local |      |  web  |    '          |reverse|
          ssh            | proxy |      | proxy |    '          | proxy |
         client --->(22)[tcp]    |      |       |    '          |       |
                         |[connect]===>(8080)   |===='===>(443)[tcp][tcp]--->  ssh
                         +-------+      +-------+    '          +-------+     server
                                                     '
           Traffic                \________ ________/'
           ---> ssh                        v         '
           ===> ssh + rot13         Traffic scanned  '
                                      by the IDS     '
                                                     '

so ---> is just plain ol' ssh doing its thing, and ===> is ssh wearing a rot13 disguise ๐Ÿฅธ and that lil v pointing at the IDS? that's the traffic getting scanned by the intrusion detection system, while your actual bytes just vibe on through ๐Ÿฆ‹


okay so the hook connector option is literally the star of the show here ๐Ÿ’… it accepts a callback that gets called for each chunk of data received, before sending it out. and it has to have exactly this signature:

# Net::Proxy versions 0.06 and 0.07
sub {
    my ( $dataref, $connector ) = @_;
    ...
}
# As from Net::Proxy version 0.08
sub {
    my ( $dataref, $socket, $connector ) = @_;
    ...
}
heads up bestie โ€” the signature changed in 0.08. if you're on the old versions you get two args, from 0.08 onward you get three ($dataref, $socket, $connector). don't mix 'em up ๐Ÿ˜ญ

the ROT-13 routine is super straightforward โ€” and yes, it must be defined in both scripts (both sides gotta speak the same secret language ๐Ÿซถ):

my $rot13 = sub { ${ $_[0] } =~ y/A-Za-z/N-ZA-Mn-za-m/ };

Client-side proxy ๐ŸŒธ:

Net::Proxy->new(
    {   in => {
            type => 'tcp',
            host => '0.0.0.0',
            port => 22,
            hook => $rot13
        },
        out => {
            type => 'connect',
            host => 'home.example.com',
            port => 22,
            hook => $rot13,

            # proxy configuration
            proxy_host => 'proxy.mycompany.com',
            proxy_port => 8080,

            # proxy credentials
            proxy_user => 'me',
            proxy_pass => 's3kr3t',
        },
    }
)->register();

Server-side proxy ๐Ÿ’œ:

Net::Proxy->new(
    {   in => {
            type => 'tcp',
            host => '0.0.0.0',
            port => 443,
            hook => $rot13
        },
        out => {
            type => 'tcp',
            port => 22,
            hook => $rot13
        }
    }
)->register();

see how the hooks match up on both ends? that's the whole trick โ€” scramble on the way in, unscramble on the way out, and the IDS in the middle just sees noise ๐ŸŒˆ periodt.


๐ŸŒธ Hiding a SSH connection under SSL through a corporate proxy ๐ŸŒธ

okay so (this example requires at least Net::Proxy version 0.08 to work โ€” no cap, older versions just can't do it) ๐Ÿ’€

another option for hiding what you're doing in your connection through the corporate proxy is to actually use SSL to connect to your SSH server, ร  la stunnel. and honestly? that's what the proxy expects anyway, so it's giving totally normal traffic energy ๐Ÿ‘‘

                                                      '
                          (internal network)          '         (Internet)
                                                      '
                       +-----------+      +-------+   '         +-------+
                       |   local   |      |  web  |   '         |reverse|
         ssh           |   proxy   |      | proxy |   '         | proxy |
        client -->(22)[tcp]        |      |       |   '         |       |
                       |[connect_ssl]===>(8080)   |==='==>(443)[ssl][tcp]--->  ssh
                       +-----------+      +-------+   '         +-------+     server
                                                      '
          Traffic                    \_______ _______/'
          ---> ssh                           v        '
          ===> ssh over SSL           Traffic scanned '
                                        by the IDS    '
                                                      '

so here ---> is plain ssh, and ===> is ssh wrapped in SSL like a lil gift ๐ŸŽ the IDS sees 443 with [ssl] on it and is like "oh cool, https, nothing to see here" ๐Ÿ™ˆ and the reverse proxy unwraps it and hands it to the ssh server on [tcp].

Client-side proxy ๐ŸŽ€:

Net::Proxy->new(
    {   in => {
            type => 'tcp',
            host => '0.0.0.0',
            port => 22,
        },
        out => {
            type => 'connect_ssl',
            host => 'home.example.com',
            port => 443,

            # proxy configuration
            proxy_host => 'proxy.mycompany.com',
            proxy_port => 8080,

            # proxy credentials
            proxy_user => 'me',
            proxy_pass => 's3kr3t',
        },
    }
)->register();

Server-side proxy โญ:

Net::Proxy->new(
    {   in => {
            type => 'ssl',
            host => '0.0.0.0',
            port => 443,
        },
        out => {
            type => 'tcp',
            port => 22,
        }
    }
)->register();

notice there's no hook anywhere in this one โ€” the SSL layer is doing all the hiding for you, so you don't need the rot13 dance. iconic behavior honestly ๐Ÿ’…


๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

Philippe "BooK" Bruhat, "<book@cpan.org>".

like, actual legend status. bow down ๐Ÿซถ


Copyright 2006-2014 Philippe 'BooK' Bruhat, All Rights Reserved.


๐Ÿ’– LICENSE ๐Ÿ’–

This tutorial is distributed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License.

so yes bestie, read it, learn it, love it โ€” just don't sell it and don't remix it, k? ๐Ÿ˜Œโœจ


and that's a wrap on this whole tutorial, fr fr ๐ŸŽ€ it came from perl v5.38.2 and got stamped with the date 2024-05-15, and the source page is Net::P...torial(3pm) โ€” go forth and tunnel responsibly, you absolute main character ๐Ÿ’œ๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from