Difference between revisions of "Bypass Proxy"

From YobiWiki
Jump to navigation Jump to search
 
(11 intermediate revisions by the same user not shown)
Line 51: Line 51:
   
 
==Utilisation de SSH à travers un proxy==
 
==Utilisation de SSH à travers un proxy==
  +
Récupérez [http://git.yobi.be/?p=ssh-tunnel.git;a=snapshot;h=HEAD;sf=tgz la dernière version du script]
Installez [{{#file: ssh-tunnel.pl}} ce script] dans par exemple /usr/local/bin/ssh-tunnel.pl
 
  +
<br>Installez le script dans par exemple /usr/local/bin/ssh-tunnel.pl
<source lang=perl>
 
#! /usr/bin/perl
 
#
 
# ssh-tunnel.pl
 
#
 
# Usage: ssh-tunnel.pl proxy_host proxy_port dest_host dest_port
 
# Run it alone to get a list of options
 
#
 
# This script can be used by ssh as a "ProxyCommand" to
 
# traverse a www-proxy/firewall that supports the http CONNECT
 
# command described in
 
# http://home.netscape.com/newsref/std/tunneling_ssl.html
 
#
 
# For basic authentication support, see
 
# http://www.zvon.org/tmRFC/RFC2617/Output/chapter2.html
 
# "To receive authorization, the client sends the userid and password,
 
# separated by a single colon (":") character, within a base64 encoded
 
# string in the credentials"
 
# Ex: echo -n "user:password" |base64-encode -> dXNlcjpwYXNzd29yZA==
 
#
 
# Example, connect to host named "remote" outside of your firewall:
 
# $ ssh remote -o'ProxyCommand ssh-tunnel.pl www-proxy 80 remote 22'
 
#
 
# Better yet, insert the ProxyCommand definition for host "remote" in
 
# your $HOME/.ssh/config file:
 
#
 
# .
 
# .
 
# Host remote
 
# ProxyCommand /usr/local/bin/ssh-tunnel.pl www-proxy 80 %h %p
 
# .
 
# .
 
#
 
# Written originaly by Urban Kaveus <urban@statt.ericsson.se>
 
# Posted by Urban Kaveus to newsgroup comp.security.ssh on 23 Jan 1997.
 
# Modified by Philippe Teuwen and Thierry Walrant
 
#
 
# twa>> 2006-08-24
 
# Config file format should be the following (located at ~/.ssh/proxy.conf)
 
# [option-section]
 
# debug = 0
 
# dry = 0
 
# timeout = 1
 
# [proxy-section]
 
# 12.34/16 proxy1:8080
 
# 56.78.91/24 proxy2:80 user password
 
#
 
# where the first column will be used as filter on the beginning of the
 
# default gateway IP address, the second is the proxy of the network
 
# and the last ones (optional) are user/pwd for the proxy.
 
# To enable autoselection feature, insert the ProxyCommand in ~/.ssh/config
 
# as following:
 
# ProxyCommand /usr/local/bin/ssh-tunnel.pl - - %h %p
 
# To enable a different port number for direct connection,
 
# use a proxy port prefixed by a ':'
 
# ProxyCommand /usr/local/bin/ssh-tunnel.pl - :8123 %h %p
 
#
 
# Revisions:
 
# - Add automatic proxy detection based on gateway IP Address & config file.
 
# - Add STDERR logging of proxy connection messages.
 
# - Acquire gateway instead of current IP address.
 
#
 
# See package ChangeLog for revision descriptions.
 
# twa<<
 
use strict;
 
use Socket;
 
use MIME::Base64;
 
use IO::Socket::SSL;
 
use Getopt::Long qw(:config no_ignore_case);
 
   
# Version string
 
my $version = "2.24";
 
   
# Initialize variables.
 
my $debug = 0;
 
my $quiet = 0;
 
my $dry = 0;
 
my $icmp = 0;
 
my $help = 0;
 
my $timeout = 1;
 
my $proxyuser = "";
 
my $proxypass = "";
 
my $proxyauth = "";
 
my $authlog = "";
 
my $noproxy1 = 0;
 
my $forceproxy = 0;
 
my $logcount = 20;
 
my $logfile = $ENV{HOME}.'/.ssh/proxy.log';
 
my $confile = $ENV{HOME}.'/.ssh/proxy.conf';
 
my $gateway;
 
my $peerhost;
 
my $peerport;
 
my $connecthost;
 
my $connectport;
 
my $rproxyhost2;
 
my $rproxyuser2 = "";
 
my $rproxypass2 = "";
 
my $rproxyauth2 = "";
 
my $rauthlog2 = "";
 
my $sslencrypt = 0;
 
my $l2proxy;
 
my $l2user;
 
my $l2pass;
 
my $l2cred;
 
my $l2auth = 'Basic';
 
my $l2config;
 
my $automode = '-';
 
my $sslignore = 0;
 
my $sslfingerprint_md5;
 
my $sslfingerprint_sha1;
 
my $sslfingerprint_sha256;
 
my $noaltport;
 
my $ntlm = 0;
 
my $ntlm2 = 0;
 
my $authtype = 'Basic';
 
my $rauthtype2 = 'Basic';
 
my $server_banner; # "SSH-2.0-OpenSSH_4.3p2 Debian-9";
 
my $client_banner; # "SSH-2.0-OpenSSH_4.6";
 
my $credfile;
 
my $rcredfile2;
 
my $tunlogfile;
 
my $dummy;
 
my %dlevels = (
 
'verbose' => 0, # Basic operations.
 
'low' => 1, # Connection infos.
 
'medium' => 2, # Decision operations.
 
'high' => 3, # Detailled decision ops.
 
'details' => 4 ); # Function internal details.
 
 
# Usage
 
sub usage {
 
my $tab = " ";
 
print STDERR "Error: $_[0]\n\n" if $_[0];
 
print STDERR "Usage: $0 proxy port destination port {Options}\n";
 
print STDERR "Version: $version\n";
 
print STDERR "Options:\n";
 
print STDERR " -h, --help ".
 
"display this help.\n";
 
print STDERR " -d, --debug ".
 
"enable and increment debug level.\n";
 
print STDERR " -q, --quiet ".
 
"enable quiet mode, suppress connection messages.\n";
 
print STDERR " -n, --dry ".
 
"show selection steps, do not connect.\n";
 
print STDERR " -c, --config=FILE ".
 
"set config file (default: $confile)\n";
 
print STDERR " -t, --timeout=TIME ".
 
"set ping timeout in seconds.\n";
 
print STDERR " -i, --icmp ".
 
"enable also ICMP ping (default: tcp only).\n";
 
print STDERR " -u, --user=USER ".
 
"enable authentication against proxy: user\n";
 
print STDERR " -p, --pass=PASSWORD ".
 
"enable authentication against proxy: password\n";
 
print STDERR " -g, --cred=FILE ".
 
"specifies a file that contains a username and/or\n";
 
print STDERR $tab."password for auth. against proxy.\n";
 
print STDERR " -l, --ntlm ".
 
"enable NTLM authentication against proxy (Default:\n";
 
print STDERR " ".
 
"Basic). Requires user and password (-u/-p or -g)\n";
 
print STDERR " -f, --forceproxy ".
 
"force going through proxy even if directly reachable\n";
 
print STDERR " -e, --encrypt ".
 
"encrypt the communication using SSL.\n";
 
print STDERR " -I, --sslignore ".
 
"ignore fingerprint verification.\n";
 
print STDERR " -s, --noproxy1 ".
 
"skip connection to primary proxy.\n";
 
print STDERR " -A, --noaltport ".
 
"ignore alternate port specified in ProxyCommand line.\n";
 
print STDERR " -C, --client=banner ".
 
"Send pre-session banner to the server at startup.\n";
 
print STDERR $tab."The banner argument is either a string or a file\n";
 
print STDERR $tab."containing the banner string. The file is\n";
 
print STDERR $tab."automatically updated with new banner, if any.\n";
 
print STDERR " -S, --server=banner ".
 
"Send pre-session banner to the client at startup.\n";
 
print STDERR $tab."See option '--client' for banner argument details.\n";
 
print STDERR " -T, --tunlog=FILE ".
 
"specifies a file to log connection peer host and port.\n";
 
print STDERR "\n";
 
print STDERR "Options for 2nd level proxy:\n";
 
print STDERR " -R, --rproxy=STRING ".
 
"use a remote (2nd level) proxy to tunnel over.\n";
 
print STDERR $tab."The 2nd level proxy is mainly used in combination\n";
 
print STDERR $tab."with SSL encryption to unwrap the SSL session.\n";
 
print STDERR " -U, --ruser=USER ".
 
"enable auth against remote proxy: user\n";
 
print STDERR " -P, --rpass=PASSWORD ".
 
"enable auth against remote proxy: password\n";
 
print STDERR " -G, --CRED=FILE ".
 
"specifies a file that contains a username and/or\n";
 
print STDERR $tab."password for auth. against remote proxy.\n";
 
print STDERR " -L, --NTLM ".
 
"enable NTLM auth. against remote proxy (Default:\n";
 
print STDERR " ".
 
"Basic). Requires user and password (-U/-P or -G)\n";
 
print STDERR " -2, --l2config ".
 
"use level-2 proxy defined in configuration file.\n";
 
print STDERR $tab."This is a shortcut to -R optionally with -U & -P.\n";
 
print STDERR $tab."Caution: encryption (-e) must be enabled separately.\n";
 
my $url = "http://www.yobi.be/mediawiki/index.php/Bypass_Proxy";
 
print STDERR "\n";
 
print STDERR "See $url for more info,\n";
 
print STDERR "specially the section 'Through real SSL Proxies'.\n";
 
exit(0);
 
}
 
 
# Function to process options, used to parse command line and config line.
 
sub readoptions {
 
Getopt::Long::Configure('no_pass_through');
 
return GetOptions
 
( "d|debug+" => \$debug,
 
"q|quiet" => \$quiet,
 
"q|quite" => \$quiet,
 
"n|dry" => \$dry,
 
"h|help" => \$dummy, # As reminder from prev. GetOptions()
 
"i|icmp" => \$icmp,
 
"c|config=s" => \$dummy, # As reminder from prev. GetOptions()
 
"l|ntlm" => \$ntlm,
 
"t|timeout=i" => \$timeout,
 
"u|user=s" => \$proxyuser,
 
"p|pass=s" => \$proxypass,
 
"g|cred=s" => \$credfile,
 
"T|tunlog=s" => \$tunlogfile,
 
"e|encrypt" => \$sslencrypt,
 
"I|sslignore" => \$sslignore,
 
"s|noproxy1" => \$noproxy1,
 
"A|noaltport" => \$noaltport,
 
"R|rproxy=s" => \$rproxyhost2,
 
"U|ruser=s" => \$rproxyuser2,
 
"P|rpass=s" => \$rproxypass2,
 
"G|CRED=s" => \$rcredfile2,
 
"L|NTLM" => \$ntlm2,
 
"2|l2config" => \$l2config,
 
"C|client=s" => \$client_banner,
 
"S|server=s" => \$server_banner,
 
"f|forceproxy" => \$forceproxy );
 
}
 
 
# Options order:
 
# 1. read config file - section general option
 
# 2. command-line options from ProxyCommand
 
# 3. command-line options from ssh wrapper thru ENV ProxyCommandOptions
 
# 4. read config file - section proxy(s)
 
# 5. override option with command-line options from ssh wrapper through
 
# ENV ProxyCommandOptions
 
# NOTE: SSH wrapper command line options overrule other options.
 
 
# Add options from Environment Variable.
 
if (defined($ENV{ProxyCommandOptions})) {
 
push @ARGV, split(/ /,$ENV{ProxyCommandOptions});
 
 
# Cleanup incremental options, for later usage of ProxyCommandOptions
 
# in config_read_proxy2()
 
$ENV{ProxyCommandOptions} =~ s/-d//g;
 
}
 
 
# Update config filename, if required.
 
Getopt::Long::Configure('pass_through'); # Required to check only one option.
 
GetOptions ( "c|config=s" => \$confile,
 
"h|help" => \$help ) ||
 
usage('getting config filename');
 
usage() if $help;
 
 
# Read configuration file, if any, and process option section.
 
config_read_options($confile);
 
 
# Parse command line options, if any.
 
readoptions() || usage('getting command-line options');
 
 
# Reset debug level, if quiet mode enabled.
 
$debug = 0 if $quiet;
 
print STDERR "Debug enabled, level-$debug (Version: $version)\n"
 
if $debug >$dlevels{low};
 
 
# Parse command line arguments
 
usage("missing arguments !") unless $#ARGV>2;
 
my $proxyhost = shift;
 
my $proxyport = shift;
 
my $desthost = shift;
 
my $destport = shift;
 
 
# ######################################################################
 
# Disable alternate port, if required.
 
$proxyport = '-' if $noaltport && $proxyport =~ m/^:/;
 
 
# ######################################################################
 
# Steps:
 
# 1- detect direct connection
 
# 2- detect primary proxy
 
# 3- use level 2 proxy, if any.
 
 
# Step 1- Try direct connection first.
 
unless ($forceproxy) {
 
if ( $proxyport =~ m/^:/ ) {
 
my $port = $proxyport;
 
$port =~ s/^://;
 
print STDERR "Direct connect (alternate port: $desthost:$port)\n"
 
if $debug>$dlevels{medium};
 
if ( reachable($desthost,$port) ) {
 
print STDERR "$desthost:$port seems directly ".
 
"accessible, let's go direct\n" if $debug>$dlevels{medium};
 
# Do not change destport yet, we still might need the real
 
# destport (eg: whenever l2proxy is forced/used).
 
# Destport will be changed later (See Mode C).
 
$proxyhost = 'direct';
 
}
 
} else {
 
print STDERR "Direct connect: $desthost:$destport\n"
 
if $debug>$dlevels{medium};
 
if ( reachable($desthost,$destport) ) {
 
print STDERR "$desthost:$destport seems directly ".
 
"accessible, let's go direct\n" if $debug>$dlevels{medium};
 
$proxyhost = 'direct';
 
}
 
}
 
}
 
 
# ######################################################################
 
# Modes: (A) (B) (C) (D)
 
# 2 proxies Primary proxy Direct 2nd proxy only
 
# ----------------------------------------------------------------------
 
# peerhost: proxyhost proxyhost desthost rproxyhost2
 
# connecthost: rproxyhost2 desthost - -
 
# 2nd connect: desthost - - desthost
 
# ######################################################################
 
 
# Auto-selection enabled: force to skip primary proxy unless already in
 
# direct mode.
 
$proxyhost = 'direct-def' if $noproxy1 && $proxyhost !~ m/^direct/;
 
 
# Step 2- detect primary proxy
 
# Auto-selection enabled: detecting proxy value.
 
if ( $proxyhost =~ m/^$automode/ ) {
 
# Get gateway IP address.
 
$gateway = getgateway() || '127.0.0.1';
 
 
print STDERR "$gateway: Proxy auto-selection...\n"
 
if $debug>$dlevels{verbose};
 
$proxyhost = 'direct-def'; # Set default in case of no match.
 
 
# Read configuration file and process proxy section: try new format 1st.
 
config_read_proxy2($confile) ||
 
config_read_proxy($confile);
 
}
 
 
# Use config defined level-2 proxy, if any.
 
if ( $l2config && defined($l2proxy)) {
 
print STDERR "Using config defined remote proxy ($l2proxy)\n"
 
if $debug>$dlevels{medium};
 
# Use config defined level2 proxy.
 
$rproxyhost2 = $l2proxy;
 
$rproxyuser2 = $l2user if $l2user;
 
$rproxypass2 = $l2pass if $l2pass;
 
$rauthtype2 = $l2auth if $l2auth;
 
$rcredfile2 = $l2cred if $l2cred;
 
}
 
 
# Read usernames and passwords from credentials files, if any.
 
my ($u,$p) = read_credentials($credfile) if $credfile;
 
$proxyuser = $u if $u;
 
$proxypass = $p if $p;
 
 
my ($u,$p) = read_credentials($rcredfile2) if $rcredfile2;
 
$rproxyuser2 = $u if $u;
 
$rproxypass2 = $p if $p;
 
 
# Compute proxy authentication, if required.
 
$authtype = 'NTLM' if $ntlm;
 
$proxyauth = $authtype.' '.encode_base64($proxyuser.':'.$proxypass)
 
if $proxyuser;
 
$proxyauth =~ s/[\r\n]*$//;
 
$authlog = " with auth token $proxyauth" if $proxyauth;
 
$authlog = " with NTLM Authentication" if $proxyauth && $ntlm;
 
print STDERR "proxyuser= $proxyuser\n" if $debug>$dlevels{high};
 
print STDERR "proxypass= $proxypass\n" if $debug>$dlevels{high};
 
 
# Compute remote proxy authentication, if required.
 
$rauthtype2 = 'NTLM' if $ntlm2;
 
$rproxyauth2 = $rauthtype2.' '.encode_base64($rproxyuser2.':'.$rproxypass2)
 
if $rproxyuser2;
 
$rproxyauth2 =~ s/[\r\n]*$//;
 
$rauthlog2 = " with auth token $rproxyauth2" if $rproxyauth2;
 
$rauthlog2 = " with NTLM Authentication" if $rproxyauth2 && $ntlm2;
 
 
# Assign peer host and port with primary proxy (static or auto selected).
 
unless ( $proxyhost =~ m/^direct/ ) {
 
$proxyport =~ s/^://;
 
print STDERR "proxy=> $proxyhost:$proxyport\n" if $debug>$dlevels{high};
 
$peerhost = $proxyhost;
 
$peerport = $proxyport;
 
 
if (defined($rproxyhost2) ) {
 
# Mode A: proxyhost,rproxyhost2,desthost
 
print STDERR "Mode A: proxyhost,rproxyhost2,desthost\n"
 
if $debug>$dlevels{high};
 
($connecthost,$connectport) = split(/:/,$rproxyhost2);
 
unless (defined($connectport)) {
 
$connectport = 80;
 
$connectport = 443 if $sslencrypt;
 
}
 
} else {
 
# Mode B: proxyhost,desthost,-
 
print STDERR "Mode B: proxyhost,desthost,-\n"
 
if $debug>$dlevels{high};
 
$connecthost = $desthost;
 
$connectport = $destport;
 
}
 
}
 
 
# Step 3- use level 2 proxy, if any.
 
# Auto-selection enabled: no proxy found, use 2nd proxy if defined.
 
if ( $proxyhost =~ m/^direct/ && defined($rproxyhost2) ) {
 
# Mode D: rproxyhost2,-,desthost
 
print STDERR "Mode D: rproxyhost2,-,desthost\n"
 
if $debug>$dlevels{high};
 
($peerhost,$peerport) = split(/:/,$rproxyhost2);
 
unless (defined($peerport)) {
 
$peerport = 80;
 
$peerport = 443 if $sslencrypt;
 
}
 
print STDERR "Primary proxy not found, ".
 
"connect to 2nd proxy: $peerhost:$peerport\n"
 
if $debug>$dlevels{medium};
 
 
# Reset primary proxy host and port.
 
$proxyhost = undef;
 
$proxyport = 0;
 
}
 
# Auto-selection enabled: direct connection is possible.
 
if ( $proxyhost =~ m/^direct/ ) {
 
# Mode C: desthost,-,-
 
print STDERR "Mode C: desthost,-,-\n"
 
if $debug>$dlevels{high};
 
my $direct = 'DIRECT';
 
$direct = 'DEFAULT' if $proxyhost =~ m/^direct-def/;
 
print STDERR "Nothing relevant, let's try direct ($direct)\n"
 
if $debug>$dlevels{medium} && $proxyhost =~ m/^direct-def/;
 
 
# Check for alternate destination port (again, in case of --forceproxy).
 
if ( $proxyport =~ m/^:/ ) {
 
print STDERR "Using alternate destination port ".
 
"($proxyport iso $destport).\n" if $debug>$dlevels{high};
 
$proxyport =~ s/^://;
 
$destport = $proxyport;
 
}
 
 
# Void the use of proxy by assigning direct destination and port.
 
$peerhost = $desthost;
 
$peerport = $destport;
 
 
# Reset primary/2nd proxy and encryption.
 
$proxyhost = undef;
 
$proxyport = 0;
 
$rproxyhost2 = undef;
 
$sslencrypt = 0;
 
 
# Log connections.
 
tunnel_log("$gateway: $direct connect to $peerhost:$peerport");
 
}
 
 
printf STDERR ("[1] Connecting to $peerhost:$peerport$authlog\n")
 
if $debug>$dlevels{low};
 
printf STDERR ("[2] Connecting to $connecthost:$connectport$rauthlog2\n")
 
if defined($connecthost) && $debug>$dlevels{low};
 
printf STDERR ("[3] %sConnecting to $desthost:$destport\n",
 
$sslencrypt ? 'SSL-':'')
 
if defined($rproxyhost2) && $debug>$dlevels{low};
 
exit(0) if $dry;
 
# twa<< 2006-08-24
 
 
# Set up network communication
 
# twa>> 2007-02-20: replace socket functions with IO::Socket::INET module.
 
my $tunnel = IO::Socket::INET->new(PeerHost => $peerhost,
 
PeerPort => $peerport,
 
Proto => 'tcp' );
 
die "Connection failed: $peerhost:$peerport\n" unless $tunnel;
 
# twa<< 2007-02-20
 
 
if ( $tunlogfile && open(my $log, ">$tunlogfile") ) {
 
my $ipaddr = gethostbyname($peerhost);
 
if ( defined($ipaddr) ) {
 
$ipaddr = inet_ntoa($ipaddr);
 
print $log "tunnel $ipaddr $peerhost $peerport gw $gateway\n";
 
close ($log);
 
}
 
}
 
 
# Force flushing of socket buffers
 
select ($tunnel); $| = 1;
 
select (STDOUT); $| = 1;
 
 
# Direct connection does NOT require CONNECT command.
 
if ( defined($connecthost) ) {
 
# Log connections.
 
tunnel_log("$gateway: Proxy connect to $connecthost:$connectport",
 
"via $peerhost:$peerport".$authlog);
 
 
# Send a "CONNECT" command to proxy:
 
proxy_connect($connecthost,$connectport,$proxyauth);
 
}
 
 
# twa>> 2007-02-16
 
# Adding support for SSL encryption, convert to SSL session.
 
if ( $sslencrypt ) {
 
tunnel_log("$gateway: Enabling SSL");
 
IO::Socket::SSL->start_SSL($tunnel);
 
 
my $cert = $tunnel->peer_certificate();
 
eval {
 
unless($sslignore) {
 
Net::SSLeay::X509_get_fingerprint($cert,'MD5');
 
}
 
};
 
if ( $@ ) {
 
print STDERR "\n";
 
print STDERR "# ============================================== #\n";
 
print STDERR "# THREAD: X509_get_fingerprint is not supported, #\n";
 
print STDERR "# in your installed version of #\n";
 
print STDERR "# Net:SSLeay.pm. Cannot authenticate #\n";
 
print STDERR "# peer certificate, aborting ! #\n";
 
print STDERR "# Use option '-I|--sslignore' at your own risk. #\n";
 
print STDERR "# ============================================== #\n";
 
exit(0);
 
} else {
 
unless($sslignore) {
 
# Per default, we need to print next warning unless a fingerprint
 
# is available from the configuration file.
 
$sslignore = 1;
 
 
# Record and verify available fingerprints.
 
my %fingerprints;
 
$fingerprints{MD5} = $sslfingerprint_md5
 
if defined($sslfingerprint_md5);
 
$fingerprints{SHA1} = $sslfingerprint_sha1
 
if defined($sslfingerprint_sha1);
 
$fingerprints{SHA256} = $sslfingerprint_sha256
 
if defined($sslfingerprint_sha256);
 
foreach (sort keys %fingerprints) {
 
print STDERR "Verifying $_ Fingerprint...\n"
 
if $debug > $dlevels{Medium};
 
$sslignore = 0;
 
my $fingerprint = Net::SSLeay::X509_get_fingerprint($cert,$_);
 
next if $fingerprints{$_} eq $fingerprint;
 
print STDERR "=============================================\n";
 
print STDERR "THREAT: unexpected $_ fingerprint:\n";
 
print STDERR " Certificate: $fingerprint\n";
 
print STDERR " Expected: $fingerprints{$_}\n";
 
print STDERR "\n";
 
print STDERR "Peer certificate is not authenticated,\n";
 
print STDERR "Aborting!\n";
 
print STDERR "Use option '-I|--sslignore' at your own risk.\n";
 
print STDERR "=============================================\n";
 
exit(0);
 
}
 
}
 
if ($sslignore) {
 
unless ($quiet) {
 
print STDERR "\n";
 
print STDERR "# ========================================= #\n";
 
print STDERR "# WARNING: peer certificate fingerprint is #\n";
 
print STDERR "# not authenticated ! #\n";
 
print STDERR "# ========================================= #\n";
 
print STDERR "\n";
 
}
 
}
 
}
 
print STDERR "SSL-" unless $quiet;
 
}
 
 
# Adding support for 2nd level proxy (ie: remote proxy)
 
if (defined($rproxyhost2)) {
 
# Log connections.
 
my $via = "via $peerhost:$peerport";
 
$via = "via $connecthost:$connectport" if defined($connecthost);
 
tunnel_log("$gateway: 2nd Proxy connect to $desthost:$destport",
 
"$via".$rauthlog2);
 
 
# Send a "CONNECT" command to 2nd proxy:
 
proxy_connect($desthost,$destport,$rproxyauth2);
 
} else {
 
# Finishing "SSL-" line/string whenever 2nd proxy is not enabled.
 
print STDERR "Enabled\n" if $sslencrypt;
 
}
 
# twa<< 2007-02-16
 
 
# twa&pte>> 2006-08-28
 
# Apparently ssh sends a HUP signal before terminating STDIN session.
 
# Catch and ignore HUP in order for the main process to exit the while
 
# loop and continue killing the child process.
 
$SIG{HUP} = 0;
 
# twa&pte<< 2006-08-28
 
 
# Start copying packets in both directions.
 
if(my $child = fork) { # Parent process
 
if ($client_banner) {
 
my $client_banner_file;
 
if ( -f $client_banner ) {
 
$client_banner_file = $client_banner;
 
print STDERR "Reading client banner from $client_banner_file.\n"
 
if $debug>$dlevels{medium};
 
if ( open(my $fh, $client_banner)) {
 
$client_banner = <$fh>;
 
$client_banner =~ s/[\r\n]*$//;
 
close($fh);
 
}
 
}
 
print STDERR "Forcing client banner $client_banner to the server\n"
 
unless $quiet;
 
# Forcing client banner
 
# CAUTION: \r (CR) is required by the proxy to start processing data,
 
# ie: forwarding data or initialize remote connection.
 
$tunnel->syswrite("$client_banner\r\n");
 
# Read client banner
 
sysread(STDIN,$_,4096) || die "Cannot read ssh-client banner: $!";
 
unless (m/$client_banner/i) {
 
print STDERR "Client banner does not match, expecting $_\n";
 
if ($client_banner_file) {
 
if (open(my $fh, ">$client_banner_file")) {
 
print STDERR "Updating client banner file: ".
 
"$client_banner_file\n with $_\n";
 
print STDERR "Try again... \n";
 
print $fh $_;
 
close($fh);
 
}
 
}
 
kill(15,$child) if $child;
 
exit(1);
 
}
 
}
 
while (sysread(STDIN,$_,4096)) {
 
$tunnel->syswrite($_)
 
}
 
kill(15,$child) if $child;
 
}
 
else { # Child process
 
if ( $server_banner ) {
 
my $server_banner_file;
 
if ( -f $server_banner ) {
 
$server_banner_file = $server_banner;
 
print STDERR "Reading server banner from $server_banner_file.\n"
 
if $debug>$dlevels{medium};
 
if ( open(my $fh, $server_banner)) {
 
$server_banner = <$fh>;
 
$server_banner =~ s/[\r\n]*$//;
 
close($fh);
 
}
 
}
 
print STDERR "Forcing server banner $server_banner to the client\n"
 
unless $server_banner;
 
# Forcing server banner
 
print "$server_banner\n";
 
# Read server banner
 
$tunnel->sysread($_,4096) || die "Cannot read ssh-server banner: $!\n";
 
unless (m/$server_banner/i) {
 
print STDERR "Server banner does not match, expecting $_\n";
 
if ($server_banner_file) {
 
if (open(my $fh, ">$server_banner_file")) {
 
print STDERR "Updating server banner file: ".
 
"$server_banner_file\n with $_\n";
 
print STDERR "Try again... (Hit Ctrl-C first)\n";
 
print $fh $_;
 
close($fh);
 
}
 
}
 
exit(1);
 
}
 
}
 
while ($tunnel->sysread($_,4096)) {
 
syswrite(STDOUT,$_) unless $server_banner && m/$server_banner/i;
 
}
 
}
 
# That's all folks.
 
exit(0);
 
 
# ############################################################
 
# Config: read configuration file - options section.
 
sub config_read_options {
 
my $filename = shift;
 
open (my $fh,$filename) || return 0;
 
# Skip line until options section
 
while(<$fh>) {
 
last if m/^\[/ && m/section/i && m/option/i;
 
}
 
# Process options, until start of next section.
 
while(<$fh>) {
 
last if m/^\[/;
 
next if m/^$/ || m/^#/;
 
next unless m/=/;
 
s/^[ \t]*//;
 
s/^[ \t]*(.*?)[ \t]*=[ \t]*(.*)[ \t]*/\1="\2"/;
 
s/^(.*)="([0-9]+)"/\1=\2/;
 
eval "\$$_";
 
}
 
close($fh);
 
return 1;
 
}
 
 
# ############################################################
 
# Config: read configuration file - proxy section.
 
sub config_read_proxy {
 
my $filename = shift;
 
my $try_ping = 1;
 
 
print STDERR "Reading proxy section: $filename\n"
 
if $debug>$dlevels{high};
 
 
# Parse configuration file.
 
open (my $fh,$filename) ||
 
die "Cannot open file $filename: $!\n";
 
 
# Skip section up to proxies.
 
while(<$fh>) {
 
# Do not stop at new format proxy section: proxy2
 
next if m/^\[/ && m/section/i && m/proxy2/i;
 
last if m/^\[/ && m/section/i && m/proxy/i;
 
}
 
 
# Process proxy section.
 
while(<$fh>) {
 
last if m/^\[/;
 
next if m/^$/ || m/^#/;
 
my @list = split;
 
 
# Check if subnet match.
 
next unless isInNet($gateway,$list[0]);
 
 
# Extract standalone ssl request.
 
my $sslmode = $list[1] =~ m/\+ssl/i;
 
 
# Extract level-2 and ssl request.
 
my $l2mode = $list[1] =~ m/\+l2/i;
 
$sslmode = $list[1] =~ m/\+l2ssl/i;
 
 
# Remove +string from proxy value.
 
$list[1] =~ s/\+(\w)*//i;
 
 
print STDERR "l2mode= $l2mode, sslmode= $sslmode, ".
 
"proxy=\"$list[1]\"\n" if $debug>$dlevels{details};
 
 
if ($list[1] =~ m/^$/ ) {
 
print STDERR "Found a match: without proxy.\n"
 
if $debug>$dlevels{high};
 
if ( $l2mode ) {
 
print STDERR "Using config defined remote proxy ($l2proxy)\n"
 
if $debug>$dlevels{medium};
 
# Use config defined level2 proxy.
 
$rproxyhost2 = $l2proxy;
 
$rproxyuser2 = $l2user;
 
$rproxypass2 = $l2pass;
 
$rauthtype2 = $l2auth;
 
}
 
$sslencrypt = 1 if $sslmode;
 
last;
 
}
 
 
# Found one match, detecting if proxy is required:
 
my ($cfgproxy,$cfgport) = split(/:/,$list[1]);
 
print STDERR "Found a match: $cfgproxy:$cfgport\n"
 
if $debug>$dlevels{medium};
 
# Check once if directly accessible.
 
if (! $forceproxy && $try_ping && reachable($desthost,$destport)) {
 
print STDERR "But $desthost:$destport seems directly ".
 
"accessible, let's go direct\n" if $debug>$dlevels{medium};
 
$proxyhost = 'direct';
 
last;
 
} else {
 
$try_ping = 0;
 
# Test if proxy is alive/reachable.
 
next unless reachable($cfgproxy,$cfgport);
 
print STDERR "And we can ping the proxy, good!\n"
 
if $debug>$dlevels{medium};
 
$proxyhost = $cfgproxy;
 
$proxyport = $cfgport;
 
 
# Check if proxy authentication is required.
 
$proxyuser = $list[2] if $list[2];
 
$proxypass = $list[3] if $list[3];
 
$authtype = $list[4] if $list[4];
 
 
if ( $l2mode ) {
 
print STDERR "Using config defined remote proxy ($l2proxy)\n"
 
if $debug>$dlevels{medium};
 
# Use config defined level2 proxy.
 
$rproxyhost2 = $l2proxy;
 
$rproxyuser2 = $l2user;
 
$rproxypass2 = $l2pass;
 
$rauthtype2 = $l2auth;
 
}
 
$sslencrypt = 1 if $sslmode;
 
last;
 
}
 
}
 
close($fh);
 
return 1;
 
}
 
 
# ############################################################
 
# Config: read configuration file - proxy2 section: NEW format
 
sub config_read_proxy2 {
 
my $filename = shift;
 
my $try_ping = 1;
 
my $ret = 0;
 
 
print STDERR "Reading proxy2 section: $filename\n"
 
if $debug>$dlevels{high};
 
 
# Parse configuration file.
 
open (my $fh,$filename) ||
 
die "Cannot open file $filename: $!\n";
 
 
# Skip section up to proxies.
 
while(<$fh>) {
 
last if m/^\[/ && m/section/i && m/proxy2/i;
 
}
 
 
# Process proxy section.
 
while(<$fh>) {
 
last if m/^\[/;
 
next if m/^$/ || m/^#/;
 
my ($subnet,$lproxy,@options) = split;
 
 
# Format: $subnet.\w.$proxyhost:$proxyport.\w.@options
 
 
# Check if subnet match.
 
next unless isInNet($gateway,$subnet);
 
 
# Single char string = no proxy specified!
 
if ( $lproxy =~ m/^.$/ ) {
 
print STDERR "Found a match: without proxy.\n"
 
if $debug>$dlevels{high};
 
} else {
 
# Found one match, detecting if proxy is required:
 
my ($cfgproxy,$cfgport) = split(/:/,$lproxy);
 
print STDERR "Found a match: $cfgproxy:$cfgport\n"
 
if $debug>$dlevels{medium};
 
# Check once if directly accessible.
 
if (! $forceproxy && $try_ping && reachable($desthost,$destport)) {
 
print STDERR "But $desthost:$destport seems directly ".
 
"accessible, let's go direct\n" if $debug>$dlevels{medium};
 
$proxyhost = 'direct';
 
last;
 
} else {
 
$try_ping = 0;
 
# Test if proxy is alive/reachable.
 
next unless reachable($cfgproxy,$cfgport);
 
print STDERR "And we can ping the proxy, good!\n"
 
if $debug>$dlevels{medium};
 
$proxyhost = $cfgproxy;
 
$proxyport = $cfgport;
 
}
 
}
 
 
# Parse options from config-line
 
@ARGV = @options;
 
 
# Add options from Environment Variable to overwrite options.
 
push @ARGV, split(/ /,$ENV{ProxyCommandOptions})
 
if defined($ENV{ProxyCommandOptions});
 
 
print STDERR "Config-line args: ".join(' ', @ARGV)."\n"
 
if $debug>$dlevels{high};
 
readoptions() || usage("Getting config options: $subnet");
 
 
$ret = 1;
 
last;
 
}
 
close($fh);
 
return $ret;
 
}
 
 
# ############################################################
 
# Socket: issue a CONNECT command and interpret response.
 
sub proxy_connect {
 
my $host = shift;
 
my $port = shift;
 
my $auth = shift;
 
 
if ( $auth && $auth =~ m/^NTLM/i ) {
 
# Try to load Authen::NTLM Module.
 
eval {
 
require Authen::NTLM;
 
};
 
if ( $@ ) {
 
die "Unsupported Authentication type: NTLM\n".
 
"Module Authen::NTLM is not installed.";
 
}
 
 
$auth =~ s/^NTLM[ ]*//i;
 
$auth = decode_base64($auth);
 
my ($domain,$user,$passwd) = split(/[\\\/:]/,$auth);
 
 
# Initialize NTLM engine.
 
Authen::NTLM->ntlm_domain($domain);
 
Authen::NTLM->ntlm_user($user);
 
Authen::NTLM->ntlm_password($passwd);
 
 
# Proceed with first phase.
 
print STDERR "NTLM authentication: first phase.\n" unless $quiet;
 
my $auth = Authen::NTLM->ntlm();
 
Authen::NTLM->ntlm_reset();
 
 
my $authstring = "";
 
$authstring = "Proxy-Authorization: NTLM $auth\n" if $auth;
 
print STDERR "CONNECT $host:$port HTTP/1.0\n$authstring\n"
 
unless $quiet;
 
print $tunnel "CONNECT $host:$port HTTP/1.0\n$authstring\n";
 
 
# Wait for HTTP status code.
 
$_ = <$tunnel>;
 
print STDERR $_ unless $quiet;
 
my ($status) = (split())[1];
 
 
# The connection may already be authorized (yes it happenned).
 
if ( int($status/100) == 4 ) {
 
# die("Received a bad status code \"$status\" from proxy server.")
 
# if ( int($status/100) != 4 );
 
 
# Parse response, extract NTLM Challenge, skip body...
 
# Skip through remaining part of response: get the challenge.
 
my $challenge;
 
my $clength;
 
while(<$tunnel>) {
 
$challenge = $1 if m/^Proxy-Authenticate: NTLM (.*)/i;
 
$clength = $1 if m/^Content-Length: (.*)/i;
 
last if /^[\n\r]*$/; # Empty line or a single <CR> left
 
}
 
 
# Consume body text
 
while (<$tunnel>) {
 
$clength -= length($_);
 
last unless $clength > 0;
 
}
 
 
# Proceed with second phase.
 
print STDERR "NTLM authentication: second phase.\n" unless $quiet;
 
Authen::NTLM->ntlm();
 
$auth = Authen::NTLM->ntlm($challenge);
 
Authen::NTLM->ntlm_reset();
 
 
$authstring = "Proxy-Authorization: NTLM $auth\n" if $auth;
 
print STDERR "CONNECT $host:$port HTTP/1.0\n$authstring\n"
 
unless $quiet;
 
print $tunnel "CONNECT $host:$port HTTP/1.0\n$authstring\n";
 
 
# Wait for HTTP status code, bail out if you don't get back
 
# a 2xx code.
 
$_ = <$tunnel>;
 
print STDERR $_ unless $quiet;
 
($status) = (split())[1];
 
}
 
 
# Bail out if you don't get back a 2xx code.
 
die("Received a bad status code \"$status\" from proxy server.")
 
if ( int($status/100) != 2 );
 
 
# Skip through remaining part of MIME header
 
while(<$tunnel>) {
 
print STDERR $_ unless $quiet;
 
last if /^[\r\n]*$/; # Empty line or a single <CR> left
 
}
 
} else {
 
die "Unsupported Authentication type: $auth\n"
 
if $auth && $auth !~ m/^Basic/i;
 
 
my $authstring = "";
 
$authstring = "Proxy-Authorization: $auth\n" if $auth;
 
print STDERR "CONNECT $host:$port HTTP/1.0\n$authstring\n"
 
unless $quiet;
 
print $tunnel "CONNECT $host:$port HTTP/1.0\n$authstring\n";
 
 
# Wait for HTTP status code, bail out if you don't get back a 2xx code.
 
$_ = <$tunnel>;
 
print STDERR $_ unless $quiet;
 
my ($status) = (split())[1];
 
 
die("Received a bad status code \"$status\" from proxy server.")
 
if ( int($status/100) != 2 );
 
 
# Skip through remaining part of MIME header
 
while(<$tunnel>) {
 
print STDERR $_ unless $quiet;
 
last if /^[\r\n]*$/; # Empty line or a single <CR> left
 
}
 
}
 
print STDERR "Connected to $host:$port\n"
 
if $debug>$dlevels{verbose};
 
return 1;
 
}
 
 
# ############################################################
 
# Read credentials file containing username and password.
 
sub read_credentials {
 
my $filename = shift;
 
my ($u, $p);
 
open(my $fh, $filename) || return (undef,undef);
 
while (<$fh>) {
 
next if m/^#/;
 
s/[\r\n]*$//;
 
my @list = split(/=/);
 
$list[1] =~ s/^[ \t]*//;
 
$u = $list[1] if $list[0] =~ m/username/i;
 
$p = $list[1] if $list[0] =~ m/password/i;
 
}
 
close($fh);
 
return ($u,$p);
 
}
 
 
# ############################################################
 
# Logging: write log entry.
 
sub tunnel_log {
 
# Log connections, keep last $logcount entries.
 
my @lines;
 
if ( open(my $fh,$logfile) ) {
 
while(<$fh>) { push @lines, $_; }
 
close($fh);
 
}
 
while ($#lines > $logcount-2) { shift @lines; }
 
open(my $fh,">$logfile") || return 0;
 
while ($lines[0]) { print $fh shift @lines; }
 
print $fh "[$$] ".join(' ',@_,"\n");
 
close($fh);
 
return 1;
 
}
 
 
# ############################################################
 
# Util: Get default gateway IP address.
 
sub getgateway {
 
# Default is Linux
 
my $routecmd = '/sbin/route -n';
 
foreach ( 'OS', 'OSTYPE' ) {
 
next unless defined($ENV{$_});
 
$routecmd = 'route print' if
 
$ENV{$_} =~ m/cygwin/i ||
 
$ENV{$_} =~ m/windows/i;
 
last;
 
}
 
my $str = '^0.0.0.0';
 
$str = '^Default Gateway' if
 
$routecmd =~ m/print/;
 
my $result = `$routecmd | grep -i \"$str\"`;
 
$result =~ s/$str//;
 
$result =~ s/^[: \t]*//;
 
$result =~ s/[ \t\n\r]*$//;
 
my ($gateway) = split(/[ \t]/,$result);
 
return $gateway;
 
}
 
 
# ############################################################
 
# Util: determine if given host is reachable on given port.
 
# First attempt with TCP protocol.
 
# Second attempt (if enabled) with ICMP protocol.
 
sub reachable {
 
use Net::Ping;
 
my $host = $_[0];
 
my $port = $_[1];
 
my $p = Net::Ping->new('tcp');
 
$p->{port_num} = $port;
 
print STDERR "Try tcp-ping($host,$port,$timeout)\n"
 
if $debug>$dlevels{high};
 
my $reachable = $p->ping($host,$timeout);
 
$p->close();
 
return 1 if $reachable;
 
return 0 unless $icmp;
 
$p = Net::Ping->new('icmp');
 
print STDERR "Try icmp-ping($host,$timeout)\n"
 
if $debug>$dlevels{high};
 
$reachable = $p->ping($host,$timeout);
 
$p->close();
 
return $reachable;
 
}
 
 
# ############################################################
 
# Util: Expand subnet prefix: 192.168 becomes 192.168.0.0
 
sub expandPrefix {
 
my @list = split(/\./, $_[0].".0.0.0");
 
$#list = 3;
 
return join('.', @list);
 
}
 
 
# ############################################################
 
# Util: Check if the given IP Address is part of the given Subnet.
 
sub isInNet {
 
my ($addr,$subnet) = @_;
 
my ($prefix,$bits) = split(/\//,$subnet);
 
my $mask = netmask($bits);
 
my $ipaddr = inet_aton($addr);
 
$prefix = expandPrefix($prefix);
 
print STDERR "isInNet($addr,$subnet)\n" if $debug>$dlevels{high};
 
print STDERR "isInNet: prefix=$prefix, bits=$bits, ".
 
"mask=".inet_ntoa($mask)."\n" if $debug>$dlevels{details};
 
return (inet_ntoa(($ipaddr & $mask)) eq $prefix);
 
}
 
 
# ############################################################
 
# Util: Check if given IP Address is a Private Internet Address.
 
sub isPrivateNet {
 
my $ipaddr = $_[0];
 
my @privAddrs = ( "10/8", "172.16/12", "192.168/16", "169.254/16" );
 
foreach ( @privAddrs ) {
 
return 1 if isInNet($ipaddr,$_);
 
}
 
return 0;
 
}
 
 
# ############################################################
 
# Create netmask for a given number of bits.
 
# Example: netmask(11)= 0xFFE00000
 
sub netmask {
 
my $value = 0;
 
my $bits = 32 - $_[0];
 
while ($bits--) { $value <<= 1; $value |= 1; }
 
return inet_aton(~$value & 0xFFFFFFFF);
 
}
 
 
# ############################################################
 
# End Of File: ssh-tunnel.pl
 
</source>
 
 
Dans ~/.ssh/config ajouter une section du style:
 
Dans ~/.ssh/config ajouter une section du style:
 
Host hal
 
Host hal
Line 1,203: Line 86:
   
 
Le script fonctionne aussi bien sous linux que sous cygwin
 
Le script fonctionne aussi bien sous linux que sous cygwin
  +
  +
Exemple de bannière à mettre dans /tmp/clbanner.txt:
  +
SSH-2.0-OpenSSH_4.6p1 Debian-5
  +
  +
Si la bannière n'est pas correcte elle sera corrigée automatiquement et au 2ème essai cela marchera.
 
===Utilisation de SSH à travers un proxy requérant une identification===
 
===Utilisation de SSH à travers un proxy requérant une identification===
 
Il faut envoyer en plus la string d'identification au proxy
 
Il faut envoyer en plus la string d'identification au proxy
 
Dans proxy.conf il suffit d'ajouter le login et pwd dans une 3ème et 4ème colonne
 
Dans proxy.conf il suffit d'ajouter le login et pwd dans une 3ème et 4ème colonne
  +
===Having fun with socat, bash & file descriptors...===
  +
This section will show some alternatives to what the Perl script is capable of, just to show interesting capabilities of bash & socat people could not think about:
  +
  +
Could socat send the ssh client banner in advance then eats the real client banner later, as the Perl script can do, to force some stupid proxies to talk to the destination?
  +
  +
Let's re-explain the issue:
  +
* We want to connect to a SSH server through a HTTP proxy
  +
* We've to send a CONNECT command to the proxy and the proxy will reply, we hope, a "Connection established" message.
  +
* The problem is that some proxies will reply ok even before having tried to establish anything. Such a proxy will first wait for the first line of the client then only establish the connection to the server and forward the line to the server. This is logical for HTTP where the first command comes from the client (GET...) but in a SSH session the server is the first to send its banner.
  +
* So we'll send what will be the client banner to provoke the proxy to connect the server and get its banner. Once the client will get the server banner it'll start speaking, sending its own banner, which we've to intercept otherwise the server will get twice the client banner.
  +
  +
Proxycommand /usr/local/bin/socat -%%SYSTEM:"(echo -n -e 'SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n'; \
  +
read -n 32;exec cat)" TCP:%h:%p
  +
  +
Little explanation:
  +
* We use SYSTEM rather than EXEC because we've to execute some bash scripting. EXEC expects a single command so you'd have to do sth like EXEC:"bash -c '(echo ...'"
  +
* socat has to modify only the client-to-server channel, therefore the "-%SYSTEM" construction.
  +
* % is repeated because it's a metachar of Proxycommand we've to escape.
  +
* The bash operations are performing what we explained before on the client-to-server channel: sending the banner, eating the banner from the client, then forwarding the communication from that point.
  +
* The client banner is here hardcoded, not the best solution...
  +
* "exec cat" replaces the current bash script by the last command, cat, to have one less process.
  +
  +
Now let's take the connection to the proxy into account, using the native capabilities of socat:
  +
  +
Proxycommand /usr/local/bin/socat -%%SYSTEM:"(echo -n -e 'SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n'; \
  +
read -n 32;exec cat)" PROXY:%h:%p|TCP:my.proxy:8080
  +
  +
Now can we get rid of socat? Using only bash?? Yes!
  +
  +
Proxycommand /usr/local/bin/bash -c 'exec 3<>/dev/tcp/my.proxy/8080;echo -n -e "CONNECT %h:%p HTTP/1.0\r\n\r\n" >&3 ; \
  +
read -n 39 <&3;(exec cat <&3 &); echo -n -e "SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n" >&3; read -n 32;exec cat >&3'
  +
  +
* Note that I'm using a /usr/local/bin/bash, a recompiled version of bash because Debian disabled this nice capability of bash. For safety they say... did they even notice gawk is also capable of TCP?? Anyway this choice doesn't have proper rationale than "shut up, we know what's good for you". Thanks Debian...
  +
* read -n 39 is there to eat the reply of the proxy, could be different on yours...
  +
* At the end 3 processes will run: ssh and two "cat", one being in background. The problem is that that last one won't be killed at the end of the session.
  +
  +
How to replace the two "cat" to maintain the bidirectional communication?
  +
  +
Proxycommand /usr/local/bin/bash -c 'exec 3<>/dev/tcp/my.proxy/8080;echo -n -e "CONNECT %h:%p HTTP/1.0\r\n\r\n" >&3; \
  +
read -n 39 <&3; echo -n -e "SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n" >&3; head -c 50 <&3; read -n 32; \
  +
exec /usr/local/bin/socat - FD:3'
  +
  +
Back with socat, here used just to maintain the forwarding between stdin/stdout and file descriptor 3.
  +
* If you compare it with the two cat solution, note that forwarding from server to client was done before as the client will send its banner only once it gets something from the server. To achieve the same we introduced the "head -c 50" to forward part of the server-to-client communication before eating the client banner.
  +
* "head -c 50": the number 50 is not that important, it has to be bigger than the server banner (usually between 20 and 35 chars) and smaller than the first server packet (about 800 chars)
  +
  +
Hum ok but didn't we want to get rid of socat? (a reason could be that under Windows/cygwin socat is considered as "undesirable" by McAfee, no comment :-( )
  +
  +
Proxycommand /usr/local/bin/bash -c 'exec 3<>/dev/tcp/my.proxy/8080;echo -n -e "CONNECT %h:%p HTTP/1.0\r\n\r\n" >&3; \
  +
read -n 39 <&3; echo -n -e "SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n" >&3; head -c 50 <&3; read -n 32;\
  +
exec multitee 0:3 3:1'
  +
  +
Multitee (source [http://cr.yp.to/software/multitee-3.0.shar.gz here], also packaged in Debian) can do exactly what socat was doing: "socat - FD:3" == "multitee 0:3 3:1".
  +
<br>To compile multitee on Cygwin, see [[Cygwin-multitee|here]]
   
 
==Using SSH as a proxy==
 
==Using SSH as a proxy==
Line 1,531: Line 473:
 
<br>''Addresses cannot be nested, so a single socat process cannot, e.g., drive ssl over socks.'' but maybe in a future version...
 
<br>''Addresses cannot be nested, so a single socat process cannot, e.g., drive ssl over socks.'' but maybe in a future version...
 
<br>[Update] According to the developer ''I am very aware of the lack of this feature - yes, the bug mentioned in the docu refers to exactly your need. I have already considered and partially implemented different approaches for his, but none got ever near to release. But I still intend to realize this feature, because it would enable astonishing possibilities! Expect it to appear in a few years though...'' so maybe one day... :-)
 
<br>[Update] According to the developer ''I am very aware of the lack of this feature - yes, the bug mentioned in the docu refers to exactly your need. I have already considered and partially implemented different approaches for his, but none got ever near to release. But I still intend to realize this feature, because it would enable astonishing possibilities! Expect it to appear in a few years though...'' so maybe one day... :-)
  +
<br>'''[Update]''' version beta 2.0 supports it \o/
  +
ProxyCommand socat - 'PROXY:%h:%p,proxyauth=user:pass|SSL,verify=0|PROXY:my.server:443,proxyauth=user:pass|TCP:big.brother.proxy:8080'
   
 
====Client side: using the perl script====
 
====Client side: using the perl script====
 
What socat cannot do, our script can!
 
What socat cannot do, our script can!
 
ProxyCommand /usr/local/bin/ssh-tunnel.pl - 0 %h %p -e -R my.server:443 -U <user> -P <password> --noproxy
 
ProxyCommand /usr/local/bin/ssh-tunnel.pl - 0 %h %p -e -R my.server:443 -U <user> -P <password> --noproxy
  +
==Make your own SOCKS5==
  +
Example: download geo-locked content.
  +
===youtube-dl===
  +
apt-get install youtube-dl
  +
or
  +
wget https://yt-dl.org/latest/youtube-dl
  +
chmod +x youtube-dl
  +
  +
===SOCKS5===
  +
Just ssh to a box in the right area and enable SOCKS5 proxy:
  +
ssh -D 12345 remote_host
  +
Get proxychains
  +
apt-get install proxychains
  +
Edit /etc/proxychains.conf
  +
dynamic_chain
  +
quiet_mode
  +
tcp_read_time_out 15000
  +
tcp_connect_time_out 8000
  +
[ProxyList]
  +
socks5 127.0.0.1 12345
  +
It's also possible to redirect DNS through proxy ("proxy_dns") but if you don't need it, it will work smoother without.
  +
  +
Use it, e.g.:
  +
proxychains youtube-dl "Some geo-locked video web page"

Latest revision as of 17:50, 1 May 2015

Comment passer outre un proxy (mal configuré)

Background

L'article Tunneling SSL Through a WWW Proxy dont une copie est visible sur Bypass Proxy reference

Introduction: Cela marche-t-il sur mon proxy?

La première chose à faire est de voir si vous pouvez passer manuellement à travers votre proxy.
Après, on verra comment "automatiser" et crypter la méthode si ça marche (qqs scripts faits pour vous attendent)
Lorsque Mozilla se connecte à un serveur https à travers un proxy, il fait exactement la même chose que ce que nous allons utiliser: il emploie une commande CONNECT cf l'article donné comme background pour une description + complète.
Essayez:

telnet <proxy-server> <port du proxy>

puis (attention, frappe à l'aveugle...)

CONNECT www.bxlug.be:80 HTTP/1.0
1 return ou +

puis si ca marche le proxy renverra une chaîne style

HTTP/1.0 200 Connection established
Proxy-Agent: firewall wwwd

et là vous accédez de facon normale au service, exemple ici:

GET / HTTP/1.0
1 return ou +

Et vous pouvez tester ainsi le service que vous désirez, par exemple:

CONNECT www.teuwen.org:22 HTTP/1.0
->
HTTP/1.0 200 Connection established
Proxy-Agent: firewall wwwd
SSH-2.0-OpenSSH_3.4p1 Debian 1:3.4p1-1

Vous pouvez aussi recevoir un truc style

HTTP/1.0 407 Proxy-Auth
Proxy-Authenticate: Basic realm=proxy
Server: CSM Proxy Server
PROXY AUTHORIZATION ERROR
[etc]

si vous devez vous identifier à votre proxy.
Dans ce cas, un truc qui fonctionne parfois est de d'abord sortir sur le Net avec votre browser IE qui s'identifiera façon NT si le proxy est M$.
Le proxy vous reconnaîtra alors par après pour qqs temps.
Sinon, vous devez inclure une string d'identification avec la commande CONNECT mais c'est déjà plus complexe.
A la main, à part l'auth basique en base64, je ne vois pas trop, les autres sont un peu trop complexes pour se faire manuellement.

CONNECT host:port HTTP/1.0
Proxy-Authorization: basic dXNlcjpwYXNzd29yZA==

Où la string dXNlcjpwYXNzd29yZA== a été obtenue par

echo -n "user:password" |base64-encode
(package mime-codecs dans la Debian, d'autres utils existent aussi)

Si le proxy est bien configuré il ne devrait pas vous laisser passer sur n'importe quel port comme je le fais mais uniquement sur les ports pour les services sous TLS/SSL: https:443 imaps:993 ...
Dans ce cas vous devrez mettre votre ssh par exemple sur un tel port.
PS: Vous pouvez employer netcat au lieu de telnet, vous verrez ce que vous taperez, ca peut aider...
Bon hack!


Utilisation de SSH à travers un proxy

Récupérez la dernière version du script
Installez le script dans par exemple /usr/local/bin/ssh-tunnel.pl


Dans ~/.ssh/config ajouter une section du style:

 Host hal
   HostName www.teuwen.org
   ProxyCommand /usr/local/bin/ssh-tunnel.pl <nom du proxy> <port du proxy> %h %p

ou pour activer la détection automatique, en bas du ficher config:

 Host *
   ProxyCommand /usr/local/bin/ssh-tunnel.pl - - %h %p

avec un fichier ~/.ssh/proxy.conf style

# SSH Tunnel configuration file.

# Section: OPTIONS
[option-section]
debug   = 0
dry     = 0
timeout = 1

# Section: PROXY
[proxy-section]

# Example
1.2.3/24   myproxy:8080
# Section: PROXY2 - new format
# Syntax for each line:
# subnet[\w]proxy:port[\w]{options}
[proxy2-section]
# Example
1.2.3/24   myproxy:8080 -C /tmp/clbanner.txt

Le script fonctionne aussi bien sous linux que sous cygwin

Exemple de bannière à mettre dans /tmp/clbanner.txt:

SSH-2.0-OpenSSH_4.6p1 Debian-5

Si la bannière n'est pas correcte elle sera corrigée automatiquement et au 2ème essai cela marchera.

Utilisation de SSH à travers un proxy requérant une identification

Il faut envoyer en plus la string d'identification au proxy Dans proxy.conf il suffit d'ajouter le login et pwd dans une 3ème et 4ème colonne

Having fun with socat, bash & file descriptors...

This section will show some alternatives to what the Perl script is capable of, just to show interesting capabilities of bash & socat people could not think about:

Could socat send the ssh client banner in advance then eats the real client banner later, as the Perl script can do, to force some stupid proxies to talk to the destination?

Let's re-explain the issue:

  • We want to connect to a SSH server through a HTTP proxy
  • We've to send a CONNECT command to the proxy and the proxy will reply, we hope, a "Connection established" message.
  • The problem is that some proxies will reply ok even before having tried to establish anything. Such a proxy will first wait for the first line of the client then only establish the connection to the server and forward the line to the server. This is logical for HTTP where the first command comes from the client (GET...) but in a SSH session the server is the first to send its banner.
  • So we'll send what will be the client banner to provoke the proxy to connect the server and get its banner. Once the client will get the server banner it'll start speaking, sending its own banner, which we've to intercept otherwise the server will get twice the client banner.
 Proxycommand /usr/local/bin/socat -%%SYSTEM:"(echo -n -e 'SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n'; \
   read -n 32;exec cat)" TCP:%h:%p

Little explanation:

  • We use SYSTEM rather than EXEC because we've to execute some bash scripting. EXEC expects a single command so you'd have to do sth like EXEC:"bash -c '(echo ...'"
  • socat has to modify only the client-to-server channel, therefore the "-%SYSTEM" construction.
  • % is repeated because it's a metachar of Proxycommand we've to escape.
  • The bash operations are performing what we explained before on the client-to-server channel: sending the banner, eating the banner from the client, then forwarding the communication from that point.
  • The client banner is here hardcoded, not the best solution...
  • "exec cat" replaces the current bash script by the last command, cat, to have one less process.

Now let's take the connection to the proxy into account, using the native capabilities of socat:

 Proxycommand /usr/local/bin/socat -%%SYSTEM:"(echo -n -e 'SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n'; \
   read -n 32;exec cat)" PROXY:%h:%p|TCP:my.proxy:8080

Now can we get rid of socat? Using only bash?? Yes!

 Proxycommand /usr/local/bin/bash -c 'exec 3<>/dev/tcp/my.proxy/8080;echo -n -e "CONNECT %h:%p HTTP/1.0\r\n\r\n" >&3 ; \
   read -n 39 <&3;(exec cat <&3 &); echo -n -e "SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n" >&3; read -n 32;exec cat >&3'
  • Note that I'm using a /usr/local/bin/bash, a recompiled version of bash because Debian disabled this nice capability of bash. For safety they say... did they even notice gawk is also capable of TCP?? Anyway this choice doesn't have proper rationale than "shut up, we know what's good for you". Thanks Debian...
  • read -n 39 is there to eat the reply of the proxy, could be different on yours...
  • At the end 3 processes will run: ssh and two "cat", one being in background. The problem is that that last one won't be killed at the end of the session.

How to replace the two "cat" to maintain the bidirectional communication?

 Proxycommand /usr/local/bin/bash -c 'exec 3<>/dev/tcp/my.proxy/8080;echo -n -e "CONNECT %h:%p HTTP/1.0\r\n\r\n" >&3; \
   read -n 39 <&3; echo -n -e "SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n" >&3; head -c 50 <&3; read -n 32; \
   exec /usr/local/bin/socat - FD:3'

Back with socat, here used just to maintain the forwarding between stdin/stdout and file descriptor 3.

  • If you compare it with the two cat solution, note that forwarding from server to client was done before as the client will send its banner only once it gets something from the server. To achieve the same we introduced the "head -c 50" to forward part of the server-to-client communication before eating the client banner.
  • "head -c 50": the number 50 is not that important, it has to be bigger than the server banner (usually between 20 and 35 chars) and smaller than the first server packet (about 800 chars)

Hum ok but didn't we want to get rid of socat? (a reason could be that under Windows/cygwin socat is considered as "undesirable" by McAfee, no comment :-( )

 Proxycommand /usr/local/bin/bash -c 'exec 3<>/dev/tcp/my.proxy/8080;echo -n -e "CONNECT %h:%p HTTP/1.0\r\n\r\n" >&3; \
   read -n 39 <&3; echo -n -e "SSH-2.0-OpenSSH_5.1p1 Debian-3\r\n" >&3; head -c 50 <&3; read -n 32;\
   exec multitee 0:3 3:1'

Multitee (source here, also packaged in Debian) can do exactly what socat was doing: "socat - FD:3" == "multitee 0:3 3:1".
To compile multitee on Cygwin, see here

Using SSH as a proxy

ssh -D 1234

Or using putty, add a dynamic port forwarding (specify local port and no dest)

This simply enables ssh as a SOCKS v5 proxy.
Points your browser to the localhost:1234 socks proxy and enjoy!

Note that by default Firefox still does the DNS queries locally and this could either fail or discard sensitive informations so you can change this behavior (ff v1.5 or 2.0 at least) by changing the following in about:config

network.proxy.socks_remote_dns=true

Et apt-get install dante-client si on veut utiliser socksify pour permettre à des programmes ne connaissant pas les proxys socks de fonctionner malgré tout.

Utilisation d'autres services à travers un proxy requérant une identification (vieux!!)

Installez ce script dans par exemple /usr/local/bin/tunnel-auth.pl

#! /usr/bin/perl
#
# tunnel-auth.pl
#
# Usage: tunnel-auth.pl ssl-proxy port destination_host port localport AuthToken
#
# This script can be used for any program to connect to any port via localhost
# It traverses a www-proxy/firewall that supports the http CONNECT
# command described in
# http://home.netscape.com/newsref/std/tunneling_ssl.html
# & http://muffin.doit.org/doc/rfc/tunneling_ssl.html
#
# The AuthToken: cf http://www.zvon.org/tmRFC/RFC2617/Output/chapter2.html
# "To receive authorization, the client sends the userid and password, separated by a
# single colon (":") character, within a base64 encoded string in the credentials"
# Ex:  echo -n "user:password" |base64-encode -> dXNlcjpwYXNzd29yZA==
#
# Example, connect to host named "remote" outside of your firewall:
# $ tunnel-auth.pl www-proxy 80 remote dXNlcjpwYXNzd29yZA=='
# Then use localhost:2222 to connect remote:22!
#
# Comment all PRINT STDOUT if you don't want a dump.
#
# Written by Urban Kaveus <urban@statt.ericsson.se>
# Posted by Urban Kaveus to newsgroup comp.security.ssh on 23 Jan 1997.
# Addition of base64 Authentication by Doegox <phil@teuwen.org>
# Addition of local server for other services than SSH by Doegox <phil@teuwen.org>

#require 'sys/socket.ph';
use Socket;
# Parse command line arguments

if ( $#ARGV != 5 ) {
    print STDERR "Usage: $0 ssl-proxy port destination port localport AuthToken\n";
    exit(1);
}

$sslproxy    = shift;
$proxyport   = shift;
$destination = shift;
$destport    = shift;
$localport   = shift;
$authtoken   = shift;

print STDOUT "\nProxy: $sslproxy:$proxyport\nDest:  $destination:$destport\nLocalPort: $localport\n";

# Makes STDOUT "hot" so always flushed:
select((select(STDOUT), $|=1)[0]);

# Set up network communication

($protocol) = (getprotobyname("tcp"))[2];
($proxyip)  = (gethostbyname($sslproxy))[4];
$localaddr  = pack('S n a4 x8', &AF_INET, 0, "\0\0\0\0");
$proxyaddr  = pack('S n a4 x8', &AF_INET, $proxyport, $proxyip);

# Set up client socket
socket(Server, PF_INET, SOCK_STREAM, $protocol) or
    die("Failed to create socket for client connexion");
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) || die "setsockopt: $!";
bind(Server, sockaddr_in($localport, INADDR_ANY)) or
    die("Failed to bind socket for client connexion");
listen(Server,SOMAXCONN) or
    die("Failed to listen for client connexion");
my $paddr;
$SIG{CHLD} = \&REAPER;
$paddr = accept(Client,Server);
my($localport,$iaddr) = sockaddr_in($paddr);
my $name = gethostbyaddr($iaddr,AF_INET);
# Force flushing of socket buffers
select (Client); $| = 1;
print STDOUT "Client connected to $name\n";

# Set up proxy connexion
socket (PROXY, &AF_INET, &SOCK_STREAM, $protocol) or
    die("Failed to create socket for proxy connexion");
bind (PROXY, $localaddr) or
    die("Failed to bind socket for proxy connexion");
connect (PROXY, $proxyaddr) or
    die("Failed to connect to $sslproxy port $proxyport");

# Force flushing of socket buffers
select (PROXY);  $| = 1;

# Send a "CONNECT" command to proxy:

print PROXY "CONNECT $destination:$destport HTTP/1.0\r\nProxy-Authorization: Basic  $authtoken\r\n\r\n";

# Wait for HTTP status code, bail out if you don't get back a 2xx code.

$_ = <PROXY>;
print STDOUT "Server connected to $destination:$destport\n";
print STDOUT "Reply of server:$_";

($status) = (split())[1];

die("Received a bad status code \"$status\" from proxy server")
    if ( int($status/100) != 2 );

# Skip through remaining part of MIME header

while(<PROXY>) {
    chomp;   # Strip <LF>
    last if /^[\r]*$/;		# Empty line or a single <CR> left
}

# Start copying packets in both directions.

if($child = fork) { # Parent process
    while (sysread(PROXY,$_,4096)) {
		print STDOUT "\n\n#<-#$_";
        print Client;
    }
    sleep 2;
    kill(15,$child) if $child;
}

else { # Child process
    while (sysread(Client,$_,4096)) {
    	print STDOUT "\n\n#->#$_";
		print PROXY;
    }
}

Pour l'utiliser par exemple pour Jabber:

/usr/local/bin/tunnel-auth.pl <nom du proxy> <port du proxy> 212.68.212.217 <string d'identification>

Le client peut alors se connecter sur 127.0.0.1:5222
Par défaut, un dump de la communication apparaîtra dans la console.
Si cela est dérangeant, par exemple si la communication se fait en SSL, rediriger la sortie.

/usr/local/bin/tunnel-auth.pl <nom du proxy> <port du proxy> 212.68.212.217 <string d'identification> >/dev/null

L'adresse de destination doit sans doutes être chiffrée (IP) plutôt que textuelle (Nom de domaine)
Si cela est gênant par ex pour le cas d'un serveur jabber, ajouter la correspondance dans /etc/hosts (ou C:\WINNT\system32\drivers\etc\hosts):

127.0.0.1 jabber.reseaucitoyen.be

et utiliser le nom de domaine réel dans le client plutôt que 127.0.0.1

Autre solution pour utiliser d'autres services que SSH:
On peut bien entendu se contenter des premiers scripts pour SSH et utiliser SSH pour créer un tunnel vers une machine à l'extérieur:

ssh -L5222:jabber.reseaucitoyen.be:5222 -N user@hal

hal a été configuré dans le ~/.ssh/config, cf plus haut

Robustesse

Il se peut que la communication soit cassée par le proxy de temps à autres
Pour la rendre robuste, on peut utiliser par exemple la commande suivante:

sh -c "while true; do date; ssh <host>; sleep 30; done"

Conjointement à l'utilisation de l'option "ServerAliveInterval 10" dans config cela garantit de redémarrer le tunnel endéans la minute (ServerAliveInterval*ServerAliveCountMax(=3)+sleep).
Enfin pour générer du traffic ce qui tend à garder la connexion ouverte par le proxy: pour une communication ssh avec terminal, voir le paquet spinner et pour une communication ssh uniquement avec déport de port et sans shell on peut créer qqch comme:

 #!/bin/bash
 
 ANSI_XY_SAVE="\033[s"
 ANSI_XY_RESTORE="\033[u"
 STAR='-\|/'
 echo -n "Dummy login "
 for ((k=0;k<5;k=(k+1)%4));do
     echo -e -n "${ANSI_XY_SAVE}${STAR:$k:1} ${ANSI_XY_RESTORE}"
     sleep 5
 done

à sauver dans /usr/local/bin/star.sh et l'activer:

chsh -s /usr/local/bin/star.sh <user>

Next step: Through real SSL Proxies

Nowadays the proxies are a bit smarter and don't let you CONNECT to ports, even port 443, if you're not talking SSL.
So for those proxies you have to talk

  • only on port 443, other ports fail
  • or SSL only, SSH or other protocols fail
  • or both: only SSL only on port 443

Talking SSL but not on the right port

Example: jabber server on port 5223, XML over SSL
That's easy providing that you can use port 443 on another server.
You can simply bounce the packets between the clients and the jabber server with some iptables on your server with a free port 443:

IPTABLES -t nat -A PREROUTING -i eth0 -d my.free.ip -p tcp --dport 443 -j DNAT --to-destination jabber.server.ip:5223
IPTABLES -A FORWARD -d jabber.server.ip -p tcp --dport 5223 -j ACCEPT
IPTABLES -A FORWARD -s jabber.server.ip -p tcp --sport 5223 -j ACCEPT
IPTABLES -t nat -A POSTROUTING -o eth0 -d jabber.server.ip -p tcp --dport 5223 -j SNAT --to-source my.free.ip

So now the connection appears to be really SSL on the port 443.

Example is my jabber bouncer: jabber-rc.yobi.be port 443 => jabber.reseaucitoyen.be port 5223
You can try with e.g. Psi which is capable of connecting through a proxy and to another server/port than the real one.

Not talking SSL

Server side: simple SSL wrapper

You can use SSL wrappers to encapsulate the traffic in sth that looks like a real SSL connection (and it is).
Look at stunnel, stunnel4 or crywrap

Or use simply socat:

socat OPENSSL-LISTEN:443 TCP4:127.0.0.1:22

More secure and flexible way:

socat -d -d -lmlocal2 OPENSSL-LISTEN:443,bind=myip,su=nobody,fork,reuseaddr TCP4:127.0.0.1:22

Client side: simple SSL wrapper

 Port 443
 Proxycommand socat - OPENSSL:%h:%p,verify=0

Or using the script above:

 Port 443
 ProxyCommand  /usr/local/bin/ssh-tunnel.pl - 0 %h %p -e --noproxy

Server side: using Apache2

Now you don't have tons of IPs and you want already to run your Apache on port 443, so what?

Here is an interesting script which serves as HTTPS server, GET and POST are served by reverse proxy, the script contacts the local Apache, and CONNECT can be used to connect to e.g. the SSH server you desesperately wanted to reach.

Client -> BigBrotherProxy -> 443:Script -> Apache or SSH!

And if an admin wants to check that IP you're connecting to in SSL, he will get... an SSL Apache ;-)
Of course your client has to be smart enough to be able to do a CONNECT / SSL / CONNECT string before letting e.g. SSH talking to its server.

Ok ok but why not using directly Apache with sth like that?

<VirtualHost 85.234.207.99:443>
  ...
  SSLEngine on
  ...
  ProxyRequests on 
  AllowCONNECT 22
  <Proxy *>  
     Some ACLs, we don't want to be universal proxy!
  </Proxy> 
</VirtualHost>

That would be wonderful but it appears that Apache, as soon as you do a CONNECT, becomes crystal and everything goes through it untouched, so the SSH server gets SSL-encrypted jam and its reply goes untouched to you while you were expecting SSL!
Slight difference between Apache 1.3 and Apache 2 is that the first one sends you its "HTTP/1.0 200 Connection established" still in SSL while the second already drops SSL before sending the msg in clear.
But anyway it's simply unusable in this stage.

Visibly other people were looking to the same direction and this bugreport, actually a feature request with a patch, seems to bring exactly what we want!

The patch applied smoothly to the current Debian Etch version (2.2.3-3.2)

apt-get build-dep apache2
apt-get install fakeroot
apt-get source apache2
apply the patch...
dpkg-buildpackage -uc -us -rfakeroot
coffee...

And now the fresh apache2.2-common_2.2.3-3.2_amd64.deb contains our patched /usr/lib/apache2/modules/mod_proxy_connect.so

To avoid problems at further upgrades, I move the new module to a new name:

mod_proxy_connect.so -> /usr/lib/apache2/modules/mod_proxy_connect_ssl.so

And create the corresponding config files:

/etc/apache2/mods-available/proxy_connect_ssl.load:
LoadModule proxy_connect_module /usr/lib/apache2/modules/mod_proxy_connect_ssl.so 

And /etc/apache2/mods-enabled/proxy_connect_ssl.load pointing to the previous one as usual.
Don't forget to enable also the main proxy module.

Client side: using socat

First test:

socat - OPENSSL:my.server:443,verify=0
CONNECT another.server:22 HTTP/1.0

HTTP/1.0 200 Connection Established
Proxy-agent: blabla

SSH-2.0-OpenSSH_4.3p2 Debian-8

hehe :-)

socat cannot cumulate proxy and ssl, but we can cascade stuffs:

socat TCP4-LISTEN:1234 OPENSSL:my.server:443,verify=0 &
socat TCP4-LISTEN:4321 PROXY:127.0.0.1:another.server:22,proxyport=1234 &
ssh -p 4321 127.0.0.1
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
RSA key fingerprint is ff:cb:6d:f1:b8:a8:96:fe:08:1b:53:0f:dc:0f:6c:73.
Are you sure you want to continue connecting (yes/no)?
YESSS :-)

Server side: using Apache2 with authentication

Next step: proxy authentication

<VirtualHost *:443>
  ...
  SSLEngine on
  ...
  ProxyRequests on
  AllowCONNECT 22
  <Proxy *>
     Order deny,allow
     Allow from all
     AuthType Basic
     AuthName "Password Required"
     AuthUserFile /path/to/htpasswd
     Require valid-user
  </Proxy> 
</VirtualHost>

By default AllowCONNECT only accepts 443 and 563 so you probably want to extend it to 22 and the other services you want to reach (VPN?)

Client side: using socat

Example:

socat TCP4-LISTEN:2222,bind=127.0.0.1 OPENSSL:my.server:443,verify=0 &
socat TCP4-LISTEN:1111,bind=127.0.0.1 PROXY:127.0.0.1:another.server:22,proxyport=2222,proxyauth=user:pass &
ssh -p 1111 127.0.0.1

And as the goal is to go through an enterprise proxy, it becomes sth similar to:

socat TCP4-LISTEN:3333,bind=127.0.0.1 PROXY:big.brother.proxy:my.server:443,proxyport=8080 &
socat TCP4-LISTEN:2222,bind=127.0.0.1 OPENSSL:127.0.0.1:3333,verify=0 &
socat TCP4-LISTEN:1111,bind=127.0.0.1 PROXY:127.0.0.1:another.server:22,proxyport=2222,proxyauth=user:pass &
ssh -p 1111 127.0.0.1

Using ProxyCommand, the last call to socat and ssh can be combined:

Host test
 Hostname another.server
 Proxycommand socat - PROXY:127.0.0.1:%h:%p,proxyport=2222,proxyauth=user:pass

That's just for a 3 lines test, but now we know we can extend our initial script described at the beginning of this page.

A nice enhancement of socat would be to allow chaining and to allow among others PROXY and OPENSSL to use local file descriptors, e.g. allocated on-the-fly so at the end we could use one single command such as:

socat TCP4-LISTEN:1111,bind=127.0.0.1 PROXY::another.server:22,proxyauth=user:pass OPENSSL::,verify=0 PROXY:big.brother.proxy:my.server:443,proxyport=8080 &

Or using ssh ProxyCommand:

ProxyCommand  socat - PROXY::%h:%p,proxyauth=user:pass OPENSSL::,verify=0 PROXY:big.brother.proxy:my.server:443,proxyport=8080

Or using a nested notation:

ProxyCommand  socat - PROXY:(OPENSSL:(PROXY:big.brother.proxy:my.server:443,proxyport=8080):,verify=0):%h:%p,proxyauth=user:pass

Version 1.5.0.0 says the lexical analysis tries to handle quotes and parenthesis meaningfully and allows escaping of special characters. If one of the characters ( { [ ' is found, the corresponding closing character - ) } ] ' - is looked for; they may also be nested
but also in the BUGS section
Addresses cannot be nested, so a single socat process cannot, e.g., drive ssl over socks. but maybe in a future version...
[Update] According to the developer I am very aware of the lack of this feature - yes, the bug mentioned in the docu refers to exactly your need. I have already considered and partially implemented different approaches for his, but none got ever near to release. But I still intend to realize this feature, because it would enable astonishing possibilities! Expect it to appear in a few years though... so maybe one day... :-)
[Update] version beta 2.0 supports it \o/

ProxyCommand socat - 'PROXY:%h:%p,proxyauth=user:pass|SSL,verify=0|PROXY:my.server:443,proxyauth=user:pass|TCP:big.brother.proxy:8080'

Client side: using the perl script

What socat cannot do, our script can!

ProxyCommand  /usr/local/bin/ssh-tunnel.pl - 0 %h %p -e -R my.server:443 -U <user> -P <password> --noproxy

Make your own SOCKS5

Example: download geo-locked content.

youtube-dl

apt-get install youtube-dl

or

wget https://yt-dl.org/latest/youtube-dl
chmod +x youtube-dl

SOCKS5

Just ssh to a box in the right area and enable SOCKS5 proxy:

ssh -D 12345 remote_host

Get proxychains

apt-get install proxychains

Edit /etc/proxychains.conf

dynamic_chain
quiet_mode
tcp_read_time_out 15000
tcp_connect_time_out 8000
[ProxyList]
socks5 127.0.0.1 12345

It's also possible to redirect DNS through proxy ("proxy_dns") but if you don't need it, it will work smoother without.

Use it, e.g.:

proxychains youtube-dl "Some geo-locked video web page"