8

I have to implement a reverse tunnel from client to server. I have used JSCH with the following command

session.setPortForwardingR(rport, lhost, lport);

and it works (see also Reverse SSH tunnel with JSCH Java)!

next I have to tunnel my ssh session over an HTTPS stream 2-way authenticated:

client -> firewall -> apache https -> ssh server 

----------------------> HTTPS
====================================> SSH
---------------------->

I'm looking for

  1. a small piece of java code to encapsulate SSH into HTTPS
  2. 2 way HTTPS authentication
  3. APACHE configuration

possible solution:

1) HTTPS Tunnel

  1. JHTTPTunnel, but it is based on J2ME and it doesn't support SSL (see also Java Http Tunneling , Is there an Java library for sending binary data over HTTP, HTTP Tunneling?)
  2. JOD, but it doesn't support SSL

3) APACHE CONFIGURATION

  1. Maybe this configuration works but I have to try
## 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 kolich.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 (kolich\.com|anothersshserver\.com) if you want
  ## to allow connections to multiple destinations.
  <ProxyMatch (kolich\.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>

2 Answers 2

2
+50

The solution proposed by yourself is ok it is based on Implement HTTPS tunneling with JSSE I think.

the basic steps are:

  1. define your connection factory for JSCH
  2. open a SSL Socket and call "CONNECT " + host + ":" + port

on server side catch all request calling the "CONNECT" and enable 22 SSH port.

But you have also to consider the following issues:

  1. tune the timeout because the SSL handshake is quite long
  2. enable 2-way authentication or all people can connect to 22 of your server: Using client/server certificates for two way authentication SSL socket on Android
Sign up to request clarification or add additional context in comments.

Comments

2

Unfortunatly no any one has tried to reply; I found the solution.

The solution is based on the HTTP 1.1 CONNECT command and doesn't support direct tunnel.

On the Java client

         // Install the all-trusting trust manager
         final SSLContext sc = SSLContext.getInstance("SSL");
         sc.init(null, trustAllCerts, new java.security.SecureRandom());
         JSch jsch = new JSch();
         Session session = jsch.getSession("root", "SSH-server", 22);

         session.setSocketFactory(new SocketFactory() {
          Socket tunnel = null;

          public Socket createSocket(String host, int port) throws IOException, UnknownHostException {

              SSLSocketFactory ssf = sc.getSocketFactory();

              // HTTP
              tunnel = ssf.createSocket(System.getProperty("https.proxyHost"), Integer.getInteger("https.proxyPort"));
              tunnel.setKeepAlive(true);

              doTunnelHandshake(tunnel, host, port);
              System.out.println(tunnel + " connect " + tunnel.isConnected());
              return tunnel; // dummy
          }

          public InputStream getInputStream(Socket socket) throws IOException {
              System.out.println(tunnel + " getInputStream " + socket.isConnected());
              return tunnel.getInputStream();
          }

          public OutputStream getOutputStream(Socket socket) throws IOException {
              System.out.println("getOutputStream");
              return socket.getOutputStream();
          }           });

      session.connect();

      try {
          session.setPortForwardingR(3391, "localhost", 3389);
      ....

where

  private static void doTunnelHandshake(Socket tunnel, String host, int port) throws IOException {
        OutputStream out = tunnel.getOutputStream();
        String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n" + 
       "User-Agent: " +
       sun.net.www.protocol.http.HttpURLConnection.userAgent + "\r\n\r\n";
        byte b[];
        try {

              b = msg.getBytes("ASCII7");
        } catch (UnsupportedEncodingException ignored) {
              /*
               * If ASCII7 isn't there, something serious is wrong, but Paranoia
               * Is Good (tm)
               */
              b = msg.getBytes();
        }
        out.write(b);
        out.flush();

        /*
         * We need to store the reply so we can create a detailed error message
         * to the user.
         */
        byte reply[] = new byte[200];
        int replyLen = 0;
        int newlinesSeen = 0;
        boolean headerDone = false; /* Done on first newline */

        InputStream in = tunnel.getInputStream();
        boolean error = false;

        while (newlinesSeen < 2) {
              int i = in.read();
              if (i < 0) {
                    throw new IOException("Unexpected EOF from proxy");
              }
              if (i == '\n') {
                    headerDone = true;
                    ++newlinesSeen;
              } else if (i != '\r') {
                    newlinesSeen = 0;
                    if (!headerDone && replyLen < reply.length) {
                          reply[replyLen++] = (byte) i;
                    }
              }
        }

        /*
         * Converting the byte array to a string is slightly wasteful in the
         * case where the connection was successful, but it's insignificant
         * compared to the network overhead.
         */
        String replyStr;
        try {
              replyStr = new String(reply, 0, replyLen, "ASCII7");
        } catch (UnsupportedEncodingException ignored) {
              replyStr = new String(reply, 0, replyLen);
        }

        System.out.println(replyStr);

        /* We asked for HTTP/1.0, so we should get that back */
        if (!replyStr.startsWith("HTTP/1.0 200")) {
              throw new IOException("Unable to tunnel for " + host + ":" + port + ".  Proxy returns \"" + replyStr + "\"");
        }

        /* tunneling Handshake was successful! */
  }

On the apache configuration

add the ssl support

 SSLEngine on
 SSLCertificateFile "conf/ssl.crt/server.crt"
 SSLCertificateKeyFile "conf/ssl.key/server.key"

here the result

Connecting to localhost port 22
HTTP/1.0 200 Connection Established
....
Authentications that can continue: password
Next authentication method: password
Authentication succeeded (password).
Connected

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.