By Linux Guru 01.01.0001
SSH via HTTP — proxytunnel
Excerpt
SSH is a hugely powerful tool for communicating with and manipulating remote machines, and as a result many companies fear it and try to block it. As such, many corporate firewalls block port 22, the port naturally used by SSH. However, few corporations can afford to block ports 80 or 443, the ports designated for http traffic.
SSH is a hugely powerful tool for communicating with and manipulating remote machines, and as a result many companies fear it and try to block it. As such, many corporate firewalls block port 22, the port naturally used by SSH. However, few corporations can afford to block ports 80 or 443, the ports designated for http traffic.
It is possible to work around these firewalls by configuring SSH to listen on either port 80 or 443. However, this approach is only suitable if you are not already using, or planning to use, port 80 or 443 to serve your websites. There is, however, another option. If you have are running an Apache webserver, you can configure it to act as an HTTP or HTTPS proxy and use it to forward SSH traffic that comes in on ports 80 and 443 to your SSH server. To do so, you will need to configure Apache to act as a proxy, and you will need to use proxytunnel when running your SSH client to make it work with the Apache proxy.
Apache Configuration
The first step is to configure Apache to act as an HTTP and HTTPS proxy. To do so on a recent Fedora system, you should create a file and place it in /etc/httpd/conf.d. The name of the file is somewhat arbitrary, but it should end in .conf. Also, the files in this directory are processed in alphabetic order, and so you may want to choose a name earlier in the alphabetic sort order if there is a chance another file may interfere. Neglecting this, assume that the file name chosen is 10-proxy.conf.
This file is suitable for use on an Apache version 2.4 or later server. It explicitly prevents proxying to any other destination than localhost port 22:
<VirtualHost _default_:80>
ProxyRequests on
ProxyVia block
AllowCONNECT 22
<Proxy *>
# Deny all proxying by default ...
Require all denied
</Proxy>
<Proxy 127.0.0.1>
# Now allow proxying through localhost only
Require all granted
</Proxy>
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
# enable ssl
SSLEngine on
# proxytunnel
ProxyRequests On
AllowConnect 22
<Proxy *>
# Deny all proxying by default ...
Require all denied
</Proxy>
<Proxy 127.0.0.1>
# Now allow proxying by localhost only
Require all granted
</Proxy>
</VirtualHost>
</IfModule>
This file configures a proxy for both port 80 and 443. You can remove the appropriate section if you only want one port to act as a proxy.
You also need to assure that Apache loads the appropriate modules: proxy, proxy_connect. proxy_http. That is done in /etc/httpd/conf.module.d. For me, this was preconfigured. I had the following lines in the file 00-proxy.conf:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
You activate the proxy by running:
sudo systemctl reload httpd
or:
Installing ProxyTunnel
On Fedora, you can install proxytunnel with:
sudo yum install proxytunnel
Testing the Proxy
You can use proxytunnel to test your Apache setup. Simply run the following:
proxytunnel -p myserver.com:80 -d 127.0.0.1:22 -v
and:
proxytunnel -E -p myserver.com:443 -d 127.0.0.1:22 -v
It should respond by saying that the tunnel was established. If you choose any other destination (IP address or port) it should fail.
Configuring Your OpenSSH Client
Add an entry to your SSH config file (~/.ssh/config) for your host that looks like the following:
host home80
HostName myserver.com
ProxyCommand proxytunnel -q -p myserver.com:80 -d 127.0.0.1:22
Port 80
host home443
HostName myserver.com
ProxyCommand proxytunnel -q -E -p myserver.com:443 -d 127.0.0.1:22
Port 443
Be sure to add the -E flag on proxytunnel when connecting to port 443 to turn on SSL encryption.
SELinux
If SELinux is running, it will likely stop Apache from connecting to SSH. If your proxy is not working, you can determine if SELinux is the problem by temporarily disabling SELinux with:
sudo setenforce permissive
If the proxy starts working, you have discovered the problem. Generally this is because SELinux nominally blocks httpd from accessing the local network, which prevents it from accessing the SSH daemon. You can circumvent this problem using:
sudo setsebool -P httpd_can_network_connect 1
The -P makes the solution persistent across reboots.
You should reactivate SELinux using:
sudo setenforce enforcing
Here’s the situation, I’m often on a network that does not allow outbound traffic on port 22. Meaning, I cannot directly “SSH out” from that network to my Linux box at home. Fair enough. However, this network does allow outbound traffic on ports 80, 443, and 8443 via a web-proxy. That said, if I want to “SSH out” from this network to my Linux box at home, I can do so with a little tweaking of my remote Apache server and my local SSH client.
Here’s how …
Overview
First, you’ll need to configure your Apache web-server to accept traffic on a port that’s acceptable to the web-proxy. In my case, I don’t have anything running on port 8443, and the web-proxy allows traffic through port 8443, so that’s perfect. Apache will be configured to listen on 8443, and act as a “proxy” between an SSH client and an SSH server (usually the SSH server running on the box you’re trying to connect to).
Second, on the client side, you’ll be using something like Proxytunnel to punch a hole through the web-proxy allowing your SSH client to connect to an SSH server of your choice.
Putting it all together, the basic flow is …
- Your local SSH client uses Proxytunnel to connect to web-proxy.corp.example.com:3128
- Web-proxy.corp.example.com connects to Apache running at yourwebserver:8443
- Your Apache server, acting as yet another proxy, connects to yoursshserver:22
- It works!
Install and Configure Proxytunnel
If you’re on Ubuntu, you can install Proxytunnel with the following command:
#/> sudo apt-get install proxytunnel
Once installed, edit your ~/.ssh/config file to instruct your SSH client to use Proxytunnel when connecting to the destination host:
## ~/.ssh/config
Host linux.com
Hostname linux.com
ProtocolKeepAlives 30
ProxyCommand /usr/bin/proxytunnel \
-p web-proxy.corp.example.com:3128 \
-r linux.com:8443 -d %h:%p \
-H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)"
In this example, when my SSH client makes an attempt to connect to linux.com:22, it will spawn Proxytunnel which will then route my connection through web-proxy.corp.example.com:3128 and on to linux.com:8443. This seems really convoluted, but it actually works quite well.
Note that I’m spoofing a somewhat real User-Agent to prevent suspicion from the system administrators running web-proxy.corp.example.com. If you’re a system administrator that runs such a web-proxy, please accept my apologies for making your life even more difficult.
Configure mod_proxy on Apache
Now that you’ve got the client part figured out, you’ll need to configure Apache’s mod_proxy module to proxy traffic between yourwebserver:8443 and yoursshserver:22. In all likelihood, your web-server and SSH server are the same box. At least, in my home, they are.
Oh, and I assume you already have Apache and mod_proxy installed, and working. There are ton of other tutorials and nice blog posts online about how to install and setup Apache if you don’t already have it installed and functional.
In my Apache virtual host configuration, I’ve added another V-host listening on port 8443 that will only accept CONNECT requests bound for linux.com on port 22:
## Load the required modules.
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
## Listen on port 8443 (in addition to other ports like 80 or 443)
Listen 8443
<VirtualHost *:8443>
ServerName youwebserver:8443
DocumentRoot /some/path/maybe/not/required
ServerAdmin [email protected]
## Only ever allow incoming HTTP CONNECT requests.
## Explicitly deny other request types like GET, POST, etc.
## This tells Apache to return a 403 Forbidden if this virtual
## host receives anything other than an HTTP CONNECT.
RewriteEngine On
RewriteCond %{REQUEST_METHOD} !^CONNECT [NC]
RewriteRule ^/(.*)$ - [F,L]
## Setup proxying between youwebserver:8443 and yoursshserver:22
ProxyRequests On
ProxyBadHeader Ignore
ProxyVia Full
## IMPORTANT: The AllowCONNECT directive specifies a list
## of port numbers to which the proxy CONNECT method may
## connect. For security, only allow CONNECT requests
## bound for port 22.
AllowCONNECT 22
## IMPORTANT: By default, deny everyone. If you don't do this
## others will be able to connect to port 22 on any host.
<Proxy *>
Order deny,allow
Deny from all
</Proxy>
## Now, only allow CONNECT requests bound for linux.com
## Should be replaced with yoursshserver.com or the hostname
## of whatever SSH server you're trying to connect to. Note
## that ProxyMatch takes a regular expression, so you can do
## things like (linux\.com|anothersshserver\.com) if you want
## to allow connections to multiple destinations.
<ProxyMatch (linux\.com)>
Order allow,deny
Allow from all
</ProxyMatch>
## Logging, always a good idea.
LogLevel warn
ErrorLog logs/yourwebserver-proxy_error_log
CustomLog logs/yourwebserver-proxy_request_log combined
</VirtualHost>
Once you get everything integrated, restart Apache and you should be golden.
Under the Hood
To prove that everything works, let’s try a few things.
First I’m going to telnet to web-proxy.corp.example.com:3128. Then, I’m going to tell it to connect to linux.com:8443. Finally, I’m going to tell Apache on linux.com:8443 to connect to linux.com:22. This is exactly the same flow used by Proxytunnel under the hood.
(tux@ubuntu)~> telnet web-proxy.corp.example.com 3128
Trying 10.10.10.10...
Connected to web-proxy.corp.example.com (10.10.10.10).
Escape character is '^]'.
CONNECT linux.com:8443 HTTP/1.1
Host: linux.com
HTTP/1.0 200 Connection Established
CONNECT linux.com:22 HTTP/1.1
Host: linux.com
HTTP/1.0 200 Connection Established
Proxy-agent: Apache
SSH-2.0-OpenSSH_4.3
Sweet! Notice the raw “SSH-2.0-OpenSSH_4.3” response from the SSH server, indicating a successful connection. Now, If I was a real SSH client, I’d continue the handshake and away we go.
So, from a real SSH client with Proxytunnel enabled …
(tux@debian)~> ssh [email protected]
Via web-proxy.corp.example.com:3128 -> linux.com:8443 -> linux.com:22
[email protected]'s password:
Last login: Sat Dec 31 12:53:22 2011 from gateway.linux.local
(tux@server)~>
It works! Notice the intermediate “Via web-proxy.corp.example.com:3128 -> linux.com:8443 -> linux.com:22” output from Proxytunnel telling me what it’s doing to connect. And of course, look at that beautiful shell prompt.
SSH through a web-proxy, I love it.
Links
https://github.com/proxytunnel/proxytunnel